authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-13 19:17:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-13 19:29:07-07:00
log2a8fc1a18e7d9017262f5c7ee9669ca7d80ebaa6
tree34907cfbd9ede1ef1c86d97a9c9ead7948ec2509
parent1baa56a25f7b4cf7b3e0a78ded90c432a40c4efa

stage2: caching system integration & Module/Compilation splitting

* update to the new cache hash API * std.Target defaultVersionRange moves to std.Target.Os.Tag * std.Target.Os gains getVersionRange which returns a tagged union * start the process of splitting Module into Compilation and "zig module". - The parts of Module having to do with only compiling zig code are extracted into ZigModule.zig. - Next step is to rename Module to Compilation. - After that rename ZigModule back to Module. * implement proper cache hash usage when compiling C objects, and properly manage the file lock of the build artifacts. * make versions optional to match recent changes to master branch. * proper cache hash integration for compiling zig code * proper cache hash integration for linking even when not compiling zig code. * ELF LLD linking integrates with the caching system. A comment from the source code: Here we want to determine whether we can save time by not invoking LLD when the output is unchanged. None of the linker options or the object files that are being linked are in the hash that namespaces the directory we are outputting to. Therefore, we must hash those now, and the resulting digest will form the "id" of the linking job we are about to perform. After a successful link, we store the id in the metadata of a symlink named "id.txt" in the artifact directory. So, now, we check if this symlink exists, and if it matches our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. * implement disable_c_depfile option * add tracy to a few more functions

25 files changed, 3954 insertions(+), 3662 deletions(-)

lib/std/cache_hash.zig+1-1
......@@ -82,7 +82,7 @@ pub const HashHelper = struct {
8282 }
8383
8484 pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
85 hh.add(list_of_bytes.items.len);
85 hh.add(list_of_bytes.len);
8686 for (list_of_bytes) |bytes| hh.addBytes(bytes);
8787 }
8888
lib/std/target.zig+33-5
......@@ -75,6 +75,13 @@ pub const Target = struct {
7575 else => return ".so",
7676 }
7777 }
78
79 pub fn defaultVersionRange(tag: Tag) Os {
80 return .{
81 .tag = tag,
82 .version_range = VersionRange.default(tag),
83 };
84 }
7885 };
7986
8087 /// Based on NTDDI version constants from
......@@ -290,11 +297,32 @@ pub const Target = struct {
290297 }
291298 };
292299
293 pub fn defaultVersionRange(tag: Tag) Os {
294 return .{
295 .tag = tag,
296 .version_range = VersionRange.default(tag),
297 };
300 pub const TaggedVersionRange = union(enum) {
301 none: void,
302 semver: Version.Range,
303 linux: LinuxVersionRange,
304 windows: WindowsVersion.Range,
305 };
306
307 /// Provides a tagged union. `Target` does not store the tag because it is
308 /// redundant with the OS tag; this function abstracts that part away.
309 pub fn getVersionRange(self: Os) TaggedVersionRange {
310 switch (self.tag) {
311 .linux => return TaggedVersionRange{ .linux = self.version_range.linux },
312 .windows => return TaggedVersionRange{ .windows = self.version_range.windows },
313
314 .freebsd,
315 .macosx,
316 .ios,
317 .tvos,
318 .watchos,
319 .netbsd,
320 .openbsd,
321 .dragonfly,
322 => return TaggedVersionRange{ .semver = self.version_range.semver },
323
324 else => return .none,
325 }
298326 }
299327
300328 /// Checks if system is guaranteed to be at least `version` or older than `version`.
lib/std/zig/cross_target.zig+1-1
......@@ -375,7 +375,7 @@ pub const CrossTarget = struct {
375375 // `Target.current.os` works when doing `zig build` because Zig generates a build executable using
376376 // native OS version range. However this will not be accurate otherwise, and
377377 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
378 var adjusted_os = if (self.os_tag) |os_tag| Target.Os.defaultVersionRange(os_tag) else Target.current.os;
378 var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange() else Target.current.os;
379379
380380 if (self.os_version_min) |min| switch (min) {
381381 .none => {},
lib/std/zig/system.zig+1-1
......@@ -203,7 +203,7 @@ pub const NativeTargetInfo = struct {
203203 /// deinitialization method.
204204 /// TODO Remove the Allocator requirement from this function.
205205 pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
206 var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
206 var os = cross_target.getOsTag().defaultVersionRange();
207207 if (cross_target.os_tag == null) {
208208 switch (Target.current.os.tag) {
209209 .linux => {
src-self-hosted/Module.zig+431-3548
......@@ -1,90 +1,39 @@
1//! TODO This is going to get renamed from Module to Compilation.
2const Module = @This();
3const Compilation = @This();
4
15const std = @import("std");
26const mem = std.mem;
37const Allocator = std.mem.Allocator;
4const ArrayListUnmanaged = std.ArrayListUnmanaged;
58const Value = @import("value.zig").Value;
6const Type = @import("type.zig").Type;
7const TypedValue = @import("TypedValue.zig");
89const assert = std.debug.assert;
9const log = std.log.scoped(.module);
10const BigIntConst = std.math.big.int.Const;
11const BigIntMutable = std.math.big.int.Mutable;
10const log = std.log.scoped(.compilation);
1211const Target = std.Target;
1312const target_util = @import("target.zig");
1413const Package = @import("Package.zig");
1514const link = @import("link.zig");
16const ir = @import("ir.zig");
17const zir = @import("zir.zig");
18const Module = @This();
19const Inst = ir.Inst;
20const Body = ir.Body;
21const ast = std.zig.ast;
2215const trace = @import("tracy.zig").trace;
2316const liveness = @import("liveness.zig");
24const astgen = @import("astgen.zig");
25const zir_sema = @import("zir_sema.zig");
2617const build_options = @import("build_options");
2718const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2819const glibc = @import("glibc.zig");
2920const fatal = @import("main.zig").fatal;
21const ZigModule = @import("ZigModule.zig");
3022
3123/// General-purpose allocator. Used for both temporary and long-term storage.
3224gpa: *Allocator,
3325/// Arena-allocated memory used during initialization. Should be untouched until deinit.
3426arena_state: std.heap.ArenaAllocator.State,
35/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
36root_pkg: ?*Package,
37/// Module owns this resource.
38/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
39root_scope: *Scope,
4027bin_file: *link.File,
41/// It's rare for a decl to be exported, so we save memory by having a sparse map of
42/// Decl pointers to details about them being exported.
43/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
44decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
45/// We track which export is associated with the given symbol name for quick
46/// detection of symbol collisions.
47symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
48/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
49/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
50/// is performing the export of another Decl.
51/// This table owns the Export memory.
52export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
53/// Maps fully qualified namespaced names to the Decl struct for them.
54decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
55
5628c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
5729
5830link_error_flags: link.File.ErrorFlags = .{},
5931
6032work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
6133
62/// We optimize memory usage for a compilation with no compile errors by storing the
63/// error messages and mapping outside of `Decl`.
64/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
65/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
66/// a Decl can have a failed_decls entry but have analysis status of success.
67failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
68/// Using a map here for consistency with the other fields here.
69/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
70failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{},
71/// Using a map here for consistency with the other fields here.
72/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
73failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
7434/// The ErrorMsg memory is owned by the `CObject`, using Module's general purpose allocator.
7535failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
7636
77/// Incrementing integer used to compare against the corresponding Decl
78/// field to determine whether a Decl's status applies to an ongoing update, or a
79/// previous analysis.
80generation: u32 = 0,
81
82next_anon_name_index: usize = 0,
83
84/// Candidates for deletion. After a semantic analysis update completes, this list
85/// contains Decls that need to be deleted if they end up having no references to them.
86deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
87
8837keep_source_files_loaded: bool,
8938use_clang: bool,
9039sanitize_c: bool,
......@@ -95,18 +44,15 @@ sanitize_c: bool,
9544clang_passthrough_mode: bool,
9645/// Whether to print clang argvs to stdout.
9746debug_cc: bool,
98
99/// Error tags and their values, tag names are duped with mod.gpa.
100global_error_set: std.StringHashMapUnmanaged(u16) = .{},
47disable_c_depfile: bool,
10148
10249c_source_files: []const CSourceFile,
10350clang_argv: []const []const u8,
104cache: std.cache_hash.CacheHash,
51cache_parent: *std.cache_hash.Cache,
10552/// Path to own executable for invoking `zig clang`.
10653self_exe_path: ?[]const u8,
10754zig_lib_directory: Directory,
10855zig_cache_directory: Directory,
109zig_cache_artifact_directory: Directory,
11056libc_include_dir_list: []const []const u8,
11157rand: *std.rand.Random,
11258
......@@ -128,7 +74,10 @@ libc_static_lib: ?[]const u8 = null,
12874/// The key is the basename, and the value is the absolute path to the completed build artifact.
12975crt_files: std.StringHashMapUnmanaged([]const u8) = .{},
13076
131pub const InnerError = error{ OutOfMemory, AnalysisFail };
77/// Keeping track of this possibly open resource so we can close it later.
78owned_link_dir: ?std.fs.Dir,
79
80pub const InnerError = ZigModule.InnerError;
13281
13382/// For passing to a C compiler.
13483pub const CSourceFile = struct {
......@@ -138,14 +87,14 @@ pub const CSourceFile = struct {
13887
13988const WorkItem = union(enum) {
14089 /// Write the machine code for a Decl to the output file.
141 codegen_decl: *Decl,
90 codegen_decl: *ZigModule.Decl,
14291 /// The Decl needs to be analyzed and possibly export itself.
14392 /// It may have already be analyzed, or it may have been determined
14493 /// to be outdated; in this case perform semantic analysis again.
145 analyze_decl: *Decl,
94 analyze_decl: *ZigModule.Decl,
14695 /// The source file containing the Decl has been updated, and so the
14796 /// Decl may need its line number information updated in the debug info.
148 update_line_number: *Decl,
97 update_line_number: *ZigModule.Decl,
14998 /// Invoke the Clang compiler to create an object file, which gets linked
15099 /// with the Module.
151100 c_object: *CObject,
......@@ -156,192 +105,6 @@ const WorkItem = union(enum) {
156105 glibc_so: *const glibc.Lib,
157106};
158107
159pub const Export = struct {
160 options: std.builtin.ExportOptions,
161 /// Byte offset into the file that contains the export directive.
162 src: usize,
163 /// Represents the position of the export, if any, in the output file.
164 link: link.File.Elf.Export,
165 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
166 owner_decl: *Decl,
167 /// The Decl being exported. Note this is *not* the Decl performing the export.
168 exported_decl: *Decl,
169 status: enum {
170 in_progress,
171 failed,
172 /// Indicates that the failure was due to a temporary issue, such as an I/O error
173 /// when writing to the output file. Retrying the export may succeed.
174 failed_retryable,
175 complete,
176 },
177};
178
179pub const Decl = struct {
180 /// This name is relative to the containing namespace of the decl. It uses a null-termination
181 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
182 /// in symbol names, because executable file formats use null-terminated strings for symbol names.
183 /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
184 /// mapping them to an address in the output file.
185 /// Memory owned by this decl, using Module's allocator.
186 name: [*:0]const u8,
187 /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
188 /// Reference to externally owned memory.
189 scope: *Scope,
190 /// The AST Node decl index or ZIR Inst index that contains this declaration.
191 /// Must be recomputed when the corresponding source file is modified.
192 src_index: usize,
193 /// The most recent value of the Decl after a successful semantic analysis.
194 typed_value: union(enum) {
195 never_succeeded: void,
196 most_recent: TypedValue.Managed,
197 },
198 /// Represents the "shallow" analysis status. For example, for decls that are functions,
199 /// the function type is analyzed with this set to `in_progress`, however, the semantic
200 /// analysis of the function body is performed with this value set to `success`. Functions
201 /// have their own analysis status field.
202 analysis: enum {
203 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
204 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
205 unreferenced,
206 /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
207 in_progress,
208 /// This Decl might be OK but it depends on another one which did not successfully complete
209 /// semantic analysis.
210 dependency_failure,
211 /// Semantic analysis failure.
212 /// There will be a corresponding ErrorMsg in Module.failed_decls.
213 sema_failure,
214 /// There will be a corresponding ErrorMsg in Module.failed_decls.
215 /// This indicates the failure was something like running out of disk space,
216 /// and attempting semantic analysis again may succeed.
217 sema_failure_retryable,
218 /// There will be a corresponding ErrorMsg in Module.failed_decls.
219 codegen_failure,
220 /// There will be a corresponding ErrorMsg in Module.failed_decls.
221 /// This indicates the failure was something like running out of disk space,
222 /// and attempting codegen again may succeed.
223 codegen_failure_retryable,
224 /// Everything is done. During an update, this Decl may be out of date, depending
225 /// on its dependencies. The `generation` field can be used to determine if this
226 /// completion status occurred before or after a given update.
227 complete,
228 /// A Module update is in progress, and this Decl has been flagged as being known
229 /// to require re-analysis.
230 outdated,
231 },
232 /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
233 /// when removed.
234 deletion_flag: bool,
235 /// Whether the corresponding AST decl has a `pub` keyword.
236 is_pub: bool,
237
238 /// An integer that can be checked against the corresponding incrementing
239 /// generation field of Module. This is used to determine whether `complete` status
240 /// represents pre- or post- re-analysis.
241 generation: u32,
242
243 /// Represents the position of the code in the output file.
244 /// This is populated regardless of semantic analysis and code generation.
245 link: link.File.LinkBlock,
246
247 /// Represents the function in the linked output file, if the `Decl` is a function.
248 /// This is stored here and not in `Fn` because `Decl` survives across updates but
249 /// `Fn` does not.
250 /// TODO Look into making `Fn` a longer lived structure and moving this field there
251 /// to save on memory usage.
252 fn_link: link.File.LinkFn,
253
254 contents_hash: std.zig.SrcHash,
255
256 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
257 /// typed_value is modified.
258 dependants: DepsTable = .{},
259 /// The shallow set of other decls whose typed_value changing indicates that this Decl's
260 /// typed_value may need to be regenerated.
261 dependencies: DepsTable = .{},
262
263 /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for
264 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
265 pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
266
267 pub fn destroy(self: *Decl, gpa: *Allocator) void {
268 gpa.free(mem.spanZ(self.name));
269 if (self.typedValueManaged()) |tvm| {
270 tvm.deinit(gpa);
271 }
272 self.dependants.deinit(gpa);
273 self.dependencies.deinit(gpa);
274 gpa.destroy(self);
275 }
276
277 pub fn src(self: Decl) usize {
278 switch (self.scope.tag) {
279 .container => {
280 const container = @fieldParentPtr(Scope.Container, "base", self.scope);
281 const tree = container.file_scope.contents.tree;
282 // TODO Container should have it's own decls()
283 const decl_node = tree.root_node.decls()[self.src_index];
284 return tree.token_locs[decl_node.firstToken()].start;
285 },
286 .zir_module => {
287 const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
288 const module = zir_module.contents.module;
289 const src_decl = module.decls[self.src_index];
290 return src_decl.inst.src;
291 },
292 .none => unreachable,
293 .file, .block => unreachable,
294 .gen_zir => unreachable,
295 .local_val => unreachable,
296 .local_ptr => unreachable,
297 .decl => unreachable,
298 }
299 }
300
301 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
302 return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
303 }
304
305 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
306 const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
307 return tvm.typed_value;
308 }
309
310 pub fn value(self: *Decl) error{AnalysisFail}!Value {
311 return (try self.typedValue()).val;
312 }
313
314 pub fn dump(self: *Decl) void {
315 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
316 std.debug.print("{}:{}:{} name={} status={}", .{
317 self.scope.sub_file_path,
318 loc.line + 1,
319 loc.column + 1,
320 mem.spanZ(self.name),
321 @tagName(self.analysis),
322 });
323 if (self.typedValueManaged()) |tvm| {
324 std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
325 }
326 std.debug.print("\n", .{});
327 }
328
329 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
330 switch (self.typed_value) {
331 .most_recent => |*x| return x,
332 .never_succeeded => return null,
333 }
334 }
335
336 fn removeDependant(self: *Decl, other: *Decl) void {
337 self.dependants.removeAssertDiscard(other);
338 }
339
340 fn removeDependency(self: *Decl, other: *Decl) void {
341 self.dependencies.removeAssertDiscard(other);
342 }
343};
344
345108pub const CObject = struct {
346109 /// Relative to cwd. Owned by arena.
347110 src_path: []const u8,
......@@ -350,578 +113,39 @@ pub const CObject = struct {
350113 arena: std.heap.ArenaAllocator.State,
351114 status: union(enum) {
352115 new,
353 /// This is the output object path. Owned by gpa.
354 success: []u8,
355 /// There will be a corresponding ErrorMsg in Module.failed_c_objects.
356 /// This is the C source file contents (used for printing error messages). Owned by gpa.
357 failure: []u8,
116 success: struct {
117 /// The outputted result. Owned by gpa.
118 object_path: []u8,
119 /// This is a file system lock on the cache hash manifest representing this
120 /// object. It prevents other invocations of the Zig compiler from interfering
121 /// with this object until released.
122 lock: std.cache_hash.Lock,
123 },
124 /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects.
125 failure,
358126 },
359127
360 pub fn destroy(self: *CObject, gpa: *Allocator) void {
128 /// Returns if there was failure.
129 pub fn clearStatus(self: *CObject, gpa: *Allocator) bool {
361130 switch (self.status) {
362 .new => {},
363 .failure, .success => |data| gpa.free(data),
364 }
365 self.arena.promote(gpa).deinit();
366 }
367};
368
369/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
370pub const Fn = struct {
371 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
372 analysis: union(enum) {
373 queued: *ZIR,
374 in_progress,
375 /// There will be a corresponding ErrorMsg in Module.failed_decls
376 sema_failure,
377 /// This Fn might be OK but it depends on another Decl which did not successfully complete
378 /// semantic analysis.
379 dependency_failure,
380 success: Body,
381 },
382 owner_decl: *Decl,
383
384 /// This memory is temporary and points to stack memory for the duration
385 /// of Fn analysis.
386 pub const Analysis = struct {
387 inner_block: Scope.Block,
388 };
389
390 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
391 pub const ZIR = struct {
392 body: zir.Module.Body,
393 arena: std.heap.ArenaAllocator.State,
394 };
395
396 /// For debugging purposes.
397 pub fn dump(self: *Fn, mod: Module) void {
398 std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name});
399 switch (self.analysis) {
400 .queued => {
401 std.debug.print("queued\n", .{});
402 },
403 .in_progress => {
404 std.debug.print("in_progress\n", .{});
405 },
406 else => {
407 std.debug.print("\n", .{});
408 zir.dumpFn(mod, self);
409 },
410 }
411 }
412};
413
414pub const Var = struct {
415 /// if is_extern == true this is undefined
416 init: Value,
417 owner_decl: *Decl,
418
419 is_extern: bool,
420 is_mutable: bool,
421 is_threadlocal: bool,
422};
423
424pub const Scope = struct {
425 tag: Tag,
426
427 pub const NameHash = [16]u8;
428
429 pub fn cast(base: *Scope, comptime T: type) ?*T {
430 if (base.tag != T.base_tag)
431 return null;
432
433 return @fieldParentPtr(T, "base", base);
434 }
435
436 /// Asserts the scope has a parent which is a DeclAnalysis and
437 /// returns the arena Allocator.
438 pub fn arena(self: *Scope) *Allocator {
439 switch (self.tag) {
440 .block => return self.cast(Block).?.arena,
441 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
442 .gen_zir => return self.cast(GenZIR).?.arena,
443 .local_val => return self.cast(LocalVal).?.gen_zir.arena,
444 .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
445 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
446 .file => unreachable,
447 .container => unreachable,
448 .none => unreachable,
449 }
450 }
451
452 /// If the scope has a parent which is a `DeclAnalysis`,
453 /// returns the `Decl`, otherwise returns `null`.
454 pub fn decl(self: *Scope) ?*Decl {
455 return switch (self.tag) {
456 .block => self.cast(Block).?.decl,
457 .gen_zir => self.cast(GenZIR).?.decl,
458 .local_val => self.cast(LocalVal).?.gen_zir.decl,
459 .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
460 .decl => self.cast(DeclAnalysis).?.decl,
461 .zir_module => null,
462 .file => null,
463 .container => null,
464 .none => unreachable,
465 };
466 }
467
468 /// Asserts the scope has a parent which is a ZIRModule or Container and
469 /// returns it.
470 pub fn namespace(self: *Scope) *Scope {
471 switch (self.tag) {
472 .block => return self.cast(Block).?.decl.scope,
473 .gen_zir => return self.cast(GenZIR).?.decl.scope,
474 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
475 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
476 .decl => return self.cast(DeclAnalysis).?.decl.scope,
477 .file => return &self.cast(File).?.root_container.base,
478 .zir_module, .container => return self,
479 .none => unreachable,
480 }
481 }
482
483 /// Must generate unique bytes with no collisions with other decls.
484 /// The point of hashing here is only to limit the number of bytes of
485 /// the unique identifier to a fixed size (16 bytes).
486 pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
487 switch (self.tag) {
488 .block => unreachable,
489 .gen_zir => unreachable,
490 .local_val => unreachable,
491 .local_ptr => unreachable,
492 .decl => unreachable,
493 .file => unreachable,
494 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
495 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
496 .none => unreachable,
497 }
498 }
499
500 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
501 pub fn tree(self: *Scope) *ast.Tree {
502 switch (self.tag) {
503 .file => return self.cast(File).?.contents.tree,
504 .zir_module => unreachable,
505 .none => unreachable,
506 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
507 .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
508 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
509 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
510 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
511 .container => return self.cast(Container).?.file_scope.contents.tree,
512 }
513 }
514
515 /// Asserts the scope is a child of a `GenZIR` and returns it.
516 pub fn getGenZIR(self: *Scope) *GenZIR {
517 return switch (self.tag) {
518 .block => unreachable,
519 .gen_zir => self.cast(GenZIR).?,
520 .local_val => return self.cast(LocalVal).?.gen_zir,
521 .local_ptr => return self.cast(LocalPtr).?.gen_zir,
522 .decl => unreachable,
523 .zir_module => unreachable,
524 .file => unreachable,
525 .container => unreachable,
526 .none => unreachable,
527 };
528 }
529
530 /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
531 /// returns the sub_file_path field.
532 pub fn subFilePath(base: *Scope) []const u8 {
533 switch (base.tag) {
534 .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
535 .file => return @fieldParentPtr(File, "base", base).sub_file_path,
536 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
537 .none => unreachable,
538 .block => unreachable,
539 .gen_zir => unreachable,
540 .local_val => unreachable,
541 .local_ptr => unreachable,
542 .decl => unreachable,
543 }
544 }
545
546 pub fn unload(base: *Scope, gpa: *Allocator) void {
547 switch (base.tag) {
548 .file => return @fieldParentPtr(File, "base", base).unload(gpa),
549 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
550 .none => {},
551 .block => unreachable,
552 .gen_zir => unreachable,
553 .local_val => unreachable,
554 .local_ptr => unreachable,
555 .decl => unreachable,
556 .container => unreachable,
557 }
558 }
559
560 pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
561 switch (base.tag) {
562 .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
563 .file => return @fieldParentPtr(File, "base", base).getSource(module),
564 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
565 .none => unreachable,
566 .gen_zir => unreachable,
567 .local_val => unreachable,
568 .local_ptr => unreachable,
569 .block => unreachable,
570 .decl => unreachable,
571 }
572 }
573
574 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
575 pub fn removeDecl(base: *Scope, child: *Decl) void {
576 switch (base.tag) {
577 .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
578 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
579 .none => unreachable,
580 .file => unreachable,
581 .block => unreachable,
582 .gen_zir => unreachable,
583 .local_val => unreachable,
584 .local_ptr => unreachable,
585 .decl => unreachable,
586 }
587 }
588
589 /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
590 pub fn destroy(base: *Scope, gpa: *Allocator) void {
591 switch (base.tag) {
592 .file => {
593 const scope_file = @fieldParentPtr(File, "base", base);
594 scope_file.deinit(gpa);
595 gpa.destroy(scope_file);
596 },
597 .zir_module => {
598 const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
599 scope_zir_module.deinit(gpa);
600 gpa.destroy(scope_zir_module);
131 .new => return false,
132 .failure => {
133 self.status = .new;
134 return true;
601135 },
602 .none => {
603 const scope_none = @fieldParentPtr(None, "base", base);
604 gpa.destroy(scope_none);
136 .success => |*success| {
137 gpa.free(success.object_path);
138 success.lock.release();
139 self.status = .new;
140 return false;
605141 },
606 .block => unreachable,
607 .gen_zir => unreachable,
608 .local_val => unreachable,
609 .local_ptr => unreachable,
610 .decl => unreachable,
611 .container => unreachable,
612142 }
613143 }
614144
615 fn name_hash_hash(x: NameHash) u32 {
616 return @truncate(u32, @bitCast(u128, x));
617 }
618
619 fn name_hash_eql(a: NameHash, b: NameHash) bool {
620 return @bitCast(u128, a) == @bitCast(u128, b);
145 pub fn destroy(self: *CObject, gpa: *Allocator) void {
146 _ = self.clearStatus(gpa);
147 self.arena.promote(gpa).deinit();
621148 }
622
623 pub const Tag = enum {
624 /// .zir source code.
625 zir_module,
626 /// .zig source code.
627 file,
628 /// There is no .zig or .zir source code being compiled in this Module.
629 none,
630 /// struct, enum or union, every .file contains one of these.
631 container,
632 block,
633 decl,
634 gen_zir,
635 local_val,
636 local_ptr,
637 };
638
639 pub const Container = struct {
640 pub const base_tag: Tag = .container;
641 base: Scope = Scope{ .tag = base_tag },
642
643 file_scope: *Scope.File,
644
645 /// Direct children of the file.
646 decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
647
648 // TODO implement container types and put this in a status union
649 // ty: Type
650
651 pub fn deinit(self: *Container, gpa: *Allocator) void {
652 self.decls.deinit(gpa);
653 self.* = undefined;
654 }
655
656 pub fn removeDecl(self: *Container, child: *Decl) void {
657 _ = self.decls.remove(child);
658 }
659
660 pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
661 // TODO container scope qualified names.
662 return std.zig.hashSrc(name);
663 }
664 };
665
666 pub const File = struct {
667 pub const base_tag: Tag = .file;
668 base: Scope = Scope{ .tag = base_tag },
669
670 /// Relative to the owning package's root_src_dir.
671 /// Reference to external memory, not owned by File.
672 sub_file_path: []const u8,
673 source: union(enum) {
674 unloaded: void,
675 bytes: [:0]const u8,
676 },
677 contents: union {
678 not_available: void,
679 tree: *ast.Tree,
680 },
681 status: enum {
682 never_loaded,
683 unloaded_success,
684 unloaded_parse_failure,
685 loaded_success,
686 },
687
688 root_container: Container,
689
690 pub fn unload(self: *File, gpa: *Allocator) void {
691 switch (self.status) {
692 .never_loaded,
693 .unloaded_parse_failure,
694 .unloaded_success,
695 => {},
696
697 .loaded_success => {
698 self.contents.tree.deinit();
699 self.status = .unloaded_success;
700 },
701 }
702 switch (self.source) {
703 .bytes => |bytes| {
704 gpa.free(bytes);
705 self.source = .{ .unloaded = {} };
706 },
707 .unloaded => {},
708 }
709 }
710
711 pub fn deinit(self: *File, gpa: *Allocator) void {
712 self.root_container.deinit(gpa);
713 self.unload(gpa);
714 self.* = undefined;
715 }
716
717 pub fn dumpSrc(self: *File, src: usize) void {
718 const loc = std.zig.findLineColumn(self.source.bytes, src);
719 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
720 }
721
722 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
723 switch (self.source) {
724 .unloaded => {
725 const source = try module.root_pkg.?.root_src_directory.handle.readFileAllocOptions(
726 module.gpa,
727 self.sub_file_path,
728 std.math.maxInt(u32),
729 null,
730 1,
731 0,
732 );
733 self.source = .{ .bytes = source };
734 return source;
735 },
736 .bytes => |bytes| return bytes,
737 }
738 }
739 };
740
741 /// For when there is no top level scope because there are no .zig files being compiled.
742 pub const None = struct {
743 pub const base_tag: Tag = .none;
744 base: Scope = Scope{ .tag = base_tag },
745 };
746
747 pub const ZIRModule = struct {
748 pub const base_tag: Tag = .zir_module;
749 base: Scope = Scope{ .tag = base_tag },
750 /// Relative to the owning package's root_src_dir.
751 /// Reference to external memory, not owned by ZIRModule.
752 sub_file_path: []const u8,
753 source: union(enum) {
754 unloaded: void,
755 bytes: [:0]const u8,
756 },
757 contents: union {
758 not_available: void,
759 module: *zir.Module,
760 },
761 status: enum {
762 never_loaded,
763 unloaded_success,
764 unloaded_parse_failure,
765 unloaded_sema_failure,
766
767 loaded_sema_failure,
768 loaded_success,
769 },
770
771 /// Even though .zir files only have 1 module, this set is still needed
772 /// because of anonymous Decls, which can exist in the global set, but
773 /// not this one.
774 decls: ArrayListUnmanaged(*Decl),
775
776 pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
777 switch (self.status) {
778 .never_loaded,
779 .unloaded_parse_failure,
780 .unloaded_sema_failure,
781 .unloaded_success,
782 => {},
783
784 .loaded_success => {
785 self.contents.module.deinit(gpa);
786 gpa.destroy(self.contents.module);
787 self.contents = .{ .not_available = {} };
788 self.status = .unloaded_success;
789 },
790 .loaded_sema_failure => {
791 self.contents.module.deinit(gpa);
792 gpa.destroy(self.contents.module);
793 self.contents = .{ .not_available = {} };
794 self.status = .unloaded_sema_failure;
795 },
796 }
797 switch (self.source) {
798 .bytes => |bytes| {
799 gpa.free(bytes);
800 self.source = .{ .unloaded = {} };
801 },
802 .unloaded => {},
803 }
804 }
805
806 pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
807 self.decls.deinit(gpa);
808 self.unload(gpa);
809 self.* = undefined;
810 }
811
812 pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
813 for (self.decls.items) |item, i| {
814 if (item == child) {
815 _ = self.decls.swapRemove(i);
816 return;
817 }
818 }
819 }
820
821 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
822 const loc = std.zig.findLineColumn(self.source.bytes, src);
823 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
824 }
825
826 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
827 switch (self.source) {
828 .unloaded => {
829 const source = try module.root_pkg.?.root_src_directory.handle.readFileAllocOptions(
830 module.gpa,
831 self.sub_file_path,
832 std.math.maxInt(u32),
833 null,
834 1,
835 0,
836 );
837 self.source = .{ .bytes = source };
838 return source;
839 },
840 .bytes => |bytes| return bytes,
841 }
842 }
843
844 pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
845 // ZIR modules only have 1 file with all decls global in the same namespace.
846 return std.zig.hashSrc(name);
847 }
848 };
849
850 /// This is a temporary structure, references to it are valid only
851 /// during semantic analysis of the block.
852 pub const Block = struct {
853 pub const base_tag: Tag = .block;
854 base: Scope = Scope{ .tag = base_tag },
855 parent: ?*Block,
856 func: ?*Fn,
857 decl: *Decl,
858 instructions: ArrayListUnmanaged(*Inst),
859 /// Points to the arena allocator of DeclAnalysis
860 arena: *Allocator,
861 label: ?Label = null,
862 is_comptime: bool,
863
864 pub const Label = struct {
865 zir_block: *zir.Inst.Block,
866 results: ArrayListUnmanaged(*Inst),
867 block_inst: *Inst.Block,
868 };
869 };
870
871 /// This is a temporary structure, references to it are valid only
872 /// during semantic analysis of the decl.
873 pub const DeclAnalysis = struct {
874 pub const base_tag: Tag = .decl;
875 base: Scope = Scope{ .tag = base_tag },
876 decl: *Decl,
877 arena: std.heap.ArenaAllocator,
878 };
879
880 /// This is a temporary structure, references to it are valid only
881 /// during semantic analysis of the decl.
882 pub const GenZIR = struct {
883 pub const base_tag: Tag = .gen_zir;
884 base: Scope = Scope{ .tag = base_tag },
885 /// Parents can be: `GenZIR`, `ZIRModule`, `File`
886 parent: *Scope,
887 decl: *Decl,
888 arena: *Allocator,
889 /// The first N instructions in a function body ZIR are arg instructions.
890 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
891 label: ?Label = null,
892
893 pub const Label = struct {
894 token: ast.TokenIndex,
895 block_inst: *zir.Inst.Block,
896 result_loc: astgen.ResultLoc,
897 };
898 };
899
900 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
901 /// This structure lives as long as the AST generation of the Block
902 /// node that contains the variable.
903 pub const LocalVal = struct {
904 pub const base_tag: Tag = .local_val;
905 base: Scope = Scope{ .tag = base_tag },
906 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
907 parent: *Scope,
908 gen_zir: *GenZIR,
909 name: []const u8,
910 inst: *zir.Inst,
911 };
912
913 /// This could be a `const` or `var` local. It has a pointer instead of a value.
914 /// This structure lives as long as the AST generation of the Block
915 /// node that contains the variable.
916 pub const LocalPtr = struct {
917 pub const base_tag: Tag = .local_ptr;
918 base: Scope = Scope{ .tag = base_tag },
919 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
920 parent: *Scope,
921 gen_zir: *GenZIR,
922 name: []const u8,
923 ptr: *zir.Inst,
924 };
925149};
926150
927151pub const AllErrors = struct {
......@@ -1029,12 +253,12 @@ pub const InitOptions = struct {
1029253 debug_link: bool = false,
1030254 stack_size_override: ?u64 = null,
1031255 self_exe_path: ?[]const u8 = null,
1032 version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 },
256 version: ?std.builtin.Version = null,
1033257 libc_installation: ?*const LibCInstallation = null,
1034258};
1035259
1036260pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1037 const mod: *Module = mod: {
261 const comp: *Module = comp: {
1038262 // For allocations that have the same lifetime as Module. This arena is used only during this
1039263 // initialization and then is freed in deinit().
1040264 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
......@@ -1043,7 +267,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1043267
1044268 // We put the `Module` itself in the arena. Freeing the arena will free the module.
1045269 // It's initialized later after we prepare the initialization options.
1046 const mod = try arena.create(Module);
270 const comp = try arena.create(Module);
1047271 const root_name = try arena.dupe(u8, options.root_name);
1048272
1049273 const ofmt = options.object_format orelse options.target.getObjectFormat();
......@@ -1153,75 +377,142 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1153377 // For example, one cannot change the target between updates, but one can change source files,
1154378 // so the target goes into the cache hash, but source files do not. This is so that we can
1155379 // find the same binary and incrementally update it even if there are modified source files.
1156 // We do this even if outputting to the current directory because (1) this cache_hash instance
1157 // will be the "parent" of other cache_hash instances such as for C objects, (2) we need
1158 // a place for intermediate build artifacts, such as a .o file to be linked with LLD, and (3)
1159 // we need somewhere to store serialization of incremental compilation metadata.
1160 var cache = try std.cache_hash.CacheHash.init(gpa, options.zig_cache_directory.handle, "h");
1161 errdefer cache.release();
1162
1163 // Now we will prepare hash state initializations to avoid redundantly computing hashes.
1164 // First we add common things between things that apply to zig source and all c source files.
1165 cache.addBytes(build_options.version);
1166 cache.add(options.optimize_mode);
1167 cache.add(options.target.cpu.arch);
1168 cache.addBytes(options.target.cpu.model.name);
1169 cache.add(options.target.cpu.features.ints);
1170 cache.add(options.target.os.tag);
1171 switch (options.target.os.tag) {
1172 .linux => {
1173 cache.add(options.target.os.version_range.linux.range.min);
1174 cache.add(options.target.os.version_range.linux.range.max);
1175 cache.add(options.target.os.version_range.linux.glibc);
1176 },
1177 .windows => {
1178 cache.add(options.target.os.version_range.windows.min);
1179 cache.add(options.target.os.version_range.windows.max);
1180 },
1181 .freebsd,
1182 .macosx,
1183 .ios,
1184 .tvos,
1185 .watchos,
1186 .netbsd,
1187 .openbsd,
1188 .dragonfly,
1189 => {
1190 cache.add(options.target.os.version_range.semver.min);
1191 cache.add(options.target.os.version_range.semver.max);
1192 },
1193 else => {},
1194 }
1195 cache.add(options.target.abi);
1196 cache.add(ofmt);
1197 cache.add(pic);
1198 cache.add(stack_check);
1199 cache.add(sanitize_c);
1200 cache.add(valgrind);
1201 cache.add(link_mode);
1202 cache.add(options.strip);
1203 cache.add(single_threaded);
380 // We do this even if outputting to the current directory because we need somewhere to store
381 // incremental compilation metadata.
382 const cache = try arena.create(std.cache_hash.Cache);
383 cache.* = .{
384 .gpa = gpa,
385 .manifest_dir = try options.zig_cache_directory.handle.makeOpenPath("h", .{}),
386 };
387 errdefer cache.manifest_dir.close();
388
389 // This is shared hasher state common to zig source and all C source files.
390 cache.hash.addBytes(build_options.version);
391 cache.hash.addBytes(options.zig_lib_directory.path orelse ".");
392 cache.hash.add(options.optimize_mode);
393 cache.hash.add(options.target.cpu.arch);
394 cache.hash.addBytes(options.target.cpu.model.name);
395 cache.hash.add(options.target.cpu.features.ints);
396 cache.hash.add(options.target.os.tag);
397 cache.hash.add(options.target.abi);
398 cache.hash.add(ofmt);
399 cache.hash.add(pic);
400 cache.hash.add(stack_check);
401 cache.hash.add(link_mode);
402 cache.hash.add(options.strip);
403 cache.hash.add(options.link_libc);
404 cache.hash.add(options.output_mode);
1204405 // TODO audit this and make sure everything is in it
1205406
1206 // We don't care whether we find something there, just show us the digest.
1207 const digest = (try cache.hit()) orelse cache.final();
1208
1209 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
1210 var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
1211 errdefer artifact_dir.close();
1212 const zig_cache_artifact_directory: Directory = .{
1213 .handle = artifact_dir,
1214 .path = if (options.zig_cache_directory.path) |p|
1215 try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir })
1216 else
1217 artifact_sub_dir,
407 const zig_module: ?*ZigModule = if (options.root_pkg) |root_pkg| blk: {
408 // Options that are specific to zig source files, that cannot be
409 // modified between incremental updates.
410 var hash = cache.hash;
411
412 hash.add(valgrind);
413 hash.add(single_threaded);
414 switch (options.target.os.getVersionRange()) {
415 .linux => |linux| {
416 hash.add(linux.range.min);
417 hash.add(linux.range.max);
418 hash.add(linux.glibc);
419 },
420 .windows => |windows| {
421 hash.add(windows.min);
422 hash.add(windows.max);
423 },
424 .semver => |semver| {
425 hash.add(semver.min);
426 hash.add(semver.max);
427 },
428 .none => {},
429 }
430
431 const digest = hash.final();
432 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
433 var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
434 errdefer artifact_dir.close();
435 const zig_cache_artifact_directory: Directory = .{
436 .handle = artifact_dir,
437 .path = if (options.zig_cache_directory.path) |p|
438 try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir })
439 else
440 artifact_sub_dir,
441 };
442
443 // TODO when we implement serialization and deserialization of incremental compilation metadata,
444 // this is where we would load it. We have open a handle to the directory where
445 // the output either already is, or will be.
446 // However we currently do not have serialization of such metadata, so for now
447 // we set up an empty ZigModule that does the entire compilation fresh.
448
449 const root_scope = rs: {
450 if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
451 const root_scope = try gpa.create(ZigModule.Scope.File);
452 root_scope.* = .{
453 .sub_file_path = root_pkg.root_src_path,
454 .source = .{ .unloaded = {} },
455 .contents = .{ .not_available = {} },
456 .status = .never_loaded,
457 .root_container = .{
458 .file_scope = root_scope,
459 .decls = .{},
460 },
461 };
462 break :rs &root_scope.base;
463 } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
464 const root_scope = try gpa.create(ZigModule.Scope.ZIRModule);
465 root_scope.* = .{
466 .sub_file_path = root_pkg.root_src_path,
467 .source = .{ .unloaded = {} },
468 .contents = .{ .not_available = {} },
469 .status = .never_loaded,
470 .decls = .{},
471 };
472 break :rs &root_scope.base;
473 } else {
474 unreachable;
475 }
476 };
477
478 const zig_module = try arena.create(ZigModule);
479 zig_module.* = .{
480 .gpa = gpa,
481 .comp = comp,
482 .root_pkg = root_pkg,
483 .root_scope = root_scope,
484 .zig_cache_artifact_directory = zig_cache_artifact_directory,
485 };
486 break :blk zig_module;
487 } else null;
488 errdefer if (zig_module) |zm| zm.deinit();
489
490 // For resource management purposes.
491 var owned_link_dir: ?std.fs.Dir = null;
492 errdefer if (owned_link_dir) |*dir| dir.close();
493
494 const bin_directory = emit_bin.directory orelse blk: {
495 if (zig_module) |zm| break :blk zm.zig_cache_artifact_directory;
496
497 const digest = cache.hash.peek();
498 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
499 var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
500 owned_link_dir = artifact_dir;
501 const link_artifact_directory: Directory = .{
502 .handle = artifact_dir,
503 .path = if (options.zig_cache_directory.path) |p|
504 try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir })
505 else
506 artifact_sub_dir,
507 };
508 break :blk link_artifact_directory;
1218509 };
1219510
1220511 const bin_file = try link.File.openPath(gpa, .{
1221 .directory = emit_bin.directory orelse zig_cache_artifact_directory,
512 .directory = bin_directory,
1222513 .sub_path = emit_bin.basename,
1223514 .root_name = root_name,
1224 .root_pkg = options.root_pkg,
515 .zig_module = zig_module,
1225516 .target = options.target,
1226517 .dynamic_linker = options.dynamic_linker,
1227518 .output_mode = options.output_mode,
......@@ -1263,70 +554,33 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1263554 });
1264555 errdefer bin_file.destroy();
1265556
1266 // We arena-allocate the root scope so there is no free needed.
1267 const root_scope = blk: {
1268 if (options.root_pkg) |root_pkg| {
1269 if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
1270 const root_scope = try gpa.create(Scope.File);
1271 root_scope.* = .{
1272 .sub_file_path = root_pkg.root_src_path,
1273 .source = .{ .unloaded = {} },
1274 .contents = .{ .not_available = {} },
1275 .status = .never_loaded,
1276 .root_container = .{
1277 .file_scope = root_scope,
1278 .decls = .{},
1279 },
1280 };
1281 break :blk &root_scope.base;
1282 } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
1283 const root_scope = try gpa.create(Scope.ZIRModule);
1284 root_scope.* = .{
1285 .sub_file_path = root_pkg.root_src_path,
1286 .source = .{ .unloaded = {} },
1287 .contents = .{ .not_available = {} },
1288 .status = .never_loaded,
1289 .decls = .{},
1290 };
1291 break :blk &root_scope.base;
1292 } else {
1293 unreachable;
1294 }
1295 } else {
1296 const root_scope = try gpa.create(Scope.None);
1297 root_scope.* = .{};
1298 break :blk &root_scope.base;
1299 }
1300 };
1301
1302 mod.* = .{
557 comp.* = .{
1303558 .gpa = gpa,
1304559 .arena_state = arena_allocator.state,
1305560 .zig_lib_directory = options.zig_lib_directory,
1306561 .zig_cache_directory = options.zig_cache_directory,
1307 .zig_cache_artifact_directory = zig_cache_artifact_directory,
1308 .root_pkg = options.root_pkg,
1309 .root_scope = root_scope,
1310562 .bin_file = bin_file,
1311563 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
1312564 .keep_source_files_loaded = options.keep_source_files_loaded,
1313565 .use_clang = use_clang,
1314566 .clang_argv = options.clang_argv,
1315567 .c_source_files = options.c_source_files,
1316 .cache = cache,
568 .cache_parent = cache,
1317569 .self_exe_path = options.self_exe_path,
1318570 .libc_include_dir_list = libc_dirs.libc_include_dir_list,
1319571 .sanitize_c = sanitize_c,
1320572 .rand = options.rand,
1321573 .clang_passthrough_mode = options.clang_passthrough_mode,
1322574 .debug_cc = options.debug_cc,
575 .disable_c_depfile = options.disable_c_depfile,
576 .owned_link_dir = owned_link_dir,
1323577 };
1324 break :mod mod;
578 break :comp comp;
1325579 };
1326 errdefer mod.destroy();
580 errdefer comp.destroy();
1327581
1328582 // Add a `CObject` for each `c_source_files`.
1329 try mod.c_object_table.ensureCapacity(gpa, options.c_source_files.len);
583 try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len);
1330584 for (options.c_source_files) |c_source_file| {
1331585 var local_arena = std.heap.ArenaAllocator.init(gpa);
1332586 errdefer local_arena.deinit();
......@@ -1335,28 +589,29 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1335589
1336590 c_object.* = .{
1337591 .status = .{ .new = {} },
1338 // TODO why are we duplicating this memory? do we need to?
1339 // look into refactoring to turn these 2 fields simply into a CSourceFile
592 // TODO look into refactoring to turn these 2 fields simply into a CSourceFile
1340593 .src_path = try local_arena.allocator.dupe(u8, c_source_file.src_path),
1341594 .extra_flags = try local_arena.allocator.dupe([]const u8, c_source_file.extra_flags),
1342595 .arena = local_arena.state,
1343596 };
1344 mod.c_object_table.putAssumeCapacityNoClobber(c_object, {});
597 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
1345598 }
1346599
1347600 // If we need to build glibc for the target, add work items for it.
1348601 // We go through the work queue so that building can be done in parallel.
1349 if (mod.wantBuildGLibCFromSource()) {
1350 try mod.addBuildingGLibCWorkItems();
602 if (comp.wantBuildGLibCFromSource()) {
603 try comp.addBuildingGLibCWorkItems();
1351604 }
1352605
1353 return mod;
606 return comp;
1354607}
1355608
1356609pub fn destroy(self: *Module) void {
610 const optional_zig_module = self.bin_file.options.zig_module;
1357611 self.bin_file.destroy();
612 if (optional_zig_module) |zig_module| zig_module.deinit();
613
1358614 const gpa = self.gpa;
1359 self.deletion_set.deinit(gpa);
1360615 self.work_queue.deinit();
1361616
1362617 {
......@@ -1368,86 +623,32 @@ pub fn destroy(self: *Module) void {
1368623 self.crt_files.deinit(gpa);
1369624 }
1370625
1371 for (self.decl_table.items()) |entry| {
1372 entry.value.destroy(gpa);
1373 }
1374 self.decl_table.deinit(gpa);
1375
1376626 for (self.c_object_table.items()) |entry| {
1377627 entry.key.destroy(gpa);
1378628 }
1379629 self.c_object_table.deinit(gpa);
1380630
1381 for (self.failed_decls.items()) |entry| {
1382 entry.value.destroy(gpa);
1383 }
1384 self.failed_decls.deinit(gpa);
1385
1386631 for (self.failed_c_objects.items()) |entry| {
1387632 entry.value.destroy(gpa);
1388633 }
1389634 self.failed_c_objects.deinit(gpa);
1390635
1391 for (self.failed_files.items()) |entry| {
1392 entry.value.destroy(gpa);
1393 }
1394 self.failed_files.deinit(gpa);
1395
1396 for (self.failed_exports.items()) |entry| {
1397 entry.value.destroy(gpa);
1398 }
1399 self.failed_exports.deinit(gpa);
1400
1401 for (self.decl_exports.items()) |entry| {
1402 const export_list = entry.value;
1403 gpa.free(export_list);
1404 }
1405 self.decl_exports.deinit(gpa);
1406
1407 for (self.export_owners.items()) |entry| {
1408 freeExportList(gpa, entry.value);
1409 }
1410 self.export_owners.deinit(gpa);
1411
1412 self.symbol_exports.deinit(gpa);
1413 self.root_scope.destroy(gpa);
1414
1415 var it = self.global_error_set.iterator();
1416 while (it.next()) |entry| {
1417 gpa.free(entry.key);
1418 }
1419 self.global_error_set.deinit(gpa);
1420
1421 self.zig_cache_artifact_directory.handle.close();
1422 self.cache.release();
636 self.cache_parent.manifest_dir.close();
637 if (self.owned_link_dir) |*dir| dir.close();
1423638
1424639 // This destroys `self`.
1425640 self.arena_state.promote(gpa).deinit();
1426641}
1427642
1428fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
1429 for (export_list) |exp| {
1430 gpa.free(exp.options.name);
1431 gpa.destroy(exp);
1432 }
1433 gpa.free(export_list);
1434}
1435
1436643pub fn getTarget(self: Module) Target {
1437644 return self.bin_file.options.target;
1438645}
1439646
1440pub fn optimizeMode(self: Module) std.builtin.Mode {
1441 return self.bin_file.options.optimize_mode;
1442}
1443
1444647/// Detect changes to source files, perform semantic analysis, and update the output files.
1445648pub fn update(self: *Module) !void {
1446649 const tracy = trace(@src());
1447650 defer tracy.end();
1448651
1449 self.generation += 1;
1450
1451652 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
1452653 // TODO Look into caching this data in memory to improve performance.
1453654 // Add a WorkItem for each C object.
......@@ -1456,36 +657,42 @@ pub fn update(self: *Module) !void {
1456657 self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key });
1457658 }
1458659
1459 // TODO Detect which source files changed.
1460 // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
1461 // to force a refresh we unload now.
1462 if (self.root_scope.cast(Scope.File)) |zig_file| {
1463 zig_file.unload(self.gpa);
1464 self.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
1465 error.AnalysisFail => {
1466 assert(self.totalErrorCount() != 0);
1467 },
1468 else => |e| return e,
1469 };
1470 } else if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| {
1471 zir_module.unload(self.gpa);
1472 self.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
1473 error.AnalysisFail => {
1474 assert(self.totalErrorCount() != 0);
1475 },
1476 else => |e| return e,
1477 };
660 if (self.bin_file.options.zig_module) |zig_module| {
661 zig_module.generation += 1;
662
663 // TODO Detect which source files changed.
664 // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
665 // to force a refresh we unload now.
666 if (zig_module.root_scope.cast(ZigModule.Scope.File)) |zig_file| {
667 zig_file.unload(zig_module.gpa);
668 zig_module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
669 error.AnalysisFail => {
670 assert(self.totalErrorCount() != 0);
671 },
672 else => |e| return e,
673 };
674 } else if (zig_module.root_scope.cast(ZigModule.Scope.ZIRModule)) |zir_module| {
675 zir_module.unload(zig_module.gpa);
676 zig_module.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
677 error.AnalysisFail => {
678 assert(self.totalErrorCount() != 0);
679 },
680 else => |e| return e,
681 };
682 }
1478683 }
1479684
1480685 try self.performAllTheWork();
1481686
1482 // Process the deletion set.
1483 while (self.deletion_set.popOrNull()) |decl| {
1484 if (decl.dependants.items().len != 0) {
1485 decl.deletion_flag = false;
1486 continue;
687 if (self.bin_file.options.zig_module) |zig_module| {
688 // Process the deletion set.
689 while (zig_module.deletion_set.popOrNull()) |decl| {
690 if (decl.dependants.items().len != 0) {
691 decl.deletion_flag = false;
692 continue;
693 }
694 try zig_module.deleteDecl(decl);
1487695 }
1488 try self.deleteDecl(decl);
1489696 }
1490697
1491698 // This is needed before reading the error flags.
......@@ -1496,7 +703,9 @@ pub fn update(self: *Module) !void {
1496703 // If there are any errors, we anticipate the source files being loaded
1497704 // to report error messages. Otherwise we unload all source files to save memory.
1498705 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
1499 self.root_scope.unload(self.gpa);
706 if (self.bin_file.options.zig_module) |zig_module| {
707 zig_module.root_scope.unload(self.gpa);
708 }
1500709 }
1501710}
1502711
......@@ -1513,11 +722,20 @@ pub fn makeBinFileWritable(self: *Module) !void {
1513722}
1514723
1515724pub fn totalErrorCount(self: *Module) usize {
1516 const total = self.failed_decls.items().len +
1517 self.failed_c_objects.items().len +
1518 self.failed_files.items().len +
1519 self.failed_exports.items().len;
1520 return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total;
725 var total: usize = self.failed_c_objects.items().len;
726
727 if (self.bin_file.options.zig_module) |zig_module| {
728 total += zig_module.failed_decls.items().len +
729 zig_module.failed_exports.items().len +
730 zig_module.failed_files.items().len;
731 }
732
733 // The "no entry point found" error only counts if there are no other errors.
734 if (total == 0) {
735 return @boolToInt(self.link_error_flags.no_entry_point_found);
736 }
737
738 return total;
1521739}
1522740
1523741pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
......@@ -1530,31 +748,32 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
1530748 for (self.failed_c_objects.items()) |entry| {
1531749 const c_object = entry.key;
1532750 const err_msg = entry.value;
1533 const source = c_object.status.failure;
1534 try AllErrors.add(&arena, &errors, c_object.src_path, source, err_msg.*);
1535 }
1536 for (self.failed_files.items()) |entry| {
1537 const scope = entry.key;
1538 const err_msg = entry.value;
1539 const source = try scope.getSource(self);
1540 try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
1541 }
1542 for (self.failed_decls.items()) |entry| {
1543 const decl = entry.key;
1544 const err_msg = entry.value;
1545 const source = try decl.scope.getSource(self);
1546 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
1547 }
1548 for (self.failed_exports.items()) |entry| {
1549 const decl = entry.key.owner_decl;
1550 const err_msg = entry.value;
1551 const source = try decl.scope.getSource(self);
1552 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
751 try AllErrors.add(&arena, &errors, c_object.src_path, "", err_msg.*);
752 }
753 if (self.bin_file.options.zig_module) |zig_module| {
754 for (zig_module.failed_files.items()) |entry| {
755 const scope = entry.key;
756 const err_msg = entry.value;
757 const source = try scope.getSource(zig_module);
758 try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
759 }
760 for (zig_module.failed_decls.items()) |entry| {
761 const decl = entry.key;
762 const err_msg = entry.value;
763 const source = try decl.scope.getSource(zig_module);
764 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
765 }
766 for (zig_module.failed_exports.items()) |entry| {
767 const decl = entry.key.owner_decl;
768 const err_msg = entry.value;
769 const source = try decl.scope.getSource(zig_module);
770 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
771 }
1553772 }
1554773
1555774 if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
1556775 const global_err_src_path = blk: {
1557 if (self.root_pkg) |root_pkg| break :blk root_pkg.root_src_path;
776 if (self.bin_file.options.zig_module) |zig_module| break :blk zig_module.root_pkg.root_src_path;
1558777 if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path;
1559778 if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0];
1560779 break :blk "(no file)";
......@@ -1590,9 +809,10 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1590809 => continue,
1591810
1592811 .complete, .codegen_failure_retryable => {
812 const zig_module = self.bin_file.options.zig_module.?;
1593813 if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
1594814 switch (payload.func.analysis) {
1595 .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
815 .queued => zig_module.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
1596816 error.AnalysisFail => {
1597817 assert(payload.func.analysis != .in_progress);
1598818 continue;
......@@ -1605,23 +825,23 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1605825 }
1606826 // Here we tack on additional allocations to the Decl's arena. The allocations are
1607827 // lifetime annotations in the ZIR.
1608 var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
828 var decl_arena = decl.typed_value.most_recent.arena.?.promote(zig_module.gpa);
1609829 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
1610830 log.debug("analyze liveness of {}\n", .{decl.name});
1611 try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success);
831 try liveness.analyze(zig_module.gpa, &decl_arena.allocator, payload.func.analysis.success);
1612832 }
1613833
1614834 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
1615835
1616 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
836 self.bin_file.updateDecl(zig_module, decl) catch |err| switch (err) {
1617837 error.OutOfMemory => return error.OutOfMemory,
1618838 error.AnalysisFail => {
1619839 decl.analysis = .dependency_failure;
1620840 },
1621841 else => {
1622 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1623 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1624 self.gpa,
842 try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1);
843 zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
844 zig_module.gpa,
1625845 decl.src(),
1626846 "unable to codegen: {}",
1627847 .{@errorName(err)},
......@@ -1632,16 +852,18 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1632852 },
1633853 },
1634854 .analyze_decl => |decl| {
1635 self.ensureDeclAnalyzed(decl) catch |err| switch (err) {
855 const zig_module = self.bin_file.options.zig_module.?;
856 zig_module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
1636857 error.OutOfMemory => return error.OutOfMemory,
1637858 error.AnalysisFail => continue,
1638859 };
1639860 },
1640861 .update_line_number => |decl| {
1641 self.bin_file.updateDeclLineNumber(self, decl) catch |err| {
1642 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1643 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1644 self.gpa,
862 const zig_module = self.bin_file.options.zig_module.?;
863 self.bin_file.updateDeclLineNumber(zig_module, decl) catch |err| {
864 try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1);
865 zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
866 zig_module.gpa,
1645867 decl.src(),
1646868 "unable to update line number: {}",
1647869 .{@errorName(err)},
......@@ -1650,21 +872,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1650872 };
1651873 },
1652874 .c_object => |c_object| {
1653 // Free the previous attempt.
1654 switch (c_object.status) {
1655 .new => {},
1656 .success => |o_file_path| {
1657 self.gpa.free(o_file_path);
1658 c_object.status = .{ .new = {} };
1659 },
1660 .failure => |source| {
1661 self.failed_c_objects.removeAssertDiscard(c_object);
1662 self.gpa.free(source);
1663
1664 c_object.status = .{ .new = {} };
1665 },
1666 }
1667 self.buildCObject(c_object) catch |err| switch (err) {
875 self.updateCObject(c_object) catch |err| switch (err) {
1668876 error.AnalysisFail => continue,
1669877 else => {
1670878 try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
......@@ -1674,7 +882,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1674882 "unable to build C object: {}",
1675883 .{@errorName(err)},
1676884 ));
1677 c_object.status = .{ .failure = "" };
885 c_object.status = .{ .failure = {} };
1678886 },
1679887 };
1680888 },
......@@ -1692,127 +900,175 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1692900 };
1693901}
1694902
1695fn buildCObject(mod: *Module, c_object: *CObject) !void {
903fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
1696904 const tracy = trace(@src());
1697905 defer tracy.end();
1698906
1699 // TODO this C source file needs its own cache hash instance
1700
1701907 if (!build_options.have_llvm) {
1702 return mod.failCObj(c_object, "clang not available: compiler not built with LLVM extensions enabled", .{});
908 return comp.failCObj(c_object, "clang not available: compiler not built with LLVM extensions enabled", .{});
909 }
910 const self_exe_path = comp.self_exe_path orelse
911 return comp.failCObj(c_object, "clang compilation disabled", .{});
912
913 if (c_object.clearStatus(comp.gpa)) {
914 // There was previous failure.
915 comp.failed_c_objects.removeAssertDiscard(c_object);
916 }
917
918 var ch = comp.cache_parent.obtain();
919 defer ch.deinit();
920
921 ch.hash.add(comp.sanitize_c);
922 ch.hash.addListOfBytes(comp.clang_argv);
923 ch.hash.add(comp.bin_file.options.link_libcpp);
924 ch.hash.addListOfBytes(comp.libc_include_dir_list);
925 // TODO
926 //cache_int(cache_hash, g->code_model);
927 //cache_bool(cache_hash, codegen_have_frame_pointer(g));
928 _ = try ch.addFile(c_object.src_path, null);
929 {
930 // Hash the extra flags, with special care to call addFile for file parameters.
931 // TODO this logic can likely be improved by utilizing clang_options_data.zig.
932 const file_args = [_][]const u8{"-include"};
933 var arg_i: usize = 0;
934 while (arg_i < c_object.extra_flags.len) : (arg_i += 1) {
935 const arg = c_object.extra_flags[arg_i];
936 ch.hash.addBytes(arg);
937 for (file_args) |file_arg| {
938 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.extra_flags.len) {
939 arg_i += 1;
940 _ = try ch.addFile(c_object.extra_flags[arg_i], null);
941 }
942 }
943 }
1703944 }
1704 const self_exe_path = mod.self_exe_path orelse
1705 return mod.failCObj(c_object, "clang compilation disabled", .{});
1706945
1707 var arena_allocator = std.heap.ArenaAllocator.init(mod.gpa);
946 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
1708947 defer arena_allocator.deinit();
1709948 const arena = &arena_allocator.allocator;
1710949
1711 var argv = std.ArrayList([]const u8).init(mod.gpa);
1712 defer argv.deinit();
1713
1714950 const c_source_basename = std.fs.path.basename(c_object.src_path);
1715951 // Special case when doing build-obj for just one C file. When there are more than one object
1716952 // file and building an object we need to link them together, but with just one it should go
1717953 // directly to the output file.
1718 const direct_o = mod.c_source_files.len == 1 and mod.root_pkg == null and
1719 mod.bin_file.options.output_mode == .Obj and mod.bin_file.options.objects.len == 0;
954 const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.zig_module == null and
955 comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0;
1720956 const o_basename_noext = if (direct_o)
1721 mod.bin_file.options.root_name
957 comp.bin_file.options.root_name
1722958 else
1723959 mem.split(c_source_basename, ".").next().?;
1724 const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, mod.getTarget().oFileExt() });
960 const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, comp.getTarget().oFileExt() });
1725961
1726 // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
1727 const out_obj_path = try mod.tmpFilePath(arena, o_basename);
962 const full_object_path = if (!(try ch.hit()) or comp.disable_c_depfile) blk: {
963 var argv = std.ArrayList([]const u8).init(comp.gpa);
964 defer argv.deinit();
1728965
1729 var zig_cache_tmp_dir = try mod.zig_cache_directory.handle.makeOpenPath("tmp", .{});
1730 defer zig_cache_tmp_dir.close();
966 // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
967 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
1731968
1732 try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" });
969 try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" });
1733970
1734 const ext = classifyFileExt(c_object.src_path);
1735 // TODO capture the .d file and deal with caching stuff
1736 try mod.addCCArgs(arena, &argv, ext, false, null);
971 const ext = classifyFileExt(c_object.src_path);
972 // TODO capture the .d file and deal with caching stuff
973 try comp.addCCArgs(arena, &argv, ext, false, null);
1737974
1738 try argv.append("-o");
1739 try argv.append(out_obj_path);
975 try argv.append("-o");
976 try argv.append(out_obj_path);
1740977
1741 try argv.append(c_object.src_path);
1742 try argv.appendSlice(c_object.extra_flags);
978 try argv.append(c_object.src_path);
979 try argv.appendSlice(c_object.extra_flags);
1743980
1744 if (mod.debug_cc) {
1745 for (argv.items[0 .. argv.items.len - 1]) |arg| {
1746 std.debug.print("{} ", .{arg});
981 if (comp.debug_cc) {
982 for (argv.items[0 .. argv.items.len - 1]) |arg| {
983 std.debug.print("{} ", .{arg});
984 }
985 std.debug.print("{}\n", .{argv.items[argv.items.len - 1]});
1747986 }
1748 std.debug.print("{}\n", .{argv.items[argv.items.len - 1]});
1749 }
1750987
1751 const child = try std.ChildProcess.init(argv.items, arena);
1752 defer child.deinit();
988 const child = try std.ChildProcess.init(argv.items, arena);
989 defer child.deinit();
1753990
1754 if (mod.clang_passthrough_mode) {
1755 child.stdin_behavior = .Inherit;
1756 child.stdout_behavior = .Inherit;
1757 child.stderr_behavior = .Inherit;
991 if (comp.clang_passthrough_mode) {
992 child.stdin_behavior = .Inherit;
993 child.stdout_behavior = .Inherit;
994 child.stderr_behavior = .Inherit;
1758995
1759 const term = child.spawnAndWait() catch |err| {
1760 return mod.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
1761 };
1762 switch (term) {
1763 .Exited => |code| {
1764 if (code != 0) {
1765 // TODO make std.process.exit and std.ChildProcess exit code have the same type
1766 // and forward it here. Currently it is u32 vs u8.
1767 std.process.exit(1);
1768 }
1769 },
1770 else => std.process.exit(1),
1771 }
1772 } else {
1773 child.stdin_behavior = .Ignore;
1774 child.stdout_behavior = .Pipe;
1775 child.stderr_behavior = .Pipe;
996 const term = child.spawnAndWait() catch |err| {
997 return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
998 };
999 switch (term) {
1000 .Exited => |code| {
1001 if (code != 0) {
1002 // TODO make std.process.exit and std.ChildProcess exit code have the same type
1003 // and forward it here. Currently it is u32 vs u8.
1004 std.process.exit(1);
1005 }
1006 },
1007 else => std.process.exit(1),
1008 }
1009 } else {
1010 child.stdin_behavior = .Ignore;
1011 child.stdout_behavior = .Pipe;
1012 child.stderr_behavior = .Pipe;
17761013
1777 try child.spawn();
1014 try child.spawn();
17781015
1779 const stdout_reader = child.stdout.?.reader();
1780 const stderr_reader = child.stderr.?.reader();
1016 const stdout_reader = child.stdout.?.reader();
1017 const stderr_reader = child.stderr.?.reader();
17811018
1782 // TODO Need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
1783 const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
1784 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
1019 // TODO Need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
1020 const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
1021 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
17851022
1786 const term = child.wait() catch |err| {
1787 return mod.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
1788 };
1023 const term = child.wait() catch |err| {
1024 return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
1025 };
17891026
1790 switch (term) {
1791 .Exited => |code| {
1792 if (code != 0) {
1793 // TODO parse clang stderr and turn it into an error message
1794 // and then call failCObjWithOwnedErrorMsg
1795 std.log.err("clang failed with stderr: {}", .{stderr});
1796 return mod.failCObj(c_object, "clang exited with code {}", .{code});
1797 }
1798 },
1799 else => {
1800 std.log.err("clang terminated with stderr: {}", .{stderr});
1801 return mod.failCObj(c_object, "clang terminated unexpectedly", .{});
1802 },
1027 switch (term) {
1028 .Exited => |code| {
1029 if (code != 0) {
1030 // TODO parse clang stderr and turn it into an error message
1031 // and then call failCObjWithOwnedErrorMsg
1032 std.log.err("clang failed with stderr: {}", .{stderr});
1033 return comp.failCObj(c_object, "clang exited with code {}", .{code});
1034 }
1035 },
1036 else => {
1037 std.log.err("clang terminated with stderr: {}", .{stderr});
1038 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
1039 },
1040 }
18031041 }
1804 }
18051042
1806 // TODO handle .d files
1043 // TODO handle .d files
18071044
1808 // TODO Add renameat capabilities to the std lib in a higher layer than the posix layer.
1809 const tmp_basename = std.fs.path.basename(out_obj_path);
1810 try std.os.renameat(zig_cache_tmp_dir.fd, tmp_basename, mod.zig_cache_artifact_directory.handle.fd, o_basename);
1045 // Rename into place.
1046 const digest = ch.final();
1047 const full_object_path = if (comp.zig_cache_directory.path) |p|
1048 try std.fs.path.join(arena, &[_][]const u8{ p, "o", &digest, o_basename })
1049 else
1050 try std.fs.path.join(arena, &[_][]const u8{ "o", &digest, o_basename });
1051 try std.fs.rename(out_obj_path, full_object_path);
18111052
1812 const success_file_path = try std.fs.path.join(mod.gpa, &[_][]const u8{
1813 mod.zig_cache_artifact_directory.path.?, o_basename,
1814 });
1815 c_object.status = .{ .success = success_file_path };
1053 ch.writeManifest() catch |err| {
1054 std.log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src_path, @errorName(err) });
1055 };
1056 break :blk full_object_path;
1057 } else blk: {
1058 const digest = ch.final();
1059 const full_object_path = if (comp.zig_cache_directory.path) |p|
1060 try std.fs.path.join(arena, &[_][]const u8{ p, "o", &digest, o_basename })
1061 else
1062 try std.fs.path.join(arena, &[_][]const u8{ "o", &digest, o_basename });
1063 break :blk full_object_path;
1064 };
1065
1066 c_object.status = .{
1067 .success = .{
1068 .object_path = full_object_path,
1069 .lock = ch.toOwnedLock(),
1070 },
1071 };
18161072}
18171073
18181074fn tmpFilePath(mod: *Module, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
......@@ -2016,2041 +1272,37 @@ fn addCCArgs(
20161272 try argv.appendSlice(mod.clang_argv);
20171273}
20181274
2019pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
2020 const tracy = trace(@src());
2021 defer tracy.end();
2022
2023 const subsequent_analysis = switch (decl.analysis) {
2024 .in_progress => unreachable,
2025
2026 .sema_failure,
2027 .sema_failure_retryable,
2028 .codegen_failure,
2029 .dependency_failure,
2030 .codegen_failure_retryable,
2031 => return error.AnalysisFail,
2032
2033 .complete => return,
2034
2035 .outdated => blk: {
2036 log.debug("re-analyzing {}\n", .{decl.name});
2037
2038 // The exports this Decl performs will be re-discovered, so we remove them here
2039 // prior to re-analysis.
2040 self.deleteDeclExports(decl);
2041 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
2042 for (decl.dependencies.items()) |entry| {
2043 const dep = entry.key;
2044 dep.removeDependant(decl);
2045 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
2046 // We don't perform a deletion here, because this Decl or another one
2047 // may end up referencing it before the update is complete.
2048 dep.deletion_flag = true;
2049 try self.deletion_set.append(self.gpa, dep);
2050 }
2051 }
2052 decl.dependencies.clearRetainingCapacity();
2053
2054 break :blk true;
2055 },
2056
2057 .unreferenced => false,
2058 };
2059
2060 const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
2061 try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
2062 else
2063 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
2064 error.OutOfMemory => return error.OutOfMemory,
2065 error.AnalysisFail => return error.AnalysisFail,
2066 else => {
2067 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
2068 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
2069 self.gpa,
2070 decl.src(),
2071 "unable to analyze: {}",
2072 .{@errorName(err)},
2073 ));
2074 decl.analysis = .sema_failure_retryable;
2075 return error.AnalysisFail;
2076 },
2077 };
2078
2079 if (subsequent_analysis) {
2080 // We may need to chase the dependants and re-analyze them.
2081 // However, if the decl is a function, and the type is the same, we do not need to.
2082 if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
2083 for (decl.dependants.items()) |entry| {
2084 const dep = entry.key;
2085 switch (dep.analysis) {
2086 .unreferenced => unreachable,
2087 .in_progress => unreachable,
2088 .outdated => continue, // already queued for update
2089
2090 .dependency_failure,
2091 .sema_failure,
2092 .sema_failure_retryable,
2093 .codegen_failure,
2094 .codegen_failure_retryable,
2095 .complete,
2096 => if (dep.generation != self.generation) {
2097 try self.markOutdatedDecl(dep);
2098 },
2099 }
2100 }
2101 }
2102 }
1275fn failCObj(mod: *Module, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
1276 @setCold(true);
1277 const err_msg = try ErrorMsg.create(mod.gpa, 0, "unable to build C object: " ++ format, args);
1278 return mod.failCObjWithOwnedErrorMsg(c_object, err_msg);
21031279}
21041280
2105fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
2106 const tracy = trace(@src());
2107 defer tracy.end();
2108
2109 const container_scope = decl.scope.cast(Scope.Container).?;
2110 const tree = try self.getAstTree(container_scope);
2111 const ast_node = tree.root_node.decls()[decl.src_index];
2112 switch (ast_node.tag) {
2113 .FnProto => {
2114 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
2115
2116 decl.analysis = .in_progress;
2117
2118 // This arena allocator's memory is discarded at the end of this function. It is used
2119 // to determine the type of the function, and hence the type of the decl, which is needed
2120 // to complete the Decl analysis.
2121 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
2122 defer fn_type_scope_arena.deinit();
2123 var fn_type_scope: Scope.GenZIR = .{
2124 .decl = decl,
2125 .arena = &fn_type_scope_arena.allocator,
2126 .parent = decl.scope,
2127 };
2128 defer fn_type_scope.instructions.deinit(self.gpa);
2129
2130 decl.is_pub = fn_proto.getVisibToken() != null;
2131 const body_node = fn_proto.getBodyNode() orelse
2132 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
2133
2134 const param_decls = fn_proto.params();
2135 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
2136
2137 const fn_src = tree.token_locs[fn_proto.fn_token].start;
2138 const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
2139 .ty = Type.initTag(.type),
2140 .val = Value.initTag(.type_type),
2141 });
2142 const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
2143 for (param_decls) |param_decl, i| {
2144 const param_type_node = switch (param_decl.param_type) {
2145 .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
2146 .type_expr => |node| node,
2147 };
2148 param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
2149 }
2150 if (fn_proto.getVarArgsToken()) |var_args_token| {
2151 return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
2152 }
2153 if (fn_proto.getLibName()) |lib_name| {
2154 return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
2155 }
2156 if (fn_proto.getAlignExpr()) |align_expr| {
2157 return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
2158 }
2159 if (fn_proto.getSectionExpr()) |sect_expr| {
2160 return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
2161 }
2162 if (fn_proto.getCallconvExpr()) |callconv_expr| {
2163 return self.failNode(
2164 &fn_type_scope.base,
2165 callconv_expr,
2166 "TODO implement function calling convention expression",
2167 .{},
2168 );
2169 }
2170 const return_type_expr = switch (fn_proto.return_type) {
2171 .Explicit => |node| node,
2172 .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
2173 .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
2174 };
2175
2176 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
2177 const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
2178 .return_type = return_type_inst,
2179 .param_types = param_types,
2180 }, .{});
2181
2182 // We need the memory for the Type to go into the arena for the Decl
2183 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
2184 errdefer decl_arena.deinit();
2185 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
2186
2187 var block_scope: Scope.Block = .{
2188 .parent = null,
2189 .func = null,
2190 .decl = decl,
2191 .instructions = .{},
2192 .arena = &decl_arena.allocator,
2193 .is_comptime = false,
2194 };
2195 defer block_scope.instructions.deinit(self.gpa);
2196
2197 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
2198 .instructions = fn_type_scope.instructions.items,
2199 });
2200 const new_func = try decl_arena.allocator.create(Fn);
2201 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
2202
2203 const fn_zir = blk: {
2204 // This scope's arena memory is discarded after the ZIR generation
2205 // pass completes, and semantic analysis of it completes.
2206 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
2207 errdefer gen_scope_arena.deinit();
2208 var gen_scope: Scope.GenZIR = .{
2209 .decl = decl,
2210 .arena = &gen_scope_arena.allocator,
2211 .parent = decl.scope,
2212 };
2213 defer gen_scope.instructions.deinit(self.gpa);
2214
2215 // We need an instruction for each parameter, and they must be first in the body.
2216 try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
2217 var params_scope = &gen_scope.base;
2218 for (fn_proto.params()) |param, i| {
2219 const name_token = param.name_token.?;
2220 const src = tree.token_locs[name_token].start;
2221 const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
2222 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
2223 arg.* = .{
2224 .base = .{
2225 .tag = .arg,
2226 .src = src,
2227 },
2228 .positionals = .{
2229 .name = param_name,
2230 },
2231 .kw_args = .{},
2232 };
2233 gen_scope.instructions.items[i] = &arg.base;
2234 const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);
2235 sub_scope.* = .{
2236 .parent = params_scope,
2237 .gen_zir = &gen_scope,
2238 .name = param_name,
2239 .inst = &arg.base,
2240 };
2241 params_scope = &sub_scope.base;
2242 }
2243
2244 const body_block = body_node.cast(ast.Node.Block).?;
2245
2246 try astgen.blockExpr(self, params_scope, body_block);
2247
2248 if (gen_scope.instructions.items.len == 0 or
2249 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
2250 {
2251 const src = tree.token_locs[body_block.rbrace].start;
2252 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
2253 }
2254
2255 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
2256 fn_zir.* = .{
2257 .body = .{
2258 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
2259 },
2260 .arena = gen_scope_arena.state,
2261 };
2262 break :blk fn_zir;
2263 };
2264
2265 new_func.* = .{
2266 .analysis = .{ .queued = fn_zir },
2267 .owner_decl = decl,
2268 };
2269 fn_payload.* = .{ .func = new_func };
2270
2271 var prev_type_has_bits = false;
2272 var type_changed = true;
2273
2274 if (decl.typedValueManaged()) |tvm| {
2275 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
2276 type_changed = !tvm.typed_value.ty.eql(fn_type);
2277
2278 tvm.deinit(self.gpa);
2279 }
2280
2281 decl_arena_state.* = decl_arena.state;
2282 decl.typed_value = .{
2283 .most_recent = .{
2284 .typed_value = .{
2285 .ty = fn_type,
2286 .val = Value.initPayload(&fn_payload.base),
2287 },
2288 .arena = decl_arena_state,
2289 },
2290 };
2291 decl.analysis = .complete;
2292 decl.generation = self.generation;
2293
2294 if (fn_type.hasCodeGenBits()) {
2295 // We don't fully codegen the decl until later, but we do need to reserve a global
2296 // offset table index for it. This allows us to codegen decls out of dependency order,
2297 // increasing how many computations can be done in parallel.
2298 try self.bin_file.allocateDeclIndexes(decl);
2299 try self.work_queue.writeItem(.{ .codegen_decl = decl });
2300 } else if (prev_type_has_bits) {
2301 self.bin_file.freeDecl(decl);
2302 }
2303
2304 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
2305 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
2306 const export_src = tree.token_locs[maybe_export_token].start;
2307 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
2308 const name = tree.tokenSliceLoc(name_loc);
2309 // The scope needs to have the decl in it.
2310 try self.analyzeExport(&block_scope.base, export_src, name, decl);
2311 }
2312 }
2313 return type_changed;
2314 },
2315 .VarDecl => {
2316 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
2317
2318 decl.analysis = .in_progress;
2319
2320 // We need the memory for the Type to go into the arena for the Decl
2321 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
2322 errdefer decl_arena.deinit();
2323 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
2324
2325 var block_scope: Scope.Block = .{
2326 .parent = null,
2327 .func = null,
2328 .decl = decl,
2329 .instructions = .{},
2330 .arena = &decl_arena.allocator,
2331 .is_comptime = true,
2332 };
2333 defer block_scope.instructions.deinit(self.gpa);
2334
2335 decl.is_pub = var_decl.getVisibToken() != null;
2336 const is_extern = blk: {
2337 const maybe_extern_token = var_decl.getExternExportToken() orelse
2338 break :blk false;
2339 if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
2340 if (var_decl.getInitNode()) |some| {
2341 return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
2342 }
2343 break :blk true;
2344 };
2345 if (var_decl.getLibName()) |lib_name| {
2346 assert(is_extern);
2347 return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
2348 }
2349 const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
2350 const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
2351 if (!is_mutable) {
2352 return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
2353 }
2354 break :blk true;
2355 } else false;
2356 assert(var_decl.getComptimeToken() == null);
2357 if (var_decl.getAlignNode()) |align_expr| {
2358 return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
2359 }
2360 if (var_decl.getSectionNode()) |sect_expr| {
2361 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
2362 }
2363
2364 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
2365 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
2366 defer gen_scope_arena.deinit();
2367 var gen_scope: Scope.GenZIR = .{
2368 .decl = decl,
2369 .arena = &gen_scope_arena.allocator,
2370 .parent = decl.scope,
2371 };
2372 defer gen_scope.instructions.deinit(self.gpa);
2373
2374 const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
2375 const src = tree.token_locs[type_node.firstToken()].start;
2376 const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
2377 .ty = Type.initTag(.type),
2378 .val = Value.initTag(.type_type),
2379 });
2380 const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
2381 break :rl .{ .ty = var_type };
2382 } else .none;
2383
2384 const src = tree.token_locs[init_node.firstToken()].start;
2385 const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
2386
2387 var inner_block: Scope.Block = .{
2388 .parent = null,
2389 .func = null,
2390 .decl = decl,
2391 .instructions = .{},
2392 .arena = &gen_scope_arena.allocator,
2393 .is_comptime = true,
2394 };
2395 defer inner_block.instructions.deinit(self.gpa);
2396 try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
2397
2398 // The result location guarantees the type coercion.
2399 const analyzed_init_inst = init_inst.analyzed_inst.?;
2400 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
2401 const val = analyzed_init_inst.value().?;
2402
2403 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
2404 break :vi .{
2405 .ty = ty,
2406 .val = try val.copy(block_scope.arena),
2407 };
2408 } else if (!is_extern) {
2409 return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
2410 } else if (var_decl.getTypeNode()) |type_node| vi: {
2411 // Temporary arena for the zir instructions.
2412 var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
2413 defer type_scope_arena.deinit();
2414 var type_scope: Scope.GenZIR = .{
2415 .decl = decl,
2416 .arena = &type_scope_arena.allocator,
2417 .parent = decl.scope,
2418 };
2419 defer type_scope.instructions.deinit(self.gpa);
2420
2421 const src = tree.token_locs[type_node.firstToken()].start;
2422 const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
2423 .ty = Type.initTag(.type),
2424 .val = Value.initTag(.type_type),
2425 });
2426 const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
2427 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
2428 .instructions = type_scope.instructions.items,
2429 });
2430 break :vi .{
2431 .ty = ty,
2432 .val = null,
2433 };
2434 } else {
2435 return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
2436 };
2437
2438 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
2439 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
2440 }
2441
2442 var type_changed = true;
2443 if (decl.typedValueManaged()) |tvm| {
2444 type_changed = !tvm.typed_value.ty.eql(var_info.ty);
2445
2446 tvm.deinit(self.gpa);
2447 }
2448
2449 const new_variable = try decl_arena.allocator.create(Var);
2450 const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
2451 new_variable.* = .{
2452 .owner_decl = decl,
2453 .init = var_info.val orelse undefined,
2454 .is_extern = is_extern,
2455 .is_mutable = is_mutable,
2456 .is_threadlocal = is_threadlocal,
2457 };
2458 var_payload.* = .{ .variable = new_variable };
2459
2460 decl_arena_state.* = decl_arena.state;
2461 decl.typed_value = .{
2462 .most_recent = .{
2463 .typed_value = .{
2464 .ty = var_info.ty,
2465 .val = Value.initPayload(&var_payload.base),
2466 },
2467 .arena = decl_arena_state,
2468 },
2469 };
2470 decl.analysis = .complete;
2471 decl.generation = self.generation;
2472
2473 if (var_decl.getExternExportToken()) |maybe_export_token| {
2474 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
2475 const export_src = tree.token_locs[maybe_export_token].start;
2476 const name_loc = tree.token_locs[var_decl.name_token];
2477 const name = tree.tokenSliceLoc(name_loc);
2478 // The scope needs to have the decl in it.
2479 try self.analyzeExport(&block_scope.base, export_src, name, decl);
2480 }
2481 }
2482 return type_changed;
2483 },
2484 .Comptime => {
2485 const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
2486
2487 decl.analysis = .in_progress;
2488
2489 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
2490 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
2491 defer analysis_arena.deinit();
2492 var gen_scope: Scope.GenZIR = .{
2493 .decl = decl,
2494 .arena = &analysis_arena.allocator,
2495 .parent = decl.scope,
2496 };
2497 defer gen_scope.instructions.deinit(self.gpa);
2498
2499 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
2500
2501 var block_scope: Scope.Block = .{
2502 .parent = null,
2503 .func = null,
2504 .decl = decl,
2505 .instructions = .{},
2506 .arena = &analysis_arena.allocator,
2507 .is_comptime = true,
2508 };
2509 defer block_scope.instructions.deinit(self.gpa);
2510
2511 _ = try zir_sema.analyzeBody(self, &block_scope.base, .{
2512 .instructions = gen_scope.instructions.items,
2513 });
2514
2515 decl.analysis = .complete;
2516 decl.generation = self.generation;
2517 return true;
2518 },
2519 .Use => @panic("TODO usingnamespace decl"),
2520 else => unreachable,
1281fn failCObjWithOwnedErrorMsg(mod: *Module, c_object: *CObject, err_msg: *ErrorMsg) InnerError {
1282 {
1283 errdefer err_msg.destroy(mod.gpa);
1284 try mod.failed_c_objects.ensureCapacity(mod.gpa, mod.failed_c_objects.items().len + 1);
25211285 }
1286 mod.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
1287 c_object.status = .failure;
1288 return error.AnalysisFail;
25221289}
25231290
2524fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
2525 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
2526 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
2527
2528 depender.dependencies.putAssumeCapacity(dependee, {});
2529 dependee.dependants.putAssumeCapacity(depender, {});
2530}
2531
2532fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
2533 switch (root_scope.status) {
2534 .never_loaded, .unloaded_success => {
2535 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
2536
2537 const source = try root_scope.getSource(self);
2538
2539 var keep_zir_module = false;
2540 const zir_module = try self.gpa.create(zir.Module);
2541 defer if (!keep_zir_module) self.gpa.destroy(zir_module);
2542
2543 zir_module.* = try zir.parse(self.gpa, source);
2544 defer if (!keep_zir_module) zir_module.deinit(self.gpa);
2545
2546 if (zir_module.error_msg) |src_err_msg| {
2547 self.failed_files.putAssumeCapacityNoClobber(
2548 &root_scope.base,
2549 try ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
2550 );
2551 root_scope.status = .unloaded_parse_failure;
2552 return error.AnalysisFail;
2553 }
2554
2555 root_scope.status = .loaded_success;
2556 root_scope.contents = .{ .module = zir_module };
2557 keep_zir_module = true;
2558
2559 return zir_module;
2560 },
2561
2562 .unloaded_parse_failure,
2563 .unloaded_sema_failure,
2564 => return error.AnalysisFail,
1291pub const ErrorMsg = struct {
1292 byte_offset: usize,
1293 msg: []const u8,
25651294
2566 .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
1295 pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
1296 const self = try gpa.create(ErrorMsg);
1297 errdefer gpa.destroy(self);
1298 self.* = try init(gpa, byte_offset, format, args);
1299 return self;
25671300 }
2568}
2569
2570fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
2571 const tracy = trace(@src());
2572 defer tracy.end();
2573
2574 const root_scope = container_scope.file_scope;
2575
2576 switch (root_scope.status) {
2577 .never_loaded, .unloaded_success => {
2578 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
2579
2580 const source = try root_scope.getSource(self);
2581
2582 var keep_tree = false;
2583 const tree = try std.zig.parse(self.gpa, source);
2584 defer if (!keep_tree) tree.deinit();
2585
2586 if (tree.errors.len != 0) {
2587 const parse_err = tree.errors[0];
2588
2589 var msg = std.ArrayList(u8).init(self.gpa);
2590 defer msg.deinit();
2591
2592 try parse_err.render(tree.token_ids, msg.outStream());
2593 const err_msg = try self.gpa.create(ErrorMsg);
2594 err_msg.* = .{
2595 .msg = msg.toOwnedSlice(),
2596 .byte_offset = tree.token_locs[parse_err.loc()].start,
2597 };
2598
2599 self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
2600 root_scope.status = .unloaded_parse_failure;
2601 return error.AnalysisFail;
2602 }
2603
2604 root_scope.status = .loaded_success;
2605 root_scope.contents = .{ .tree = tree };
2606 keep_tree = true;
26071301
2608 return tree;
2609 },
2610
2611 .unloaded_parse_failure => return error.AnalysisFail,
2612
2613 .loaded_success => return root_scope.contents.tree,
2614 }
2615}
2616
2617fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
2618 const tracy = trace(@src());
2619 defer tracy.end();
2620
2621 // We may be analyzing it for the first time, or this may be
2622 // an incremental update. This code handles both cases.
2623 const tree = try self.getAstTree(container_scope);
2624 const decls = tree.root_node.decls();
2625
2626 try self.work_queue.ensureUnusedCapacity(decls.len);
2627 try container_scope.decls.ensureCapacity(self.gpa, decls.len);
2628
2629 // Keep track of the decls that we expect to see in this file so that
2630 // we know which ones have been deleted.
2631 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
2632 defer deleted_decls.deinit();
2633 try deleted_decls.ensureCapacity(container_scope.decls.items().len);
2634 for (container_scope.decls.items()) |entry| {
2635 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
2636 }
2637
2638 for (decls) |src_decl, decl_i| {
2639 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
2640 // We will create a Decl for it regardless of analysis status.
2641 const name_tok = fn_proto.getNameToken() orelse {
2642 @panic("TODO missing function name");
2643 };
2644
2645 const name_loc = tree.token_locs[name_tok];
2646 const name = tree.tokenSliceLoc(name_loc);
2647 const name_hash = container_scope.fullyQualifiedNameHash(name);
2648 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
2649 if (self.decl_table.get(name_hash)) |decl| {
2650 // Update the AST Node index of the decl, even if its contents are unchanged, it may
2651 // have been re-ordered.
2652 decl.src_index = decl_i;
2653 if (deleted_decls.remove(decl) == null) {
2654 decl.analysis = .sema_failure;
2655 const err_msg = try ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
2656 errdefer err_msg.destroy(self.gpa);
2657 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
2658 } else {
2659 if (!srcHashEql(decl.contents_hash, contents_hash)) {
2660 try self.markOutdatedDecl(decl);
2661 decl.contents_hash = contents_hash;
2662 } else switch (self.bin_file.tag) {
2663 .coff => {
2664 // TODO Implement for COFF
2665 },
2666 .elf => if (decl.fn_link.elf.len != 0) {
2667 // TODO Look into detecting when this would be unnecessary by storing enough state
2668 // in `Decl` to notice that the line number did not change.
2669 self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
2670 },
2671 .macho => {
2672 // TODO Implement for MachO
2673 },
2674 .c, .wasm => {},
2675 }
2676 }
2677 } else {
2678 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
2679 container_scope.decls.putAssumeCapacity(new_decl, {});
2680 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
2681 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
2682 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
2683 }
2684 }
2685 }
2686 } else if (src_decl.castTag(.VarDecl)) |var_decl| {
2687 const name_loc = tree.token_locs[var_decl.name_token];
2688 const name = tree.tokenSliceLoc(name_loc);
2689 const name_hash = container_scope.fullyQualifiedNameHash(name);
2690 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
2691 if (self.decl_table.get(name_hash)) |decl| {
2692 // Update the AST Node index of the decl, even if its contents are unchanged, it may
2693 // have been re-ordered.
2694 decl.src_index = decl_i;
2695 if (deleted_decls.remove(decl) == null) {
2696 decl.analysis = .sema_failure;
2697 const err_msg = try ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name});
2698 errdefer err_msg.destroy(self.gpa);
2699 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
2700 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
2701 try self.markOutdatedDecl(decl);
2702 decl.contents_hash = contents_hash;
2703 }
2704 } else {
2705 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
2706 container_scope.decls.putAssumeCapacity(new_decl, {});
2707 if (var_decl.getExternExportToken()) |maybe_export_token| {
2708 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
2709 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
2710 }
2711 }
2712 }
2713 } else if (src_decl.castTag(.Comptime)) |comptime_node| {
2714 const name_index = self.getNextAnonNameIndex();
2715 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
2716 defer self.gpa.free(name);
2717
2718 const name_hash = container_scope.fullyQualifiedNameHash(name);
2719 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
2720
2721 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
2722 container_scope.decls.putAssumeCapacity(new_decl, {});
2723 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
2724 } else if (src_decl.castTag(.ContainerField)) |container_field| {
2725 log.err("TODO: analyze container field", .{});
2726 } else if (src_decl.castTag(.TestDecl)) |test_decl| {
2727 log.err("TODO: analyze test decl", .{});
2728 } else if (src_decl.castTag(.Use)) |use_decl| {
2729 log.err("TODO: analyze usingnamespace decl", .{});
2730 } else {
2731 unreachable;
2732 }
2733 }
2734 // Handle explicitly deleted decls from the source code. Not to be confused
2735 // with when we delete decls because they are no longer referenced.
2736 for (deleted_decls.items()) |entry| {
2737 log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
2738 try self.deleteDecl(entry.key);
2739 }
2740}
2741
2742fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
2743 // We may be analyzing it for the first time, or this may be
2744 // an incremental update. This code handles both cases.
2745 const src_module = try self.getSrcModule(root_scope);
2746
2747 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
2748 try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
2749
2750 var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
2751 defer exports_to_resolve.deinit();
2752
2753 // Keep track of the decls that we expect to see in this file so that
2754 // we know which ones have been deleted.
2755 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
2756 defer deleted_decls.deinit();
2757 try deleted_decls.ensureCapacity(self.decl_table.items().len);
2758 for (self.decl_table.items()) |entry| {
2759 deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
2760 }
2761
2762 for (src_module.decls) |src_decl, decl_i| {
2763 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
2764 if (self.decl_table.get(name_hash)) |decl| {
2765 deleted_decls.removeAssertDiscard(decl);
2766 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
2767 try self.markOutdatedDecl(decl);
2768 decl.contents_hash = src_decl.contents_hash;
2769 }
2770 } else {
2771 const new_decl = try self.createNewDecl(
2772 &root_scope.base,
2773 src_decl.name,
2774 decl_i,
2775 name_hash,
2776 src_decl.contents_hash,
2777 );
2778 root_scope.decls.appendAssumeCapacity(new_decl);
2779 if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
2780 try exports_to_resolve.append(src_decl);
2781 }
2782 }
2783 }
2784 for (exports_to_resolve.items) |export_decl| {
2785 _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
2786 }
2787 // Handle explicitly deleted decls from the source code. Not to be confused
2788 // with when we delete decls because they are no longer referenced.
2789 for (deleted_decls.items()) |entry| {
2790 log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
2791 try self.deleteDecl(entry.key);
2792 }
2793}
2794
2795fn deleteDecl(self: *Module, decl: *Decl) !void {
2796 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
2797
2798 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
2799 // not be present in the set, and this does nothing.
2800 decl.scope.removeDecl(decl);
2801
2802 log.debug("deleting decl '{}'\n", .{decl.name});
2803 const name_hash = decl.fullyQualifiedNameHash();
2804 self.decl_table.removeAssertDiscard(name_hash);
2805 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
2806 for (decl.dependencies.items()) |entry| {
2807 const dep = entry.key;
2808 dep.removeDependant(decl);
2809 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
2810 // We don't recursively perform a deletion here, because during the update,
2811 // another reference to it may turn up.
2812 dep.deletion_flag = true;
2813 self.deletion_set.appendAssumeCapacity(dep);
2814 }
2815 }
2816 // Anything that depends on this deleted decl certainly needs to be re-analyzed.
2817 for (decl.dependants.items()) |entry| {
2818 const dep = entry.key;
2819 dep.removeDependency(decl);
2820 if (dep.analysis != .outdated) {
2821 // TODO Move this failure possibility to the top of the function.
2822 try self.markOutdatedDecl(dep);
2823 }
2824 }
2825 if (self.failed_decls.remove(decl)) |entry| {
2826 entry.value.destroy(self.gpa);
2827 }
2828 self.deleteDeclExports(decl);
2829 self.bin_file.freeDecl(decl);
2830 decl.destroy(self.gpa);
2831}
2832
2833/// Delete all the Export objects that are caused by this Decl. Re-analysis of
2834/// this Decl will cause them to be re-created (or not).
2835fn deleteDeclExports(self: *Module, decl: *Decl) void {
2836 const kv = self.export_owners.remove(decl) orelse return;
2837
2838 for (kv.value) |exp| {
2839 if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
2840 // Remove exports with owner_decl matching the regenerating decl.
2841 const list = decl_exports_kv.value;
2842 var i: usize = 0;
2843 var new_len = list.len;
2844 while (i < new_len) {
2845 if (list[i].owner_decl == decl) {
2846 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
2847 new_len -= 1;
2848 } else {
2849 i += 1;
2850 }
2851 }
2852 decl_exports_kv.value = self.gpa.shrink(list, new_len);
2853 if (new_len == 0) {
2854 self.decl_exports.removeAssertDiscard(exp.exported_decl);
2855 }
2856 }
2857 if (self.bin_file.cast(link.File.Elf)) |elf| {
2858 elf.deleteExport(exp.link);
2859 }
2860 if (self.failed_exports.remove(exp)) |entry| {
2861 entry.value.destroy(self.gpa);
2862 }
2863 _ = self.symbol_exports.remove(exp.options.name);
2864 self.gpa.free(exp.options.name);
2865 self.gpa.destroy(exp);
2866 }
2867 self.gpa.free(kv.value);
2868}
2869
2870fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
2871 const tracy = trace(@src());
2872 defer tracy.end();
2873
2874 // Use the Decl's arena for function memory.
2875 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
2876 defer decl.typed_value.most_recent.arena.?.* = arena.state;
2877 var inner_block: Scope.Block = .{
2878 .parent = null,
2879 .func = func,
2880 .decl = decl,
2881 .instructions = .{},
2882 .arena = &arena.allocator,
2883 .is_comptime = false,
2884 };
2885 defer inner_block.instructions.deinit(self.gpa);
2886
2887 const fn_zir = func.analysis.queued;
2888 defer fn_zir.arena.promote(self.gpa).deinit();
2889 func.analysis = .{ .in_progress = {} };
2890 log.debug("set {} to in_progress\n", .{decl.name});
2891
2892 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
2893
2894 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
2895 func.analysis = .{ .success = .{ .instructions = instructions } };
2896 log.debug("set {} to success\n", .{decl.name});
2897}
2898
2899fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
2900 log.debug("mark {} outdated\n", .{decl.name});
2901 try self.work_queue.writeItem(.{ .analyze_decl = decl });
2902 if (self.failed_decls.remove(decl)) |entry| {
2903 entry.value.destroy(self.gpa);
2904 }
2905 decl.analysis = .outdated;
2906}
2907
2908fn allocateNewDecl(
2909 self: *Module,
2910 scope: *Scope,
2911 src_index: usize,
2912 contents_hash: std.zig.SrcHash,
2913) !*Decl {
2914 const new_decl = try self.gpa.create(Decl);
2915 new_decl.* = .{
2916 .name = "",
2917 .scope = scope.namespace(),
2918 .src_index = src_index,
2919 .typed_value = .{ .never_succeeded = {} },
2920 .analysis = .unreferenced,
2921 .deletion_flag = false,
2922 .contents_hash = contents_hash,
2923 .link = switch (self.bin_file.tag) {
2924 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
2925 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
2926 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
2927 .c => .{ .c = {} },
2928 .wasm => .{ .wasm = {} },
2929 },
2930 .fn_link = switch (self.bin_file.tag) {
2931 .coff => .{ .coff = {} },
2932 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
2933 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
2934 .c => .{ .c = {} },
2935 .wasm => .{ .wasm = null },
2936 },
2937 .generation = 0,
2938 .is_pub = false,
2939 };
2940 return new_decl;
2941}
2942
2943fn createNewDecl(
2944 self: *Module,
2945 scope: *Scope,
2946 decl_name: []const u8,
2947 src_index: usize,
2948 name_hash: Scope.NameHash,
2949 contents_hash: std.zig.SrcHash,
2950) !*Decl {
2951 try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
2952 const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
2953 errdefer self.gpa.destroy(new_decl);
2954 new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
2955 self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
2956 return new_decl;
2957}
2958
2959/// Get error value for error tag `name`.
2960pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
2961 const gop = try self.global_error_set.getOrPut(self.gpa, name);
2962 if (gop.found_existing)
2963 return gop.entry.*;
2964 errdefer self.global_error_set.removeAssertDiscard(name);
2965
2966 gop.entry.key = try self.gpa.dupe(u8, name);
2967 gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
2968 return gop.entry.*;
2969}
2970
2971pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2972 return scope.cast(Scope.Block) orelse
2973 return self.fail(scope, src, "instruction illegal outside function body", .{});
2974}
2975
2976pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2977 const block = try self.requireFunctionBlock(scope, src);
2978 if (block.is_comptime) {
2979 return self.fail(scope, src, "unable to resolve comptime value", .{});
2980 }
2981 return block;
2982}
2983
2984pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
2985 return (try self.resolveDefinedValue(scope, base)) orelse
2986 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
2987}
2988
2989pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
2990 if (base.value()) |val| {
2991 if (val.isUndef()) {
2992 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
2993 }
2994 return val;
2995 }
2996 return null;
2997}
2998
2999pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
3000 try self.ensureDeclAnalyzed(exported_decl);
3001 const typed_value = exported_decl.typed_value.most_recent.typed_value;
3002 switch (typed_value.ty.zigTypeTag()) {
3003 .Fn => {},
3004 else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
3005 }
3006
3007 try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
3008 try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
3009
3010 const new_export = try self.gpa.create(Export);
3011 errdefer self.gpa.destroy(new_export);
3012
3013 const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
3014 errdefer self.gpa.free(symbol_name);
3015
3016 const owner_decl = scope.decl().?;
3017
3018 new_export.* = .{
3019 .options = .{ .name = symbol_name },
3020 .src = src,
3021 .link = .{},
3022 .owner_decl = owner_decl,
3023 .exported_decl = exported_decl,
3024 .status = .in_progress,
3025 };
3026
3027 // Add to export_owners table.
3028 const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
3029 if (!eo_gop.found_existing) {
3030 eo_gop.entry.value = &[0]*Export{};
3031 }
3032 eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
3033 eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
3034 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
3035
3036 // Add to exported_decl table.
3037 const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
3038 if (!de_gop.found_existing) {
3039 de_gop.entry.value = &[0]*Export{};
3040 }
3041 de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
3042 de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
3043 errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
3044
3045 if (self.symbol_exports.get(symbol_name)) |_| {
3046 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
3047 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
3048 self.gpa,
3049 src,
3050 "exported symbol collision: {}",
3051 .{symbol_name},
3052 ));
3053 // TODO: add a note
3054 new_export.status = .failed;
3055 return;
3056 }
3057
3058 try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
3059 self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
3060 error.OutOfMemory => return error.OutOfMemory,
3061 else => {
3062 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
3063 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
3064 self.gpa,
3065 src,
3066 "unable to export: {}",
3067 .{@errorName(err)},
3068 ));
3069 new_export.status = .failed_retryable;
3070 },
3071 };
3072}
3073
3074pub fn addNoOp(
3075 self: *Module,
3076 block: *Scope.Block,
3077 src: usize,
3078 ty: Type,
3079 comptime tag: Inst.Tag,
3080) !*Inst {
3081 const inst = try block.arena.create(tag.Type());
3082 inst.* = .{
3083 .base = .{
3084 .tag = tag,
3085 .ty = ty,
3086 .src = src,
3087 },
3088 };
3089 try block.instructions.append(self.gpa, &inst.base);
3090 return &inst.base;
3091}
3092
3093pub fn addUnOp(
3094 self: *Module,
3095 block: *Scope.Block,
3096 src: usize,
3097 ty: Type,
3098 tag: Inst.Tag,
3099 operand: *Inst,
3100) !*Inst {
3101 const inst = try block.arena.create(Inst.UnOp);
3102 inst.* = .{
3103 .base = .{
3104 .tag = tag,
3105 .ty = ty,
3106 .src = src,
3107 },
3108 .operand = operand,
3109 };
3110 try block.instructions.append(self.gpa, &inst.base);
3111 return &inst.base;
3112}
3113
3114pub fn addBinOp(
3115 self: *Module,
3116 block: *Scope.Block,
3117 src: usize,
3118 ty: Type,
3119 tag: Inst.Tag,
3120 lhs: *Inst,
3121 rhs: *Inst,
3122) !*Inst {
3123 const inst = try block.arena.create(Inst.BinOp);
3124 inst.* = .{
3125 .base = .{
3126 .tag = tag,
3127 .ty = ty,
3128 .src = src,
3129 },
3130 .lhs = lhs,
3131 .rhs = rhs,
3132 };
3133 try block.instructions.append(self.gpa, &inst.base);
3134 return &inst.base;
3135}
3136
3137pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
3138 const inst = try block.arena.create(Inst.Arg);
3139 inst.* = .{
3140 .base = .{
3141 .tag = .arg,
3142 .ty = ty,
3143 .src = src,
3144 },
3145 .name = name,
3146 };
3147 try block.instructions.append(self.gpa, &inst.base);
3148 return &inst.base;
3149}
3150
3151pub fn addBr(
3152 self: *Module,
3153 scope_block: *Scope.Block,
3154 src: usize,
3155 target_block: *Inst.Block,
3156 operand: *Inst,
3157) !*Inst {
3158 const inst = try scope_block.arena.create(Inst.Br);
3159 inst.* = .{
3160 .base = .{
3161 .tag = .br,
3162 .ty = Type.initTag(.noreturn),
3163 .src = src,
3164 },
3165 .operand = operand,
3166 .block = target_block,
3167 };
3168 try scope_block.instructions.append(self.gpa, &inst.base);
3169 return &inst.base;
3170}
3171
3172pub fn addCondBr(
3173 self: *Module,
3174 block: *Scope.Block,
3175 src: usize,
3176 condition: *Inst,
3177 then_body: ir.Body,
3178 else_body: ir.Body,
3179) !*Inst {
3180 const inst = try block.arena.create(Inst.CondBr);
3181 inst.* = .{
3182 .base = .{
3183 .tag = .condbr,
3184 .ty = Type.initTag(.noreturn),
3185 .src = src,
3186 },
3187 .condition = condition,
3188 .then_body = then_body,
3189 .else_body = else_body,
3190 };
3191 try block.instructions.append(self.gpa, &inst.base);
3192 return &inst.base;
3193}
3194
3195pub fn addCall(
3196 self: *Module,
3197 block: *Scope.Block,
3198 src: usize,
3199 ty: Type,
3200 func: *Inst,
3201 args: []const *Inst,
3202) !*Inst {
3203 const inst = try block.arena.create(Inst.Call);
3204 inst.* = .{
3205 .base = .{
3206 .tag = .call,
3207 .ty = ty,
3208 .src = src,
3209 },
3210 .func = func,
3211 .args = args,
3212 };
3213 try block.instructions.append(self.gpa, &inst.base);
3214 return &inst.base;
3215}
3216
3217pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
3218 const const_inst = try scope.arena().create(Inst.Constant);
3219 const_inst.* = .{
3220 .base = .{
3221 .tag = Inst.Constant.base_tag,
3222 .ty = typed_value.ty,
3223 .src = src,
3224 },
3225 .val = typed_value.val,
3226 };
3227 return &const_inst.base;
3228}
3229
3230pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
3231 return self.constInst(scope, src, .{
3232 .ty = Type.initTag(.type),
3233 .val = try ty.toValue(scope.arena()),
3234 });
3235}
3236
3237pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
3238 return self.constInst(scope, src, .{
3239 .ty = Type.initTag(.void),
3240 .val = Value.initTag(.void_value),
3241 });
3242}
3243
3244pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
3245 return self.constInst(scope, src, .{
3246 .ty = Type.initTag(.noreturn),
3247 .val = Value.initTag(.unreachable_value),
3248 });
3249}
3250
3251pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
3252 return self.constInst(scope, src, .{
3253 .ty = ty,
3254 .val = Value.initTag(.undef),
3255 });
3256}
3257
3258pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
3259 return self.constInst(scope, src, .{
3260 .ty = Type.initTag(.bool),
3261 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
3262 });
3263}
3264
3265pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
3266 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
3267 int_payload.* = .{ .int = int };
3268
3269 return self.constInst(scope, src, .{
3270 .ty = ty,
3271 .val = Value.initPayload(&int_payload.base),
3272 });
3273}
3274
3275pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
3276 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
3277 int_payload.* = .{ .int = int };
3278
3279 return self.constInst(scope, src, .{
3280 .ty = ty,
3281 .val = Value.initPayload(&int_payload.base),
3282 });
3283}
3284
3285pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
3286 const val_payload = if (big_int.positive) blk: {
3287 if (big_int.to(u64)) |x| {
3288 return self.constIntUnsigned(scope, src, ty, x);
3289 } else |err| switch (err) {
3290 error.NegativeIntoUnsigned => unreachable,
3291 error.TargetTooSmall => {}, // handled below
3292 }
3293 const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
3294 big_int_payload.* = .{ .limbs = big_int.limbs };
3295 break :blk &big_int_payload.base;
3296 } else blk: {
3297 if (big_int.to(i64)) |x| {
3298 return self.constIntSigned(scope, src, ty, x);
3299 } else |err| switch (err) {
3300 error.NegativeIntoUnsigned => unreachable,
3301 error.TargetTooSmall => {}, // handled below
3302 }
3303 const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
3304 big_int_payload.* = .{ .limbs = big_int.limbs };
3305 break :blk &big_int_payload.base;
3306 };
3307
3308 return self.constInst(scope, src, .{
3309 .ty = ty,
3310 .val = Value.initPayload(val_payload),
3311 });
3312}
3313
3314pub fn createAnonymousDecl(
3315 self: *Module,
3316 scope: *Scope,
3317 decl_arena: *std.heap.ArenaAllocator,
3318 typed_value: TypedValue,
3319) !*Decl {
3320 const name_index = self.getNextAnonNameIndex();
3321 const scope_decl = scope.decl().?;
3322 const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
3323 defer self.gpa.free(name);
3324 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
3325 const src_hash: std.zig.SrcHash = undefined;
3326 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
3327 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
3328
3329 decl_arena_state.* = decl_arena.state;
3330 new_decl.typed_value = .{
3331 .most_recent = .{
3332 .typed_value = typed_value,
3333 .arena = decl_arena_state,
3334 },
3335 };
3336 new_decl.analysis = .complete;
3337 new_decl.generation = self.generation;
3338
3339 // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
3340 // We should be able to further improve the compiler to not omit Decls which are only referenced at
3341 // compile-time and not runtime.
3342 if (typed_value.ty.hasCodeGenBits()) {
3343 try self.bin_file.allocateDeclIndexes(new_decl);
3344 try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
3345 }
3346
3347 return new_decl;
3348}
3349
3350fn getNextAnonNameIndex(self: *Module) usize {
3351 return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
3352}
3353
3354pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
3355 const namespace = scope.namespace();
3356 const name_hash = namespace.fullyQualifiedNameHash(ident_name);
3357 return self.decl_table.get(name_hash);
3358}
3359
3360pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
3361 const scope_decl = scope.decl().?;
3362 try self.declareDeclDependency(scope_decl, decl);
3363 self.ensureDeclAnalyzed(decl) catch |err| {
3364 if (scope.cast(Scope.Block)) |block| {
3365 if (block.func) |func| {
3366 func.analysis = .dependency_failure;
3367 } else {
3368 block.decl.analysis = .dependency_failure;
3369 }
3370 } else {
3371 scope_decl.analysis = .dependency_failure;
3372 }
3373 return err;
3374 };
3375
3376 const decl_tv = try decl.typedValue();
3377 if (decl_tv.val.tag() == .variable) {
3378 return self.analyzeVarRef(scope, src, decl_tv);
3379 }
3380 const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One);
3381 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
3382 val_payload.* = .{ .decl = decl };
3383
3384 return self.constInst(scope, src, .{
3385 .ty = ty,
3386 .val = Value.initPayload(&val_payload.base),
3387 });
3388}
3389
3390fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
3391 const variable = tv.val.cast(Value.Payload.Variable).?.variable;
3392
3393 const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
3394 if (!variable.is_mutable and !variable.is_extern) {
3395 const val_payload = try scope.arena().create(Value.Payload.RefVal);
3396 val_payload.* = .{ .val = variable.init };
3397 return self.constInst(scope, src, .{
3398 .ty = ty,
3399 .val = Value.initPayload(&val_payload.base),
3400 });
3401 }
3402
3403 const b = try self.requireRuntimeBlock(scope, src);
3404 const inst = try b.arena.create(Inst.VarPtr);
3405 inst.* = .{
3406 .base = .{
3407 .tag = .varptr,
3408 .ty = ty,
3409 .src = src,
3410 },
3411 .variable = variable,
3412 };
3413 try b.instructions.append(self.gpa, &inst.base);
3414 return &inst.base;
3415}
3416
3417pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
3418 const elem_ty = switch (ptr.ty.zigTypeTag()) {
3419 .Pointer => ptr.ty.elemType(),
3420 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
3421 };
3422 if (ptr.value()) |val| {
3423 return self.constInst(scope, src, .{
3424 .ty = elem_ty,
3425 .val = try val.pointerDeref(scope.arena()),
3426 });
3427 }
3428
3429 const b = try self.requireRuntimeBlock(scope, src);
3430 return self.addUnOp(b, src, elem_ty, .load, ptr);
3431}
3432
3433pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
3434 const decl = self.lookupDeclName(scope, decl_name) orelse
3435 return self.fail(scope, src, "decl '{}' not found", .{decl_name});
3436 return self.analyzeDeclRef(scope, src, decl);
3437}
3438
3439pub fn wantSafety(self: *Module, scope: *Scope) bool {
3440 // TODO take into account scope's safety overrides
3441 return switch (self.optimizeMode()) {
3442 .Debug => true,
3443 .ReleaseSafe => true,
3444 .ReleaseFast => false,
3445 .ReleaseSmall => false,
3446 };
3447}
3448
3449pub fn analyzeIsNull(
3450 self: *Module,
3451 scope: *Scope,
3452 src: usize,
3453 operand: *Inst,
3454 invert_logic: bool,
3455) InnerError!*Inst {
3456 if (operand.value()) |opt_val| {
3457 const is_null = opt_val.isNull();
3458 const bool_value = if (invert_logic) !is_null else is_null;
3459 return self.constBool(scope, src, bool_value);
3460 }
3461 const b = try self.requireRuntimeBlock(scope, src);
3462 const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull;
3463 return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
3464}
3465
3466pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
3467 return self.fail(scope, src, "TODO implement analysis of iserr", .{});
3468}
3469
3470pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
3471 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
3472 .Pointer => array_ptr.ty.elemType(),
3473 else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
3474 };
3475
3476 var array_type = ptr_child;
3477 const elem_type = switch (ptr_child.zigTypeTag()) {
3478 .Array => ptr_child.elemType(),
3479 .Pointer => blk: {
3480 if (ptr_child.isSinglePointer()) {
3481 if (ptr_child.elemType().zigTypeTag() == .Array) {
3482 array_type = ptr_child.elemType();
3483 break :blk ptr_child.elemType().elemType();
3484 }
3485
3486 return self.fail(scope, src, "slice of single-item pointer", .{});
3487 }
3488 break :blk ptr_child.elemType();
3489 },
3490 else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
3491 };
3492
3493 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
3494 const casted = try self.coerce(scope, elem_type, sentinel);
3495 break :blk try self.resolveConstValue(scope, casted);
3496 } else null;
3497
3498 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
3499 var return_elem_type = elem_type;
3500 if (end_opt) |end| {
3501 if (end.value()) |end_val| {
3502 if (start.value()) |start_val| {
3503 const start_u64 = start_val.toUnsignedInt();
3504 const end_u64 = end_val.toUnsignedInt();
3505 if (start_u64 > end_u64) {
3506 return self.fail(scope, src, "out of bounds slice", .{});
3507 }
3508
3509 const len = end_u64 - start_u64;
3510 const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
3511 array_type.sentinel()
3512 else
3513 slice_sentinel;
3514 return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
3515 return_ptr_size = .One;
3516 }
3517 }
3518 }
3519 const return_type = try self.ptrType(
3520 scope,
3521 src,
3522 return_elem_type,
3523 if (end_opt == null) slice_sentinel else null,
3524 0, // TODO alignment
3525 0,
3526 0,
3527 !ptr_child.isConstPtr(),
3528 ptr_child.isAllowzeroPtr(),
3529 ptr_child.isVolatilePtr(),
3530 return_ptr_size,
3531 );
3532
3533 return self.fail(scope, src, "TODO implement analysis of slice", .{});
3534}
3535
3536/// Asserts that lhs and rhs types are both numeric.
3537pub fn cmpNumeric(
3538 self: *Module,
3539 scope: *Scope,
3540 src: usize,
3541 lhs: *Inst,
3542 rhs: *Inst,
3543 op: std.math.CompareOperator,
3544) !*Inst {
3545 assert(lhs.ty.isNumeric());
3546 assert(rhs.ty.isNumeric());
3547
3548 const lhs_ty_tag = lhs.ty.zigTypeTag();
3549 const rhs_ty_tag = rhs.ty.zigTypeTag();
3550
3551 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
3552 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3553 return self.fail(scope, src, "vector length mismatch: {} and {}", .{
3554 lhs.ty.arrayLen(),
3555 rhs.ty.arrayLen(),
3556 });
3557 }
3558 return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
3559 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
3560 return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
3561 lhs.ty,
3562 rhs.ty,
3563 });
3564 }
3565
3566 if (lhs.value()) |lhs_val| {
3567 if (rhs.value()) |rhs_val| {
3568 return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
3569 }
3570 }
3571
3572 // TODO handle comparisons against lazy zero values
3573 // Some values can be compared against zero without being runtime known or without forcing
3574 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
3575 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
3576 // of this function if we don't need to.
3577
3578 // It must be a runtime comparison.
3579 const b = try self.requireRuntimeBlock(scope, src);
3580 // For floats, emit a float comparison instruction.
3581 const lhs_is_float = switch (lhs_ty_tag) {
3582 .Float, .ComptimeFloat => true,
3583 else => false,
3584 };
3585 const rhs_is_float = switch (rhs_ty_tag) {
3586 .Float, .ComptimeFloat => true,
3587 else => false,
3588 };
3589 if (lhs_is_float and rhs_is_float) {
3590 // Implicit cast the smaller one to the larger one.
3591 const dest_type = x: {
3592 if (lhs_ty_tag == .ComptimeFloat) {
3593 break :x rhs.ty;
3594 } else if (rhs_ty_tag == .ComptimeFloat) {
3595 break :x lhs.ty;
3596 }
3597 if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
3598 break :x lhs.ty;
3599 } else {
3600 break :x rhs.ty;
3601 }
3602 };
3603 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3604 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3605 return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3606 }
3607 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
3608 // For mixed signed and unsigned integers, implicit cast both operands to a signed
3609 // integer with + 1 bit.
3610 // For mixed floats and integers, extract the integer part from the float, cast that to
3611 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
3612 // add/subtract 1.
3613 const lhs_is_signed = if (lhs.value()) |lhs_val|
3614 lhs_val.compareWithZero(.lt)
3615 else
3616 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
3617 const rhs_is_signed = if (rhs.value()) |rhs_val|
3618 rhs_val.compareWithZero(.lt)
3619 else
3620 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
3621 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
3622
3623 var dest_float_type: ?Type = null;
3624
3625 var lhs_bits: usize = undefined;
3626 if (lhs.value()) |lhs_val| {
3627 if (lhs_val.isUndef())
3628 return self.constUndef(scope, src, Type.initTag(.bool));
3629 const is_unsigned = if (lhs_is_float) x: {
3630 var bigint_space: Value.BigIntSpace = undefined;
3631 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
3632 defer bigint.deinit();
3633 const zcmp = lhs_val.orderAgainstZero();
3634 if (lhs_val.floatHasFraction()) {
3635 switch (op) {
3636 .eq => return self.constBool(scope, src, false),
3637 .neq => return self.constBool(scope, src, true),
3638 else => {},
3639 }
3640 if (zcmp == .lt) {
3641 try bigint.addScalar(bigint.toConst(), -1);
3642 } else {
3643 try bigint.addScalar(bigint.toConst(), 1);
3644 }
3645 }
3646 lhs_bits = bigint.toConst().bitCountTwosComp();
3647 break :x (zcmp != .lt);
3648 } else x: {
3649 lhs_bits = lhs_val.intBitCountTwosComp();
3650 break :x (lhs_val.orderAgainstZero() != .lt);
3651 };
3652 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
3653 } else if (lhs_is_float) {
3654 dest_float_type = lhs.ty;
3655 } else {
3656 const int_info = lhs.ty.intInfo(self.getTarget());
3657 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
3658 }
3659
3660 var rhs_bits: usize = undefined;
3661 if (rhs.value()) |rhs_val| {
3662 if (rhs_val.isUndef())
3663 return self.constUndef(scope, src, Type.initTag(.bool));
3664 const is_unsigned = if (rhs_is_float) x: {
3665 var bigint_space: Value.BigIntSpace = undefined;
3666 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
3667 defer bigint.deinit();
3668 const zcmp = rhs_val.orderAgainstZero();
3669 if (rhs_val.floatHasFraction()) {
3670 switch (op) {
3671 .eq => return self.constBool(scope, src, false),
3672 .neq => return self.constBool(scope, src, true),
3673 else => {},
3674 }
3675 if (zcmp == .lt) {
3676 try bigint.addScalar(bigint.toConst(), -1);
3677 } else {
3678 try bigint.addScalar(bigint.toConst(), 1);
3679 }
3680 }
3681 rhs_bits = bigint.toConst().bitCountTwosComp();
3682 break :x (zcmp != .lt);
3683 } else x: {
3684 rhs_bits = rhs_val.intBitCountTwosComp();
3685 break :x (rhs_val.orderAgainstZero() != .lt);
3686 };
3687 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
3688 } else if (rhs_is_float) {
3689 dest_float_type = rhs.ty;
3690 } else {
3691 const int_info = rhs.ty.intInfo(self.getTarget());
3692 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
3693 }
3694
3695 const dest_type = if (dest_float_type) |ft| ft else blk: {
3696 const max_bits = std.math.max(lhs_bits, rhs_bits);
3697 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
3698 error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
3699 };
3700 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
3701 };
3702 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3703 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3704
3705 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3706}
3707
3708fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3709 if (inst.value()) |val| {
3710 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3711 }
3712
3713 const b = try self.requireRuntimeBlock(scope, inst.src);
3714 return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
3715}
3716
3717fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
3718 if (signed) {
3719 const int_payload = try scope.arena().create(Type.Payload.IntSigned);
3720 int_payload.* = .{ .bits = bits };
3721 return Type.initPayload(&int_payload.base);
3722 } else {
3723 const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
3724 int_payload.* = .{ .bits = bits };
3725 return Type.initPayload(&int_payload.base);
3726 }
3727}
3728
3729pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
3730 if (instructions.len == 0)
3731 return Type.initTag(.noreturn);
3732
3733 if (instructions.len == 1)
3734 return instructions[0].ty;
3735
3736 var prev_inst = instructions[0];
3737 for (instructions[1..]) |next_inst| {
3738 if (next_inst.ty.eql(prev_inst.ty))
3739 continue;
3740 if (next_inst.ty.zigTypeTag() == .NoReturn)
3741 continue;
3742 if (prev_inst.ty.zigTypeTag() == .NoReturn) {
3743 prev_inst = next_inst;
3744 continue;
3745 }
3746 if (next_inst.ty.zigTypeTag() == .Undefined)
3747 continue;
3748 if (prev_inst.ty.zigTypeTag() == .Undefined) {
3749 prev_inst = next_inst;
3750 continue;
3751 }
3752 if (prev_inst.ty.isInt() and
3753 next_inst.ty.isInt() and
3754 prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
3755 {
3756 if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {
3757 prev_inst = next_inst;
3758 }
3759 continue;
3760 }
3761 if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
3762 if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
3763 prev_inst = next_inst;
3764 }
3765 continue;
3766 }
3767
3768 // TODO error notes pointing out each type
3769 return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
3770 }
3771
3772 return prev_inst.ty;
3773}
3774
3775pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3776 // If the types are the same, we can return the operand.
3777 if (dest_type.eql(inst.ty))
3778 return inst;
3779
3780 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
3781 if (in_memory_result == .ok) {
3782 return self.bitcast(scope, dest_type, inst);
3783 }
3784
3785 // undefined to anything
3786 if (inst.value()) |val| {
3787 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
3788 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3789 }
3790 }
3791 assert(inst.ty.zigTypeTag() != .Undefined);
3792
3793 // null to ?T
3794 if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
3795 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
3796 }
3797
3798 // T to ?T
3799 if (dest_type.zigTypeTag() == .Optional) {
3800 var buf: Type.Payload.PointerSimple = undefined;
3801 const child_type = dest_type.optionalChild(&buf);
3802 if (child_type.eql(inst.ty)) {
3803 return self.wrapOptional(scope, dest_type, inst);
3804 } else if (try self.coerceNum(scope, child_type, inst)) |some| {
3805 return self.wrapOptional(scope, dest_type, some);
3806 }
3807 }
3808
3809 // *[N]T to []T
3810 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
3811 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
3812 {
3813 const array_type = inst.ty.elemType();
3814 const dst_elem_type = dest_type.elemType();
3815 if (array_type.zigTypeTag() == .Array and
3816 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
3817 {
3818 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
3819 }
3820 }
3821
3822 // comptime known number to other number
3823 if (try self.coerceNum(scope, dest_type, inst)) |some|
3824 return some;
3825
3826 // integer widening
3827 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
3828 assert(inst.value() == null); // handled above
3829
3830 const src_info = inst.ty.intInfo(self.getTarget());
3831 const dst_info = dest_type.intInfo(self.getTarget());
3832 if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
3833 // small enough unsigned ints can get casted to large enough signed ints
3834 (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
3835 {
3836 const b = try self.requireRuntimeBlock(scope, inst.src);
3837 return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
3838 }
3839 }
3840
3841 // float widening
3842 if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
3843 assert(inst.value() == null); // handled above
3844
3845 const src_bits = inst.ty.floatBits(self.getTarget());
3846 const dst_bits = dest_type.floatBits(self.getTarget());
3847 if (dst_bits >= src_bits) {
3848 const b = try self.requireRuntimeBlock(scope, inst.src);
3849 return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
3850 }
3851 }
3852
3853 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
3854}
3855
3856pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
3857 const val = inst.value() orelse return null;
3858 const src_zig_tag = inst.ty.zigTypeTag();
3859 const dst_zig_tag = dest_type.zigTypeTag();
3860
3861 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
3862 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
3863 if (val.floatHasFraction()) {
3864 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
3865 }
3866 return self.fail(scope, inst.src, "TODO float to int", .{});
3867 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
3868 if (!val.intFitsInType(dest_type, self.getTarget())) {
3869 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
3870 }
3871 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3872 }
3873 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
3874 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
3875 const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) {
3876 error.Overflow => return self.fail(
3877 scope,
3878 inst.src,
3879 "cast of value {} to type '{}' loses information",
3880 .{ val, dest_type },
3881 ),
3882 error.OutOfMemory => return error.OutOfMemory,
3883 };
3884 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
3885 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
3886 return self.fail(scope, inst.src, "TODO int to float", .{});
3887 }
3888 }
3889 return null;
3890}
3891
3892pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
3893 if (ptr.ty.isConstPtr())
3894 return self.fail(scope, src, "cannot assign to constant", .{});
3895
3896 const elem_ty = ptr.ty.elemType();
3897 const value = try self.coerce(scope, elem_ty, uncasted_value);
3898 if (elem_ty.onePossibleValue() != null)
3899 return self.constVoid(scope, src);
3900
3901 // TODO handle comptime pointer writes
3902 // TODO handle if the element type requires comptime
3903
3904 const b = try self.requireRuntimeBlock(scope, src);
3905 return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
3906}
3907
3908pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3909 if (inst.value()) |val| {
3910 // Keep the comptime Value representation; take the new type.
3911 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3912 }
3913 // TODO validate the type size and other compile errors
3914 const b = try self.requireRuntimeBlock(scope, inst.src);
3915 return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
3916}
3917
3918fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3919 if (inst.value()) |val| {
3920 // The comptime Value representation is compatible with both types.
3921 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3922 }
3923 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
3924}
3925
3926fn failCObj(mod: *Module, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
3927 @setCold(true);
3928 const err_msg = try ErrorMsg.create(mod.gpa, 0, "unable to build C object: " ++ format, args);
3929 return mod.failCObjWithOwnedErrorMsg(c_object, err_msg);
3930}
3931
3932fn failCObjWithOwnedErrorMsg(mod: *Module, c_object: *CObject, err_msg: *ErrorMsg) InnerError {
3933 {
3934 errdefer err_msg.destroy(mod.gpa);
3935 try mod.failed_c_objects.ensureCapacity(mod.gpa, mod.failed_c_objects.items().len + 1);
3936 }
3937 mod.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
3938 c_object.status = .{ .failure = "" };
3939 return error.AnalysisFail;
3940}
3941
3942pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
3943 @setCold(true);
3944 const err_msg = try ErrorMsg.create(self.gpa, src, format, args);
3945 return self.failWithOwnedErrorMsg(scope, src, err_msg);
3946}
3947
3948pub fn failTok(
3949 self: *Module,
3950 scope: *Scope,
3951 token_index: ast.TokenIndex,
3952 comptime format: []const u8,
3953 args: anytype,
3954) InnerError {
3955 @setCold(true);
3956 const src = scope.tree().token_locs[token_index].start;
3957 return self.fail(scope, src, format, args);
3958}
3959
3960pub fn failNode(
3961 self: *Module,
3962 scope: *Scope,
3963 ast_node: *ast.Node,
3964 comptime format: []const u8,
3965 args: anytype,
3966) InnerError {
3967 @setCold(true);
3968 const src = scope.tree().token_locs[ast_node.firstToken()].start;
3969 return self.fail(scope, src, format, args);
3970}
3971
3972fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError {
3973 {
3974 errdefer err_msg.destroy(self.gpa);
3975 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
3976 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
3977 }
3978 switch (scope.tag) {
3979 .decl => {
3980 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
3981 decl.analysis = .sema_failure;
3982 decl.generation = self.generation;
3983 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
3984 },
3985 .block => {
3986 const block = scope.cast(Scope.Block).?;
3987 if (block.func) |func| {
3988 func.analysis = .sema_failure;
3989 } else {
3990 block.decl.analysis = .sema_failure;
3991 block.decl.generation = self.generation;
3992 }
3993 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
3994 },
3995 .gen_zir => {
3996 const gen_zir = scope.cast(Scope.GenZIR).?;
3997 gen_zir.decl.analysis = .sema_failure;
3998 gen_zir.decl.generation = self.generation;
3999 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
4000 },
4001 .local_val => {
4002 const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
4003 gen_zir.decl.analysis = .sema_failure;
4004 gen_zir.decl.generation = self.generation;
4005 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
4006 },
4007 .local_ptr => {
4008 const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
4009 gen_zir.decl.analysis = .sema_failure;
4010 gen_zir.decl.generation = self.generation;
4011 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
4012 },
4013 .zir_module => {
4014 const zir_module = scope.cast(Scope.ZIRModule).?;
4015 zir_module.status = .loaded_sema_failure;
4016 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
4017 },
4018 .none => unreachable,
4019 .file => unreachable,
4020 .container => unreachable,
4021 }
4022 return error.AnalysisFail;
4023}
4024
4025const InMemoryCoercionResult = enum {
4026 ok,
4027 no_match,
4028};
4029
4030fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
4031 if (dest_type.eql(src_type))
4032 return .ok;
4033
4034 // TODO: implement more of this function
4035
4036 return .no_match;
4037}
4038
4039pub const ErrorMsg = struct {
4040 byte_offset: usize,
4041 msg: []const u8,
4042
4043 pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
4044 const self = try gpa.create(ErrorMsg);
4045 errdefer gpa.destroy(self);
4046 self.* = try init(gpa, byte_offset, format, args);
4047 return self;
4048 }
4049
4050 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
4051 pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
4052 self.deinit(gpa);
4053 gpa.destroy(self);
1302 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
1303 pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
1304 self.deinit(gpa);
1305 gpa.destroy(self);
40541306 }
40551307
40561308 pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
......@@ -4066,375 +1318,6 @@ pub const ErrorMsg = struct {
40661318 }
40671319};
40681320
4069fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
4070 return @bitCast(u128, a) == @bitCast(u128, b);
4071}
4072
4073pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
4074 // TODO is this a performance issue? maybe we should try the operation without
4075 // resorting to BigInt first.
4076 var lhs_space: Value.BigIntSpace = undefined;
4077 var rhs_space: Value.BigIntSpace = undefined;
4078 const lhs_bigint = lhs.toBigInt(&lhs_space);
4079 const rhs_bigint = rhs.toBigInt(&rhs_space);
4080 const limbs = try allocator.alloc(
4081 std.math.big.Limb,
4082 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
4083 );
4084 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
4085 result_bigint.add(lhs_bigint, rhs_bigint);
4086 const result_limbs = result_bigint.limbs[0..result_bigint.len];
4087
4088 const val_payload = if (result_bigint.positive) blk: {
4089 const val_payload = try allocator.create(Value.Payload.IntBigPositive);
4090 val_payload.* = .{ .limbs = result_limbs };
4091 break :blk &val_payload.base;
4092 } else blk: {
4093 const val_payload = try allocator.create(Value.Payload.IntBigNegative);
4094 val_payload.* = .{ .limbs = result_limbs };
4095 break :blk &val_payload.base;
4096 };
4097
4098 return Value.initPayload(val_payload);
4099}
4100
4101pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
4102 // TODO is this a performance issue? maybe we should try the operation without
4103 // resorting to BigInt first.
4104 var lhs_space: Value.BigIntSpace = undefined;
4105 var rhs_space: Value.BigIntSpace = undefined;
4106 const lhs_bigint = lhs.toBigInt(&lhs_space);
4107 const rhs_bigint = rhs.toBigInt(&rhs_space);
4108 const limbs = try allocator.alloc(
4109 std.math.big.Limb,
4110 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
4111 );
4112 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
4113 result_bigint.sub(lhs_bigint, rhs_bigint);
4114 const result_limbs = result_bigint.limbs[0..result_bigint.len];
4115
4116 const val_payload = if (result_bigint.positive) blk: {
4117 const val_payload = try allocator.create(Value.Payload.IntBigPositive);
4118 val_payload.* = .{ .limbs = result_limbs };
4119 break :blk &val_payload.base;
4120 } else blk: {
4121 const val_payload = try allocator.create(Value.Payload.IntBigNegative);
4122 val_payload.* = .{ .limbs = result_limbs };
4123 break :blk &val_payload.base;
4124 };
4125
4126 return Value.initPayload(val_payload);
4127}
4128
4129pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
4130 var bit_count = switch (float_type.tag()) {
4131 .comptime_float => 128,
4132 else => float_type.floatBits(self.getTarget()),
4133 };
4134
4135 const allocator = scope.arena();
4136 const val_payload = switch (bit_count) {
4137 16 => {
4138 return self.fail(scope, src, "TODO Implement addition for soft floats", .{});
4139 },
4140 32 => blk: {
4141 const lhs_val = lhs.toFloat(f32);
4142 const rhs_val = rhs.toFloat(f32);
4143 const val_payload = try allocator.create(Value.Payload.Float_32);
4144 val_payload.* = .{ .val = lhs_val + rhs_val };
4145 break :blk &val_payload.base;
4146 },
4147 64 => blk: {
4148 const lhs_val = lhs.toFloat(f64);
4149 const rhs_val = rhs.toFloat(f64);
4150 const val_payload = try allocator.create(Value.Payload.Float_64);
4151 val_payload.* = .{ .val = lhs_val + rhs_val };
4152 break :blk &val_payload.base;
4153 },
4154 128 => {
4155 return self.fail(scope, src, "TODO Implement addition for big floats", .{});
4156 },
4157 else => unreachable,
4158 };
4159
4160 return Value.initPayload(val_payload);
4161}
4162
4163pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
4164 var bit_count = switch (float_type.tag()) {
4165 .comptime_float => 128,
4166 else => float_type.floatBits(self.getTarget()),
4167 };
4168
4169 const allocator = scope.arena();
4170 const val_payload = switch (bit_count) {
4171 16 => {
4172 return self.fail(scope, src, "TODO Implement substraction for soft floats", .{});
4173 },
4174 32 => blk: {
4175 const lhs_val = lhs.toFloat(f32);
4176 const rhs_val = rhs.toFloat(f32);
4177 const val_payload = try allocator.create(Value.Payload.Float_32);
4178 val_payload.* = .{ .val = lhs_val - rhs_val };
4179 break :blk &val_payload.base;
4180 },
4181 64 => blk: {
4182 const lhs_val = lhs.toFloat(f64);
4183 const rhs_val = rhs.toFloat(f64);
4184 const val_payload = try allocator.create(Value.Payload.Float_64);
4185 val_payload.* = .{ .val = lhs_val - rhs_val };
4186 break :blk &val_payload.base;
4187 },
4188 128 => {
4189 return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
4190 },
4191 else => unreachable,
4192 };
4193
4194 return Value.initPayload(val_payload);
4195}
4196
4197pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
4198 if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
4199 return Type.initTag(.const_slice_u8);
4200 }
4201 // TODO stage1 type inference bug
4202 const T = Type.Tag;
4203
4204 const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
4205 type_payload.* = .{
4206 .base = .{
4207 .tag = switch (size) {
4208 .One => if (mutable) T.single_mut_pointer else T.single_const_pointer,
4209 .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer,
4210 .C => if (mutable) T.c_mut_pointer else T.c_const_pointer,
4211 .Slice => if (mutable) T.mut_slice else T.const_slice,
4212 },
4213 },
4214 .pointee_type = elem_ty,
4215 };
4216 return Type.initPayload(&type_payload.base);
4217}
4218
4219pub fn ptrType(
4220 self: *Module,
4221 scope: *Scope,
4222 src: usize,
4223 elem_ty: Type,
4224 sentinel: ?Value,
4225 @"align": u32,
4226 bit_offset: u16,
4227 host_size: u16,
4228 mutable: bool,
4229 @"allowzero": bool,
4230 @"volatile": bool,
4231 size: std.builtin.TypeInfo.Pointer.Size,
4232) Allocator.Error!Type {
4233 assert(host_size == 0 or bit_offset < host_size * 8);
4234
4235 // TODO check if type can be represented by simplePtrType
4236 const type_payload = try scope.arena().create(Type.Payload.Pointer);
4237 type_payload.* = .{
4238 .pointee_type = elem_ty,
4239 .sentinel = sentinel,
4240 .@"align" = @"align",
4241 .bit_offset = bit_offset,
4242 .host_size = host_size,
4243 .@"allowzero" = @"allowzero",
4244 .mutable = mutable,
4245 .@"volatile" = @"volatile",
4246 .size = size,
4247 };
4248 return Type.initPayload(&type_payload.base);
4249}
4250
4251pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
4252 return Type.initPayload(switch (child_type.tag()) {
4253 .single_const_pointer => blk: {
4254 const payload = try scope.arena().create(Type.Payload.PointerSimple);
4255 payload.* = .{
4256 .base = .{ .tag = .optional_single_const_pointer },
4257 .pointee_type = child_type.elemType(),
4258 };
4259 break :blk &payload.base;
4260 },
4261 .single_mut_pointer => blk: {
4262 const payload = try scope.arena().create(Type.Payload.PointerSimple);
4263 payload.* = .{
4264 .base = .{ .tag = .optional_single_mut_pointer },
4265 .pointee_type = child_type.elemType(),
4266 };
4267 break :blk &payload.base;
4268 },
4269 else => blk: {
4270 const payload = try scope.arena().create(Type.Payload.Optional);
4271 payload.* = .{
4272 .child_type = child_type,
4273 };
4274 break :blk &payload.base;
4275 },
4276 });
4277}
4278
4279pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type {
4280 if (elem_type.eql(Type.initTag(.u8))) {
4281 if (sentinel) |some| {
4282 if (some.eql(Value.initTag(.zero))) {
4283 const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
4284 payload.* = .{
4285 .len = len,
4286 };
4287 return Type.initPayload(&payload.base);
4288 }
4289 } else {
4290 const payload = try scope.arena().create(Type.Payload.Array_u8);
4291 payload.* = .{
4292 .len = len,
4293 };
4294 return Type.initPayload(&payload.base);
4295 }
4296 }
4297
4298 if (sentinel) |some| {
4299 const payload = try scope.arena().create(Type.Payload.ArraySentinel);
4300 payload.* = .{
4301 .len = len,
4302 .sentinel = some,
4303 .elem_type = elem_type,
4304 };
4305 return Type.initPayload(&payload.base);
4306 }
4307
4308 const payload = try scope.arena().create(Type.Payload.Array);
4309 payload.* = .{
4310 .len = len,
4311 .elem_type = elem_type,
4312 };
4313 return Type.initPayload(&payload.base);
4314}
4315
4316pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type {
4317 assert(error_set.zigTypeTag() == .ErrorSet);
4318 if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) {
4319 return Type.initTag(.anyerror_void_error_union);
4320 }
4321
4322 const result = try scope.arena().create(Type.Payload.ErrorUnion);
4323 result.* = .{
4324 .error_set = error_set,
4325 .payload = payload,
4326 };
4327 return Type.initPayload(&result.base);
4328}
4329
4330pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
4331 const result = try scope.arena().create(Type.Payload.AnyFrame);
4332 result.* = .{
4333 .return_type = return_type,
4334 };
4335 return Type.initPayload(&result.base);
4336}
4337
4338pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
4339 const zir_module = scope.namespace();
4340 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
4341 const loc = std.zig.findLineColumn(source, inst.src);
4342 if (inst.tag == .constant) {
4343 std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
4344 inst.ty,
4345 inst.castTag(.constant).?.val,
4346 zir_module.subFilePath(),
4347 loc.line + 1,
4348 loc.column + 1,
4349 });
4350 } else if (inst.deaths == 0) {
4351 std.debug.print("{} ty={} src={}:{}:{}\n", .{
4352 @tagName(inst.tag),
4353 inst.ty,
4354 zir_module.subFilePath(),
4355 loc.line + 1,
4356 loc.column + 1,
4357 });
4358 } else {
4359 std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
4360 @tagName(inst.tag),
4361 inst.ty,
4362 inst.deaths,
4363 zir_module.subFilePath(),
4364 loc.line + 1,
4365 loc.column + 1,
4366 });
4367 }
4368}
4369
4370pub const PanicId = enum {
4371 unreach,
4372 unwrap_null,
4373};
4374
4375pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
4376 const block_inst = try parent_block.arena.create(Inst.Block);
4377 block_inst.* = .{
4378 .base = .{
4379 .tag = Inst.Block.base_tag,
4380 .ty = Type.initTag(.void),
4381 .src = ok.src,
4382 },
4383 .body = .{
4384 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
4385 },
4386 };
4387
4388 const ok_body: ir.Body = .{
4389 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
4390 };
4391 const brvoid = try parent_block.arena.create(Inst.BrVoid);
4392 brvoid.* = .{
4393 .base = .{
4394 .tag = .brvoid,
4395 .ty = Type.initTag(.noreturn),
4396 .src = ok.src,
4397 },
4398 .block = block_inst,
4399 };
4400 ok_body.instructions[0] = &brvoid.base;
4401
4402 var fail_block: Scope.Block = .{
4403 .parent = parent_block,
4404 .func = parent_block.func,
4405 .decl = parent_block.decl,
4406 .instructions = .{},
4407 .arena = parent_block.arena,
4408 .is_comptime = parent_block.is_comptime,
4409 };
4410 defer fail_block.instructions.deinit(mod.gpa);
4411
4412 _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
4413
4414 const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
4415
4416 const condbr = try parent_block.arena.create(Inst.CondBr);
4417 condbr.* = .{
4418 .base = .{
4419 .tag = .condbr,
4420 .ty = Type.initTag(.noreturn),
4421 .src = ok.src,
4422 },
4423 .condition = ok,
4424 .then_body = ok_body,
4425 .else_body = fail_body,
4426 };
4427 block_inst.body.instructions[0] = &condbr.base;
4428
4429 try parent_block.instructions.append(mod.gpa, &block_inst.base);
4430}
4431
4432pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
4433 // TODO Once we have a panic function to call, call it here instead of breakpoint.
4434 _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
4435 return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
4436}
4437
44381321pub const FileExt = enum {
44391322 c,
44401323 cpp,
src-self-hosted/ZigModule.zig created+3238
......@@ -0,0 +1,3238 @@
1//! TODO This is going to get renamed from ZigModule to Module (but first we have to rename
2//! Module to Compilation).
3const Module = @This();
4const Compilation = @import("Module.zig");
5
6const std = @import("std");
7const mem = std.mem;
8const Allocator = std.mem.Allocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const Value = @import("value.zig").Value;
11const Type = @import("type.zig").Type;
12const TypedValue = @import("TypedValue.zig");
13const assert = std.debug.assert;
14const log = std.log.scoped(.module);
15const BigIntConst = std.math.big.int.Const;
16const BigIntMutable = std.math.big.int.Mutable;
17const Target = std.Target;
18const Package = @import("Package.zig");
19const link = @import("link.zig");
20const ir = @import("ir.zig");
21const zir = @import("zir.zig");
22const Inst = ir.Inst;
23const Body = ir.Body;
24const ast = std.zig.ast;
25const trace = @import("tracy.zig").trace;
26const astgen = @import("astgen.zig");
27const zir_sema = @import("zir_sema.zig");
28
29/// General-purpose allocator. Used for both temporary and long-term storage.
30gpa: *Allocator,
31comp: *Compilation,
32
33/// Where our incremental compilation metadata serialization will go.
34zig_cache_artifact_directory: Compilation.Directory,
35/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
36root_pkg: *Package,
37/// Module owns this resource.
38/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
39root_scope: *Scope,
40/// It's rare for a decl to be exported, so we save memory by having a sparse map of
41/// Decl pointers to details about them being exported.
42/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
43decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
44/// We track which export is associated with the given symbol name for quick
45/// detection of symbol collisions.
46symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
47/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
48/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
49/// is performing the export of another Decl.
50/// This table owns the Export memory.
51export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
52/// Maps fully qualified namespaced names to the Decl struct for them.
53decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
54/// We optimize memory usage for a compilation with no compile errors by storing the
55/// error messages and mapping outside of `Decl`.
56/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
57/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
58/// a Decl can have a failed_decls entry but have analysis status of success.
59failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
60/// Using a map here for consistency with the other fields here.
61/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
62failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
63/// Using a map here for consistency with the other fields here.
64/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
65failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{},
66
67next_anon_name_index: usize = 0,
68
69/// Candidates for deletion. After a semantic analysis update completes, this list
70/// contains Decls that need to be deleted if they end up having no references to them.
71deletion_set: ArrayListUnmanaged(*Decl) = .{},
72
73/// Error tags and their values, tag names are duped with mod.gpa.
74global_error_set: std.StringHashMapUnmanaged(u16) = .{},
75
76/// Incrementing integer used to compare against the corresponding Decl
77/// field to determine whether a Decl's status applies to an ongoing update, or a
78/// previous analysis.
79generation: u32 = 0,
80
81pub const Export = struct {
82 options: std.builtin.ExportOptions,
83 /// Byte offset into the file that contains the export directive.
84 src: usize,
85 /// Represents the position of the export, if any, in the output file.
86 link: link.File.Elf.Export,
87 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
88 owner_decl: *Decl,
89 /// The Decl being exported. Note this is *not* the Decl performing the export.
90 exported_decl: *Decl,
91 status: enum {
92 in_progress,
93 failed,
94 /// Indicates that the failure was due to a temporary issue, such as an I/O error
95 /// when writing to the output file. Retrying the export may succeed.
96 failed_retryable,
97 complete,
98 },
99};
100
101pub const Decl = struct {
102 /// This name is relative to the containing namespace of the decl. It uses a null-termination
103 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
104 /// in symbol names, because executable file formats use null-terminated strings for symbol names.
105 /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
106 /// mapping them to an address in the output file.
107 /// Memory owned by this decl, using Module's allocator.
108 name: [*:0]const u8,
109 /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
110 /// Reference to externally owned memory.
111 scope: *Scope,
112 /// The AST Node decl index or ZIR Inst index that contains this declaration.
113 /// Must be recomputed when the corresponding source file is modified.
114 src_index: usize,
115 /// The most recent value of the Decl after a successful semantic analysis.
116 typed_value: union(enum) {
117 never_succeeded: void,
118 most_recent: TypedValue.Managed,
119 },
120 /// Represents the "shallow" analysis status. For example, for decls that are functions,
121 /// the function type is analyzed with this set to `in_progress`, however, the semantic
122 /// analysis of the function body is performed with this value set to `success`. Functions
123 /// have their own analysis status field.
124 analysis: enum {
125 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
126 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
127 unreferenced,
128 /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
129 in_progress,
130 /// This Decl might be OK but it depends on another one which did not successfully complete
131 /// semantic analysis.
132 dependency_failure,
133 /// Semantic analysis failure.
134 /// There will be a corresponding ErrorMsg in Module.failed_decls.
135 sema_failure,
136 /// There will be a corresponding ErrorMsg in Module.failed_decls.
137 /// This indicates the failure was something like running out of disk space,
138 /// and attempting semantic analysis again may succeed.
139 sema_failure_retryable,
140 /// There will be a corresponding ErrorMsg in Module.failed_decls.
141 codegen_failure,
142 /// There will be a corresponding ErrorMsg in Module.failed_decls.
143 /// This indicates the failure was something like running out of disk space,
144 /// and attempting codegen again may succeed.
145 codegen_failure_retryable,
146 /// Everything is done. During an update, this Decl may be out of date, depending
147 /// on its dependencies. The `generation` field can be used to determine if this
148 /// completion status occurred before or after a given update.
149 complete,
150 /// A Module update is in progress, and this Decl has been flagged as being known
151 /// to require re-analysis.
152 outdated,
153 },
154 /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
155 /// when removed.
156 deletion_flag: bool,
157 /// Whether the corresponding AST decl has a `pub` keyword.
158 is_pub: bool,
159
160 /// An integer that can be checked against the corresponding incrementing
161 /// generation field of Module. This is used to determine whether `complete` status
162 /// represents pre- or post- re-analysis.
163 generation: u32,
164
165 /// Represents the position of the code in the output file.
166 /// This is populated regardless of semantic analysis and code generation.
167 link: link.File.LinkBlock,
168
169 /// Represents the function in the linked output file, if the `Decl` is a function.
170 /// This is stored here and not in `Fn` because `Decl` survives across updates but
171 /// `Fn` does not.
172 /// TODO Look into making `Fn` a longer lived structure and moving this field there
173 /// to save on memory usage.
174 fn_link: link.File.LinkFn,
175
176 contents_hash: std.zig.SrcHash,
177
178 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
179 /// typed_value is modified.
180 dependants: DepsTable = .{},
181 /// The shallow set of other decls whose typed_value changing indicates that this Decl's
182 /// typed_value may need to be regenerated.
183 dependencies: DepsTable = .{},
184
185 /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for
186 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
187 pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
188
189 pub fn destroy(self: *Decl, gpa: *Allocator) void {
190 gpa.free(mem.spanZ(self.name));
191 if (self.typedValueManaged()) |tvm| {
192 tvm.deinit(gpa);
193 }
194 self.dependants.deinit(gpa);
195 self.dependencies.deinit(gpa);
196 gpa.destroy(self);
197 }
198
199 pub fn src(self: Decl) usize {
200 switch (self.scope.tag) {
201 .container => {
202 const container = @fieldParentPtr(Scope.Container, "base", self.scope);
203 const tree = container.file_scope.contents.tree;
204 // TODO Container should have it's own decls()
205 const decl_node = tree.root_node.decls()[self.src_index];
206 return tree.token_locs[decl_node.firstToken()].start;
207 },
208 .zir_module => {
209 const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
210 const module = zir_module.contents.module;
211 const src_decl = module.decls[self.src_index];
212 return src_decl.inst.src;
213 },
214 .file, .block => unreachable,
215 .gen_zir => unreachable,
216 .local_val => unreachable,
217 .local_ptr => unreachable,
218 .decl => unreachable,
219 }
220 }
221
222 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
223 return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
224 }
225
226 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
227 const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
228 return tvm.typed_value;
229 }
230
231 pub fn value(self: *Decl) error{AnalysisFail}!Value {
232 return (try self.typedValue()).val;
233 }
234
235 pub fn dump(self: *Decl) void {
236 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
237 std.debug.print("{}:{}:{} name={} status={}", .{
238 self.scope.sub_file_path,
239 loc.line + 1,
240 loc.column + 1,
241 mem.spanZ(self.name),
242 @tagName(self.analysis),
243 });
244 if (self.typedValueManaged()) |tvm| {
245 std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
246 }
247 std.debug.print("\n", .{});
248 }
249
250 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
251 switch (self.typed_value) {
252 .most_recent => |*x| return x,
253 .never_succeeded => return null,
254 }
255 }
256
257 fn removeDependant(self: *Decl, other: *Decl) void {
258 self.dependants.removeAssertDiscard(other);
259 }
260
261 fn removeDependency(self: *Decl, other: *Decl) void {
262 self.dependencies.removeAssertDiscard(other);
263 }
264};
265
266/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
267pub const Fn = struct {
268 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
269 analysis: union(enum) {
270 queued: *ZIR,
271 in_progress,
272 /// There will be a corresponding ErrorMsg in Module.failed_decls
273 sema_failure,
274 /// This Fn might be OK but it depends on another Decl which did not successfully complete
275 /// semantic analysis.
276 dependency_failure,
277 success: Body,
278 },
279 owner_decl: *Decl,
280
281 /// This memory is temporary and points to stack memory for the duration
282 /// of Fn analysis.
283 pub const Analysis = struct {
284 inner_block: Scope.Block,
285 };
286
287 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
288 pub const ZIR = struct {
289 body: zir.Module.Body,
290 arena: std.heap.ArenaAllocator.State,
291 };
292
293 /// For debugging purposes.
294 pub fn dump(self: *Fn, mod: Module) void {
295 std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name});
296 switch (self.analysis) {
297 .queued => {
298 std.debug.print("queued\n", .{});
299 },
300 .in_progress => {
301 std.debug.print("in_progress\n", .{});
302 },
303 else => {
304 std.debug.print("\n", .{});
305 zir.dumpFn(mod, self);
306 },
307 }
308 }
309};
310
311pub const Var = struct {
312 /// if is_extern == true this is undefined
313 init: Value,
314 owner_decl: *Decl,
315
316 is_extern: bool,
317 is_mutable: bool,
318 is_threadlocal: bool,
319};
320
321pub const Scope = struct {
322 tag: Tag,
323
324 pub const NameHash = [16]u8;
325
326 pub fn cast(base: *Scope, comptime T: type) ?*T {
327 if (base.tag != T.base_tag)
328 return null;
329
330 return @fieldParentPtr(T, "base", base);
331 }
332
333 /// Asserts the scope has a parent which is a DeclAnalysis and
334 /// returns the arena Allocator.
335 pub fn arena(self: *Scope) *Allocator {
336 switch (self.tag) {
337 .block => return self.cast(Block).?.arena,
338 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
339 .gen_zir => return self.cast(GenZIR).?.arena,
340 .local_val => return self.cast(LocalVal).?.gen_zir.arena,
341 .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
342 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
343 .file => unreachable,
344 .container => unreachable,
345 }
346 }
347
348 /// If the scope has a parent which is a `DeclAnalysis`,
349 /// returns the `Decl`, otherwise returns `null`.
350 pub fn decl(self: *Scope) ?*Decl {
351 return switch (self.tag) {
352 .block => self.cast(Block).?.decl,
353 .gen_zir => self.cast(GenZIR).?.decl,
354 .local_val => self.cast(LocalVal).?.gen_zir.decl,
355 .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
356 .decl => self.cast(DeclAnalysis).?.decl,
357 .zir_module => null,
358 .file => null,
359 .container => null,
360 };
361 }
362
363 /// Asserts the scope has a parent which is a ZIRModule or Container and
364 /// returns it.
365 pub fn namespace(self: *Scope) *Scope {
366 switch (self.tag) {
367 .block => return self.cast(Block).?.decl.scope,
368 .gen_zir => return self.cast(GenZIR).?.decl.scope,
369 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
370 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
371 .decl => return self.cast(DeclAnalysis).?.decl.scope,
372 .file => return &self.cast(File).?.root_container.base,
373 .zir_module, .container => return self,
374 }
375 }
376
377 /// Must generate unique bytes with no collisions with other decls.
378 /// The point of hashing here is only to limit the number of bytes of
379 /// the unique identifier to a fixed size (16 bytes).
380 pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
381 switch (self.tag) {
382 .block => unreachable,
383 .gen_zir => unreachable,
384 .local_val => unreachable,
385 .local_ptr => unreachable,
386 .decl => unreachable,
387 .file => unreachable,
388 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
389 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
390 }
391 }
392
393 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
394 pub fn tree(self: *Scope) *ast.Tree {
395 switch (self.tag) {
396 .file => return self.cast(File).?.contents.tree,
397 .zir_module => unreachable,
398 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
399 .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
400 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
401 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
402 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
403 .container => return self.cast(Container).?.file_scope.contents.tree,
404 }
405 }
406
407 /// Asserts the scope is a child of a `GenZIR` and returns it.
408 pub fn getGenZIR(self: *Scope) *GenZIR {
409 return switch (self.tag) {
410 .block => unreachable,
411 .gen_zir => self.cast(GenZIR).?,
412 .local_val => return self.cast(LocalVal).?.gen_zir,
413 .local_ptr => return self.cast(LocalPtr).?.gen_zir,
414 .decl => unreachable,
415 .zir_module => unreachable,
416 .file => unreachable,
417 .container => unreachable,
418 };
419 }
420
421 /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
422 /// returns the sub_file_path field.
423 pub fn subFilePath(base: *Scope) []const u8 {
424 switch (base.tag) {
425 .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
426 .file => return @fieldParentPtr(File, "base", base).sub_file_path,
427 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
428 .block => unreachable,
429 .gen_zir => unreachable,
430 .local_val => unreachable,
431 .local_ptr => unreachable,
432 .decl => unreachable,
433 }
434 }
435
436 pub fn unload(base: *Scope, gpa: *Allocator) void {
437 switch (base.tag) {
438 .file => return @fieldParentPtr(File, "base", base).unload(gpa),
439 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
440 .block => unreachable,
441 .gen_zir => unreachable,
442 .local_val => unreachable,
443 .local_ptr => unreachable,
444 .decl => unreachable,
445 .container => unreachable,
446 }
447 }
448
449 pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
450 switch (base.tag) {
451 .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
452 .file => return @fieldParentPtr(File, "base", base).getSource(module),
453 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
454 .gen_zir => unreachable,
455 .local_val => unreachable,
456 .local_ptr => unreachable,
457 .block => unreachable,
458 .decl => unreachable,
459 }
460 }
461
462 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
463 pub fn removeDecl(base: *Scope, child: *Decl) void {
464 switch (base.tag) {
465 .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
466 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
467 .file => unreachable,
468 .block => unreachable,
469 .gen_zir => unreachable,
470 .local_val => unreachable,
471 .local_ptr => unreachable,
472 .decl => unreachable,
473 }
474 }
475
476 /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
477 pub fn destroy(base: *Scope, gpa: *Allocator) void {
478 switch (base.tag) {
479 .file => {
480 const scope_file = @fieldParentPtr(File, "base", base);
481 scope_file.deinit(gpa);
482 gpa.destroy(scope_file);
483 },
484 .zir_module => {
485 const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
486 scope_zir_module.deinit(gpa);
487 gpa.destroy(scope_zir_module);
488 },
489 .block => unreachable,
490 .gen_zir => unreachable,
491 .local_val => unreachable,
492 .local_ptr => unreachable,
493 .decl => unreachable,
494 .container => unreachable,
495 }
496 }
497
498 fn name_hash_hash(x: NameHash) u32 {
499 return @truncate(u32, @bitCast(u128, x));
500 }
501
502 fn name_hash_eql(a: NameHash, b: NameHash) bool {
503 return @bitCast(u128, a) == @bitCast(u128, b);
504 }
505
506 pub const Tag = enum {
507 /// .zir source code.
508 zir_module,
509 /// .zig source code.
510 file,
511 /// struct, enum or union, every .file contains one of these.
512 container,
513 block,
514 decl,
515 gen_zir,
516 local_val,
517 local_ptr,
518 };
519
520 pub const Container = struct {
521 pub const base_tag: Tag = .container;
522 base: Scope = Scope{ .tag = base_tag },
523
524 file_scope: *Scope.File,
525
526 /// Direct children of the file.
527 decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
528
529 // TODO implement container types and put this in a status union
530 // ty: Type
531
532 pub fn deinit(self: *Container, gpa: *Allocator) void {
533 self.decls.deinit(gpa);
534 self.* = undefined;
535 }
536
537 pub fn removeDecl(self: *Container, child: *Decl) void {
538 _ = self.decls.remove(child);
539 }
540
541 pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
542 // TODO container scope qualified names.
543 return std.zig.hashSrc(name);
544 }
545 };
546
547 pub const File = struct {
548 pub const base_tag: Tag = .file;
549 base: Scope = Scope{ .tag = base_tag },
550
551 /// Relative to the owning package's root_src_dir.
552 /// Reference to external memory, not owned by File.
553 sub_file_path: []const u8,
554 source: union(enum) {
555 unloaded: void,
556 bytes: [:0]const u8,
557 },
558 contents: union {
559 not_available: void,
560 tree: *ast.Tree,
561 },
562 status: enum {
563 never_loaded,
564 unloaded_success,
565 unloaded_parse_failure,
566 loaded_success,
567 },
568
569 root_container: Container,
570
571 pub fn unload(self: *File, gpa: *Allocator) void {
572 switch (self.status) {
573 .never_loaded,
574 .unloaded_parse_failure,
575 .unloaded_success,
576 => {},
577
578 .loaded_success => {
579 self.contents.tree.deinit();
580 self.status = .unloaded_success;
581 },
582 }
583 switch (self.source) {
584 .bytes => |bytes| {
585 gpa.free(bytes);
586 self.source = .{ .unloaded = {} };
587 },
588 .unloaded => {},
589 }
590 }
591
592 pub fn deinit(self: *File, gpa: *Allocator) void {
593 self.root_container.deinit(gpa);
594 self.unload(gpa);
595 self.* = undefined;
596 }
597
598 pub fn dumpSrc(self: *File, src: usize) void {
599 const loc = std.zig.findLineColumn(self.source.bytes, src);
600 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
601 }
602
603 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
604 switch (self.source) {
605 .unloaded => {
606 const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
607 module.gpa,
608 self.sub_file_path,
609 std.math.maxInt(u32),
610 null,
611 1,
612 0,
613 );
614 self.source = .{ .bytes = source };
615 return source;
616 },
617 .bytes => |bytes| return bytes,
618 }
619 }
620 };
621
622 pub const ZIRModule = struct {
623 pub const base_tag: Tag = .zir_module;
624 base: Scope = Scope{ .tag = base_tag },
625 /// Relative to the owning package's root_src_dir.
626 /// Reference to external memory, not owned by ZIRModule.
627 sub_file_path: []const u8,
628 source: union(enum) {
629 unloaded: void,
630 bytes: [:0]const u8,
631 },
632 contents: union {
633 not_available: void,
634 module: *zir.Module,
635 },
636 status: enum {
637 never_loaded,
638 unloaded_success,
639 unloaded_parse_failure,
640 unloaded_sema_failure,
641
642 loaded_sema_failure,
643 loaded_success,
644 },
645
646 /// Even though .zir files only have 1 module, this set is still needed
647 /// because of anonymous Decls, which can exist in the global set, but
648 /// not this one.
649 decls: ArrayListUnmanaged(*Decl),
650
651 pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
652 switch (self.status) {
653 .never_loaded,
654 .unloaded_parse_failure,
655 .unloaded_sema_failure,
656 .unloaded_success,
657 => {},
658
659 .loaded_success => {
660 self.contents.module.deinit(gpa);
661 gpa.destroy(self.contents.module);
662 self.contents = .{ .not_available = {} };
663 self.status = .unloaded_success;
664 },
665 .loaded_sema_failure => {
666 self.contents.module.deinit(gpa);
667 gpa.destroy(self.contents.module);
668 self.contents = .{ .not_available = {} };
669 self.status = .unloaded_sema_failure;
670 },
671 }
672 switch (self.source) {
673 .bytes => |bytes| {
674 gpa.free(bytes);
675 self.source = .{ .unloaded = {} };
676 },
677 .unloaded => {},
678 }
679 }
680
681 pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
682 self.decls.deinit(gpa);
683 self.unload(gpa);
684 self.* = undefined;
685 }
686
687 pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
688 for (self.decls.items) |item, i| {
689 if (item == child) {
690 _ = self.decls.swapRemove(i);
691 return;
692 }
693 }
694 }
695
696 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
697 const loc = std.zig.findLineColumn(self.source.bytes, src);
698 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
699 }
700
701 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
702 switch (self.source) {
703 .unloaded => {
704 const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
705 module.gpa,
706 self.sub_file_path,
707 std.math.maxInt(u32),
708 null,
709 1,
710 0,
711 );
712 self.source = .{ .bytes = source };
713 return source;
714 },
715 .bytes => |bytes| return bytes,
716 }
717 }
718
719 pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
720 // ZIR modules only have 1 file with all decls global in the same namespace.
721 return std.zig.hashSrc(name);
722 }
723 };
724
725 /// This is a temporary structure, references to it are valid only
726 /// during semantic analysis of the block.
727 pub const Block = struct {
728 pub const base_tag: Tag = .block;
729 base: Scope = Scope{ .tag = base_tag },
730 parent: ?*Block,
731 func: ?*Fn,
732 decl: *Decl,
733 instructions: ArrayListUnmanaged(*Inst),
734 /// Points to the arena allocator of DeclAnalysis
735 arena: *Allocator,
736 label: ?Label = null,
737 is_comptime: bool,
738
739 pub const Label = struct {
740 zir_block: *zir.Inst.Block,
741 results: ArrayListUnmanaged(*Inst),
742 block_inst: *Inst.Block,
743 };
744 };
745
746 /// This is a temporary structure, references to it are valid only
747 /// during semantic analysis of the decl.
748 pub const DeclAnalysis = struct {
749 pub const base_tag: Tag = .decl;
750 base: Scope = Scope{ .tag = base_tag },
751 decl: *Decl,
752 arena: std.heap.ArenaAllocator,
753 };
754
755 /// This is a temporary structure, references to it are valid only
756 /// during semantic analysis of the decl.
757 pub const GenZIR = struct {
758 pub const base_tag: Tag = .gen_zir;
759 base: Scope = Scope{ .tag = base_tag },
760 /// Parents can be: `GenZIR`, `ZIRModule`, `File`
761 parent: *Scope,
762 decl: *Decl,
763 arena: *Allocator,
764 /// The first N instructions in a function body ZIR are arg instructions.
765 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
766 label: ?Label = null,
767
768 pub const Label = struct {
769 token: ast.TokenIndex,
770 block_inst: *zir.Inst.Block,
771 result_loc: astgen.ResultLoc,
772 };
773 };
774
775 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
776 /// This structure lives as long as the AST generation of the Block
777 /// node that contains the variable.
778 pub const LocalVal = struct {
779 pub const base_tag: Tag = .local_val;
780 base: Scope = Scope{ .tag = base_tag },
781 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
782 parent: *Scope,
783 gen_zir: *GenZIR,
784 name: []const u8,
785 inst: *zir.Inst,
786 };
787
788 /// This could be a `const` or `var` local. It has a pointer instead of a value.
789 /// This structure lives as long as the AST generation of the Block
790 /// node that contains the variable.
791 pub const LocalPtr = struct {
792 pub const base_tag: Tag = .local_ptr;
793 base: Scope = Scope{ .tag = base_tag },
794 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
795 parent: *Scope,
796 gen_zir: *GenZIR,
797 name: []const u8,
798 ptr: *zir.Inst,
799 };
800};
801
802pub const InnerError = error{ OutOfMemory, AnalysisFail };
803
804pub fn deinit(self: *Module) void {
805 const gpa = self.gpa;
806
807 self.zig_cache_artifact_directory.handle.close();
808
809 self.deletion_set.deinit(gpa);
810
811 for (self.decl_table.items()) |entry| {
812 entry.value.destroy(gpa);
813 }
814 self.decl_table.deinit(gpa);
815
816 for (self.failed_decls.items()) |entry| {
817 entry.value.destroy(gpa);
818 }
819 self.failed_decls.deinit(gpa);
820
821 for (self.failed_files.items()) |entry| {
822 entry.value.destroy(gpa);
823 }
824 self.failed_files.deinit(gpa);
825
826 for (self.failed_exports.items()) |entry| {
827 entry.value.destroy(gpa);
828 }
829 self.failed_exports.deinit(gpa);
830
831 for (self.decl_exports.items()) |entry| {
832 const export_list = entry.value;
833 gpa.free(export_list);
834 }
835 self.decl_exports.deinit(gpa);
836
837 for (self.export_owners.items()) |entry| {
838 freeExportList(gpa, entry.value);
839 }
840 self.export_owners.deinit(gpa);
841
842 self.symbol_exports.deinit(gpa);
843 self.root_scope.destroy(gpa);
844
845 var it = self.global_error_set.iterator();
846 while (it.next()) |entry| {
847 gpa.free(entry.key);
848 }
849 self.global_error_set.deinit(gpa);
850}
851
852fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
853 for (export_list) |exp| {
854 gpa.free(exp.options.name);
855 gpa.destroy(exp);
856 }
857 gpa.free(export_list);
858}
859
860pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
861 const tracy = trace(@src());
862 defer tracy.end();
863
864 const subsequent_analysis = switch (decl.analysis) {
865 .in_progress => unreachable,
866
867 .sema_failure,
868 .sema_failure_retryable,
869 .codegen_failure,
870 .dependency_failure,
871 .codegen_failure_retryable,
872 => return error.AnalysisFail,
873
874 .complete => return,
875
876 .outdated => blk: {
877 log.debug("re-analyzing {}\n", .{decl.name});
878
879 // The exports this Decl performs will be re-discovered, so we remove them here
880 // prior to re-analysis.
881 self.deleteDeclExports(decl);
882 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
883 for (decl.dependencies.items()) |entry| {
884 const dep = entry.key;
885 dep.removeDependant(decl);
886 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
887 // We don't perform a deletion here, because this Decl or another one
888 // may end up referencing it before the update is complete.
889 dep.deletion_flag = true;
890 try self.deletion_set.append(self.gpa, dep);
891 }
892 }
893 decl.dependencies.clearRetainingCapacity();
894
895 break :blk true;
896 },
897
898 .unreferenced => false,
899 };
900
901 const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
902 try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
903 else
904 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
905 error.OutOfMemory => return error.OutOfMemory,
906 error.AnalysisFail => return error.AnalysisFail,
907 else => {
908 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
909 self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create(
910 self.gpa,
911 decl.src(),
912 "unable to analyze: {}",
913 .{@errorName(err)},
914 ));
915 decl.analysis = .sema_failure_retryable;
916 return error.AnalysisFail;
917 },
918 };
919
920 if (subsequent_analysis) {
921 // We may need to chase the dependants and re-analyze them.
922 // However, if the decl is a function, and the type is the same, we do not need to.
923 if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
924 for (decl.dependants.items()) |entry| {
925 const dep = entry.key;
926 switch (dep.analysis) {
927 .unreferenced => unreachable,
928 .in_progress => unreachable,
929 .outdated => continue, // already queued for update
930
931 .dependency_failure,
932 .sema_failure,
933 .sema_failure_retryable,
934 .codegen_failure,
935 .codegen_failure_retryable,
936 .complete,
937 => if (dep.generation != self.generation) {
938 try self.markOutdatedDecl(dep);
939 },
940 }
941 }
942 }
943 }
944}
945
946fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
947 const tracy = trace(@src());
948 defer tracy.end();
949
950 const container_scope = decl.scope.cast(Scope.Container).?;
951 const tree = try self.getAstTree(container_scope);
952 const ast_node = tree.root_node.decls()[decl.src_index];
953 switch (ast_node.tag) {
954 .FnProto => {
955 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
956
957 decl.analysis = .in_progress;
958
959 // This arena allocator's memory is discarded at the end of this function. It is used
960 // to determine the type of the function, and hence the type of the decl, which is needed
961 // to complete the Decl analysis.
962 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
963 defer fn_type_scope_arena.deinit();
964 var fn_type_scope: Scope.GenZIR = .{
965 .decl = decl,
966 .arena = &fn_type_scope_arena.allocator,
967 .parent = decl.scope,
968 };
969 defer fn_type_scope.instructions.deinit(self.gpa);
970
971 decl.is_pub = fn_proto.getVisibToken() != null;
972 const body_node = fn_proto.getBodyNode() orelse
973 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
974
975 const param_decls = fn_proto.params();
976 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
977
978 const fn_src = tree.token_locs[fn_proto.fn_token].start;
979 const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
980 .ty = Type.initTag(.type),
981 .val = Value.initTag(.type_type),
982 });
983 const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
984 for (param_decls) |param_decl, i| {
985 const param_type_node = switch (param_decl.param_type) {
986 .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
987 .type_expr => |node| node,
988 };
989 param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
990 }
991 if (fn_proto.getVarArgsToken()) |var_args_token| {
992 return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
993 }
994 if (fn_proto.getLibName()) |lib_name| {
995 return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
996 }
997 if (fn_proto.getAlignExpr()) |align_expr| {
998 return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
999 }
1000 if (fn_proto.getSectionExpr()) |sect_expr| {
1001 return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
1002 }
1003 if (fn_proto.getCallconvExpr()) |callconv_expr| {
1004 return self.failNode(
1005 &fn_type_scope.base,
1006 callconv_expr,
1007 "TODO implement function calling convention expression",
1008 .{},
1009 );
1010 }
1011 const return_type_expr = switch (fn_proto.return_type) {
1012 .Explicit => |node| node,
1013 .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
1014 .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
1015 };
1016
1017 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
1018 const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
1019 .return_type = return_type_inst,
1020 .param_types = param_types,
1021 }, .{});
1022
1023 // We need the memory for the Type to go into the arena for the Decl
1024 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1025 errdefer decl_arena.deinit();
1026 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
1027
1028 var block_scope: Scope.Block = .{
1029 .parent = null,
1030 .func = null,
1031 .decl = decl,
1032 .instructions = .{},
1033 .arena = &decl_arena.allocator,
1034 .is_comptime = false,
1035 };
1036 defer block_scope.instructions.deinit(self.gpa);
1037
1038 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
1039 .instructions = fn_type_scope.instructions.items,
1040 });
1041 const new_func = try decl_arena.allocator.create(Fn);
1042 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
1043
1044 const fn_zir = blk: {
1045 // This scope's arena memory is discarded after the ZIR generation
1046 // pass completes, and semantic analysis of it completes.
1047 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1048 errdefer gen_scope_arena.deinit();
1049 var gen_scope: Scope.GenZIR = .{
1050 .decl = decl,
1051 .arena = &gen_scope_arena.allocator,
1052 .parent = decl.scope,
1053 };
1054 defer gen_scope.instructions.deinit(self.gpa);
1055
1056 // We need an instruction for each parameter, and they must be first in the body.
1057 try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
1058 var params_scope = &gen_scope.base;
1059 for (fn_proto.params()) |param, i| {
1060 const name_token = param.name_token.?;
1061 const src = tree.token_locs[name_token].start;
1062 const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
1063 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
1064 arg.* = .{
1065 .base = .{
1066 .tag = .arg,
1067 .src = src,
1068 },
1069 .positionals = .{
1070 .name = param_name,
1071 },
1072 .kw_args = .{},
1073 };
1074 gen_scope.instructions.items[i] = &arg.base;
1075 const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);
1076 sub_scope.* = .{
1077 .parent = params_scope,
1078 .gen_zir = &gen_scope,
1079 .name = param_name,
1080 .inst = &arg.base,
1081 };
1082 params_scope = &sub_scope.base;
1083 }
1084
1085 const body_block = body_node.cast(ast.Node.Block).?;
1086
1087 try astgen.blockExpr(self, params_scope, body_block);
1088
1089 if (gen_scope.instructions.items.len == 0 or
1090 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
1091 {
1092 const src = tree.token_locs[body_block.rbrace].start;
1093 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
1094 }
1095
1096 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
1097 fn_zir.* = .{
1098 .body = .{
1099 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1100 },
1101 .arena = gen_scope_arena.state,
1102 };
1103 break :blk fn_zir;
1104 };
1105
1106 new_func.* = .{
1107 .analysis = .{ .queued = fn_zir },
1108 .owner_decl = decl,
1109 };
1110 fn_payload.* = .{ .func = new_func };
1111
1112 var prev_type_has_bits = false;
1113 var type_changed = true;
1114
1115 if (decl.typedValueManaged()) |tvm| {
1116 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1117 type_changed = !tvm.typed_value.ty.eql(fn_type);
1118
1119 tvm.deinit(self.gpa);
1120 }
1121
1122 decl_arena_state.* = decl_arena.state;
1123 decl.typed_value = .{
1124 .most_recent = .{
1125 .typed_value = .{
1126 .ty = fn_type,
1127 .val = Value.initPayload(&fn_payload.base),
1128 },
1129 .arena = decl_arena_state,
1130 },
1131 };
1132 decl.analysis = .complete;
1133 decl.generation = self.generation;
1134
1135 if (fn_type.hasCodeGenBits()) {
1136 // We don't fully codegen the decl until later, but we do need to reserve a global
1137 // offset table index for it. This allows us to codegen decls out of dependency order,
1138 // increasing how many computations can be done in parallel.
1139 try self.comp.bin_file.allocateDeclIndexes(decl);
1140 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1141 } else if (prev_type_has_bits) {
1142 self.comp.bin_file.freeDecl(decl);
1143 }
1144
1145 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1146 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1147 const export_src = tree.token_locs[maybe_export_token].start;
1148 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
1149 const name = tree.tokenSliceLoc(name_loc);
1150 // The scope needs to have the decl in it.
1151 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1152 }
1153 }
1154 return type_changed;
1155 },
1156 .VarDecl => {
1157 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
1158
1159 decl.analysis = .in_progress;
1160
1161 // We need the memory for the Type to go into the arena for the Decl
1162 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1163 errdefer decl_arena.deinit();
1164 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
1165
1166 var block_scope: Scope.Block = .{
1167 .parent = null,
1168 .func = null,
1169 .decl = decl,
1170 .instructions = .{},
1171 .arena = &decl_arena.allocator,
1172 .is_comptime = true,
1173 };
1174 defer block_scope.instructions.deinit(self.gpa);
1175
1176 decl.is_pub = var_decl.getVisibToken() != null;
1177 const is_extern = blk: {
1178 const maybe_extern_token = var_decl.getExternExportToken() orelse
1179 break :blk false;
1180 if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
1181 if (var_decl.getInitNode()) |some| {
1182 return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
1183 }
1184 break :blk true;
1185 };
1186 if (var_decl.getLibName()) |lib_name| {
1187 assert(is_extern);
1188 return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
1189 }
1190 const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
1191 const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
1192 if (!is_mutable) {
1193 return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
1194 }
1195 break :blk true;
1196 } else false;
1197 assert(var_decl.getComptimeToken() == null);
1198 if (var_decl.getAlignNode()) |align_expr| {
1199 return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
1200 }
1201 if (var_decl.getSectionNode()) |sect_expr| {
1202 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
1203 }
1204
1205 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
1206 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1207 defer gen_scope_arena.deinit();
1208 var gen_scope: Scope.GenZIR = .{
1209 .decl = decl,
1210 .arena = &gen_scope_arena.allocator,
1211 .parent = decl.scope,
1212 };
1213 defer gen_scope.instructions.deinit(self.gpa);
1214
1215 const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
1216 const src = tree.token_locs[type_node.firstToken()].start;
1217 const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
1218 .ty = Type.initTag(.type),
1219 .val = Value.initTag(.type_type),
1220 });
1221 const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
1222 break :rl .{ .ty = var_type };
1223 } else .none;
1224
1225 const src = tree.token_locs[init_node.firstToken()].start;
1226 const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
1227
1228 var inner_block: Scope.Block = .{
1229 .parent = null,
1230 .func = null,
1231 .decl = decl,
1232 .instructions = .{},
1233 .arena = &gen_scope_arena.allocator,
1234 .is_comptime = true,
1235 };
1236 defer inner_block.instructions.deinit(self.gpa);
1237 try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
1238
1239 // The result location guarantees the type coercion.
1240 const analyzed_init_inst = init_inst.analyzed_inst.?;
1241 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1242 const val = analyzed_init_inst.value().?;
1243
1244 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
1245 break :vi .{
1246 .ty = ty,
1247 .val = try val.copy(block_scope.arena),
1248 };
1249 } else if (!is_extern) {
1250 return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
1251 } else if (var_decl.getTypeNode()) |type_node| vi: {
1252 // Temporary arena for the zir instructions.
1253 var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1254 defer type_scope_arena.deinit();
1255 var type_scope: Scope.GenZIR = .{
1256 .decl = decl,
1257 .arena = &type_scope_arena.allocator,
1258 .parent = decl.scope,
1259 };
1260 defer type_scope.instructions.deinit(self.gpa);
1261
1262 const src = tree.token_locs[type_node.firstToken()].start;
1263 const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
1264 .ty = Type.initTag(.type),
1265 .val = Value.initTag(.type_type),
1266 });
1267 const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
1268 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
1269 .instructions = type_scope.instructions.items,
1270 });
1271 break :vi .{
1272 .ty = ty,
1273 .val = null,
1274 };
1275 } else {
1276 return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
1277 };
1278
1279 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
1280 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
1281 }
1282
1283 var type_changed = true;
1284 if (decl.typedValueManaged()) |tvm| {
1285 type_changed = !tvm.typed_value.ty.eql(var_info.ty);
1286
1287 tvm.deinit(self.gpa);
1288 }
1289
1290 const new_variable = try decl_arena.allocator.create(Var);
1291 const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
1292 new_variable.* = .{
1293 .owner_decl = decl,
1294 .init = var_info.val orelse undefined,
1295 .is_extern = is_extern,
1296 .is_mutable = is_mutable,
1297 .is_threadlocal = is_threadlocal,
1298 };
1299 var_payload.* = .{ .variable = new_variable };
1300
1301 decl_arena_state.* = decl_arena.state;
1302 decl.typed_value = .{
1303 .most_recent = .{
1304 .typed_value = .{
1305 .ty = var_info.ty,
1306 .val = Value.initPayload(&var_payload.base),
1307 },
1308 .arena = decl_arena_state,
1309 },
1310 };
1311 decl.analysis = .complete;
1312 decl.generation = self.generation;
1313
1314 if (var_decl.getExternExportToken()) |maybe_export_token| {
1315 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1316 const export_src = tree.token_locs[maybe_export_token].start;
1317 const name_loc = tree.token_locs[var_decl.name_token];
1318 const name = tree.tokenSliceLoc(name_loc);
1319 // The scope needs to have the decl in it.
1320 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1321 }
1322 }
1323 return type_changed;
1324 },
1325 .Comptime => {
1326 const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
1327
1328 decl.analysis = .in_progress;
1329
1330 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
1331 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
1332 defer analysis_arena.deinit();
1333 var gen_scope: Scope.GenZIR = .{
1334 .decl = decl,
1335 .arena = &analysis_arena.allocator,
1336 .parent = decl.scope,
1337 };
1338 defer gen_scope.instructions.deinit(self.gpa);
1339
1340 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
1341
1342 var block_scope: Scope.Block = .{
1343 .parent = null,
1344 .func = null,
1345 .decl = decl,
1346 .instructions = .{},
1347 .arena = &analysis_arena.allocator,
1348 .is_comptime = true,
1349 };
1350 defer block_scope.instructions.deinit(self.gpa);
1351
1352 _ = try zir_sema.analyzeBody(self, &block_scope.base, .{
1353 .instructions = gen_scope.instructions.items,
1354 });
1355
1356 decl.analysis = .complete;
1357 decl.generation = self.generation;
1358 return true;
1359 },
1360 .Use => @panic("TODO usingnamespace decl"),
1361 else => unreachable,
1362 }
1363}
1364
1365fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
1366 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
1367 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
1368
1369 depender.dependencies.putAssumeCapacity(dependee, {});
1370 dependee.dependants.putAssumeCapacity(depender, {});
1371}
1372
1373fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
1374 switch (root_scope.status) {
1375 .never_loaded, .unloaded_success => {
1376 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
1377
1378 const source = try root_scope.getSource(self);
1379
1380 var keep_zir_module = false;
1381 const zir_module = try self.gpa.create(zir.Module);
1382 defer if (!keep_zir_module) self.gpa.destroy(zir_module);
1383
1384 zir_module.* = try zir.parse(self.gpa, source);
1385 defer if (!keep_zir_module) zir_module.deinit(self.gpa);
1386
1387 if (zir_module.error_msg) |src_err_msg| {
1388 self.failed_files.putAssumeCapacityNoClobber(
1389 &root_scope.base,
1390 try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
1391 );
1392 root_scope.status = .unloaded_parse_failure;
1393 return error.AnalysisFail;
1394 }
1395
1396 root_scope.status = .loaded_success;
1397 root_scope.contents = .{ .module = zir_module };
1398 keep_zir_module = true;
1399
1400 return zir_module;
1401 },
1402
1403 .unloaded_parse_failure,
1404 .unloaded_sema_failure,
1405 => return error.AnalysisFail,
1406
1407 .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
1408 }
1409}
1410
1411fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
1412 const tracy = trace(@src());
1413 defer tracy.end();
1414
1415 const root_scope = container_scope.file_scope;
1416
1417 switch (root_scope.status) {
1418 .never_loaded, .unloaded_success => {
1419 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
1420
1421 const source = try root_scope.getSource(self);
1422
1423 var keep_tree = false;
1424 const tree = try std.zig.parse(self.gpa, source);
1425 defer if (!keep_tree) tree.deinit();
1426
1427 if (tree.errors.len != 0) {
1428 const parse_err = tree.errors[0];
1429
1430 var msg = std.ArrayList(u8).init(self.gpa);
1431 defer msg.deinit();
1432
1433 try parse_err.render(tree.token_ids, msg.outStream());
1434 const err_msg = try self.gpa.create(Compilation.ErrorMsg);
1435 err_msg.* = .{
1436 .msg = msg.toOwnedSlice(),
1437 .byte_offset = tree.token_locs[parse_err.loc()].start,
1438 };
1439
1440 self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
1441 root_scope.status = .unloaded_parse_failure;
1442 return error.AnalysisFail;
1443 }
1444
1445 root_scope.status = .loaded_success;
1446 root_scope.contents = .{ .tree = tree };
1447 keep_tree = true;
1448
1449 return tree;
1450 },
1451
1452 .unloaded_parse_failure => return error.AnalysisFail,
1453
1454 .loaded_success => return root_scope.contents.tree,
1455 }
1456}
1457
1458pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
1459 const tracy = trace(@src());
1460 defer tracy.end();
1461
1462 // We may be analyzing it for the first time, or this may be
1463 // an incremental update. This code handles both cases.
1464 const tree = try self.getAstTree(container_scope);
1465 const decls = tree.root_node.decls();
1466
1467 try self.comp.work_queue.ensureUnusedCapacity(decls.len);
1468 try container_scope.decls.ensureCapacity(self.gpa, decls.len);
1469
1470 // Keep track of the decls that we expect to see in this file so that
1471 // we know which ones have been deleted.
1472 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
1473 defer deleted_decls.deinit();
1474 try deleted_decls.ensureCapacity(container_scope.decls.items().len);
1475 for (container_scope.decls.items()) |entry| {
1476 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
1477 }
1478
1479 for (decls) |src_decl, decl_i| {
1480 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
1481 // We will create a Decl for it regardless of analysis status.
1482 const name_tok = fn_proto.getNameToken() orelse {
1483 @panic("TODO missing function name");
1484 };
1485
1486 const name_loc = tree.token_locs[name_tok];
1487 const name = tree.tokenSliceLoc(name_loc);
1488 const name_hash = container_scope.fullyQualifiedNameHash(name);
1489 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1490 if (self.decl_table.get(name_hash)) |decl| {
1491 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1492 // have been re-ordered.
1493 decl.src_index = decl_i;
1494 if (deleted_decls.remove(decl) == null) {
1495 decl.analysis = .sema_failure;
1496 const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
1497 errdefer err_msg.destroy(self.gpa);
1498 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
1499 } else {
1500 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1501 try self.markOutdatedDecl(decl);
1502 decl.contents_hash = contents_hash;
1503 } else switch (self.comp.bin_file.tag) {
1504 .coff => {
1505 // TODO Implement for COFF
1506 },
1507 .elf => if (decl.fn_link.elf.len != 0) {
1508 // TODO Look into detecting when this would be unnecessary by storing enough state
1509 // in `Decl` to notice that the line number did not change.
1510 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1511 },
1512 .macho => {
1513 // TODO Implement for MachO
1514 },
1515 .c, .wasm => {},
1516 }
1517 }
1518 } else {
1519 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1520 container_scope.decls.putAssumeCapacity(new_decl, {});
1521 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1522 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1523 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1524 }
1525 }
1526 }
1527 } else if (src_decl.castTag(.VarDecl)) |var_decl| {
1528 const name_loc = tree.token_locs[var_decl.name_token];
1529 const name = tree.tokenSliceLoc(name_loc);
1530 const name_hash = container_scope.fullyQualifiedNameHash(name);
1531 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1532 if (self.decl_table.get(name_hash)) |decl| {
1533 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1534 // have been re-ordered.
1535 decl.src_index = decl_i;
1536 if (deleted_decls.remove(decl) == null) {
1537 decl.analysis = .sema_failure;
1538 const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name});
1539 errdefer err_msg.destroy(self.gpa);
1540 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
1541 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
1542 try self.markOutdatedDecl(decl);
1543 decl.contents_hash = contents_hash;
1544 }
1545 } else {
1546 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1547 container_scope.decls.putAssumeCapacity(new_decl, {});
1548 if (var_decl.getExternExportToken()) |maybe_export_token| {
1549 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1550 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1551 }
1552 }
1553 }
1554 } else if (src_decl.castTag(.Comptime)) |comptime_node| {
1555 const name_index = self.getNextAnonNameIndex();
1556 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
1557 defer self.gpa.free(name);
1558
1559 const name_hash = container_scope.fullyQualifiedNameHash(name);
1560 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1561
1562 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1563 container_scope.decls.putAssumeCapacity(new_decl, {});
1564 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1565 } else if (src_decl.castTag(.ContainerField)) |container_field| {
1566 log.err("TODO: analyze container field", .{});
1567 } else if (src_decl.castTag(.TestDecl)) |test_decl| {
1568 log.err("TODO: analyze test decl", .{});
1569 } else if (src_decl.castTag(.Use)) |use_decl| {
1570 log.err("TODO: analyze usingnamespace decl", .{});
1571 } else {
1572 unreachable;
1573 }
1574 }
1575 // Handle explicitly deleted decls from the source code. Not to be confused
1576 // with when we delete decls because they are no longer referenced.
1577 for (deleted_decls.items()) |entry| {
1578 log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
1579 try self.deleteDecl(entry.key);
1580 }
1581}
1582
1583pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1584 // We may be analyzing it for the first time, or this may be
1585 // an incremental update. This code handles both cases.
1586 const src_module = try self.getSrcModule(root_scope);
1587
1588 try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len);
1589 try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
1590
1591 var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
1592 defer exports_to_resolve.deinit();
1593
1594 // Keep track of the decls that we expect to see in this file so that
1595 // we know which ones have been deleted.
1596 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
1597 defer deleted_decls.deinit();
1598 try deleted_decls.ensureCapacity(self.decl_table.items().len);
1599 for (self.decl_table.items()) |entry| {
1600 deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
1601 }
1602
1603 for (src_module.decls) |src_decl, decl_i| {
1604 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
1605 if (self.decl_table.get(name_hash)) |decl| {
1606 deleted_decls.removeAssertDiscard(decl);
1607 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
1608 try self.markOutdatedDecl(decl);
1609 decl.contents_hash = src_decl.contents_hash;
1610 }
1611 } else {
1612 const new_decl = try self.createNewDecl(
1613 &root_scope.base,
1614 src_decl.name,
1615 decl_i,
1616 name_hash,
1617 src_decl.contents_hash,
1618 );
1619 root_scope.decls.appendAssumeCapacity(new_decl);
1620 if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
1621 try exports_to_resolve.append(src_decl);
1622 }
1623 }
1624 }
1625 for (exports_to_resolve.items) |export_decl| {
1626 _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
1627 }
1628 // Handle explicitly deleted decls from the source code. Not to be confused
1629 // with when we delete decls because they are no longer referenced.
1630 for (deleted_decls.items()) |entry| {
1631 log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
1632 try self.deleteDecl(entry.key);
1633 }
1634}
1635
1636pub fn deleteDecl(self: *Module, decl: *Decl) !void {
1637 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
1638
1639 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
1640 // not be present in the set, and this does nothing.
1641 decl.scope.removeDecl(decl);
1642
1643 log.debug("deleting decl '{}'\n", .{decl.name});
1644 const name_hash = decl.fullyQualifiedNameHash();
1645 self.decl_table.removeAssertDiscard(name_hash);
1646 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
1647 for (decl.dependencies.items()) |entry| {
1648 const dep = entry.key;
1649 dep.removeDependant(decl);
1650 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
1651 // We don't recursively perform a deletion here, because during the update,
1652 // another reference to it may turn up.
1653 dep.deletion_flag = true;
1654 self.deletion_set.appendAssumeCapacity(dep);
1655 }
1656 }
1657 // Anything that depends on this deleted decl certainly needs to be re-analyzed.
1658 for (decl.dependants.items()) |entry| {
1659 const dep = entry.key;
1660 dep.removeDependency(decl);
1661 if (dep.analysis != .outdated) {
1662 // TODO Move this failure possibility to the top of the function.
1663 try self.markOutdatedDecl(dep);
1664 }
1665 }
1666 if (self.failed_decls.remove(decl)) |entry| {
1667 entry.value.destroy(self.gpa);
1668 }
1669 self.deleteDeclExports(decl);
1670 self.comp.bin_file.freeDecl(decl);
1671 decl.destroy(self.gpa);
1672}
1673
1674/// Delete all the Export objects that are caused by this Decl. Re-analysis of
1675/// this Decl will cause them to be re-created (or not).
1676fn deleteDeclExports(self: *Module, decl: *Decl) void {
1677 const kv = self.export_owners.remove(decl) orelse return;
1678
1679 for (kv.value) |exp| {
1680 if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
1681 // Remove exports with owner_decl matching the regenerating decl.
1682 const list = decl_exports_kv.value;
1683 var i: usize = 0;
1684 var new_len = list.len;
1685 while (i < new_len) {
1686 if (list[i].owner_decl == decl) {
1687 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
1688 new_len -= 1;
1689 } else {
1690 i += 1;
1691 }
1692 }
1693 decl_exports_kv.value = self.gpa.shrink(list, new_len);
1694 if (new_len == 0) {
1695 self.decl_exports.removeAssertDiscard(exp.exported_decl);
1696 }
1697 }
1698 if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
1699 elf.deleteExport(exp.link);
1700 }
1701 if (self.failed_exports.remove(exp)) |entry| {
1702 entry.value.destroy(self.gpa);
1703 }
1704 _ = self.symbol_exports.remove(exp.options.name);
1705 self.gpa.free(exp.options.name);
1706 self.gpa.destroy(exp);
1707 }
1708 self.gpa.free(kv.value);
1709}
1710
1711pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1712 const tracy = trace(@src());
1713 defer tracy.end();
1714
1715 // Use the Decl's arena for function memory.
1716 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
1717 defer decl.typed_value.most_recent.arena.?.* = arena.state;
1718 var inner_block: Scope.Block = .{
1719 .parent = null,
1720 .func = func,
1721 .decl = decl,
1722 .instructions = .{},
1723 .arena = &arena.allocator,
1724 .is_comptime = false,
1725 };
1726 defer inner_block.instructions.deinit(self.gpa);
1727
1728 const fn_zir = func.analysis.queued;
1729 defer fn_zir.arena.promote(self.gpa).deinit();
1730 func.analysis = .{ .in_progress = {} };
1731 log.debug("set {} to in_progress\n", .{decl.name});
1732
1733 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
1734
1735 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1736 func.analysis = .{ .success = .{ .instructions = instructions } };
1737 log.debug("set {} to success\n", .{decl.name});
1738}
1739
1740fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1741 log.debug("mark {} outdated\n", .{decl.name});
1742 try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
1743 if (self.failed_decls.remove(decl)) |entry| {
1744 entry.value.destroy(self.gpa);
1745 }
1746 decl.analysis = .outdated;
1747}
1748
1749fn allocateNewDecl(
1750 self: *Module,
1751 scope: *Scope,
1752 src_index: usize,
1753 contents_hash: std.zig.SrcHash,
1754) !*Decl {
1755 const new_decl = try self.gpa.create(Decl);
1756 new_decl.* = .{
1757 .name = "",
1758 .scope = scope.namespace(),
1759 .src_index = src_index,
1760 .typed_value = .{ .never_succeeded = {} },
1761 .analysis = .unreferenced,
1762 .deletion_flag = false,
1763 .contents_hash = contents_hash,
1764 .link = switch (self.comp.bin_file.tag) {
1765 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
1766 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
1767 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
1768 .c => .{ .c = {} },
1769 .wasm => .{ .wasm = {} },
1770 },
1771 .fn_link = switch (self.comp.bin_file.tag) {
1772 .coff => .{ .coff = {} },
1773 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
1774 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
1775 .c => .{ .c = {} },
1776 .wasm => .{ .wasm = null },
1777 },
1778 .generation = 0,
1779 .is_pub = false,
1780 };
1781 return new_decl;
1782}
1783
1784fn createNewDecl(
1785 self: *Module,
1786 scope: *Scope,
1787 decl_name: []const u8,
1788 src_index: usize,
1789 name_hash: Scope.NameHash,
1790 contents_hash: std.zig.SrcHash,
1791) !*Decl {
1792 try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
1793 const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
1794 errdefer self.gpa.destroy(new_decl);
1795 new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
1796 self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
1797 return new_decl;
1798}
1799
1800/// Get error value for error tag `name`.
1801pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
1802 const gop = try self.global_error_set.getOrPut(self.gpa, name);
1803 if (gop.found_existing)
1804 return gop.entry.*;
1805 errdefer self.global_error_set.removeAssertDiscard(name);
1806
1807 gop.entry.key = try self.gpa.dupe(u8, name);
1808 gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
1809 return gop.entry.*;
1810}
1811
1812pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
1813 return scope.cast(Scope.Block) orelse
1814 return self.fail(scope, src, "instruction illegal outside function body", .{});
1815}
1816
1817pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
1818 const block = try self.requireFunctionBlock(scope, src);
1819 if (block.is_comptime) {
1820 return self.fail(scope, src, "unable to resolve comptime value", .{});
1821 }
1822 return block;
1823}
1824
1825pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
1826 return (try self.resolveDefinedValue(scope, base)) orelse
1827 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
1828}
1829
1830pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1831 if (base.value()) |val| {
1832 if (val.isUndef()) {
1833 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
1834 }
1835 return val;
1836 }
1837 return null;
1838}
1839
1840pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
1841 try self.ensureDeclAnalyzed(exported_decl);
1842 const typed_value = exported_decl.typed_value.most_recent.typed_value;
1843 switch (typed_value.ty.zigTypeTag()) {
1844 .Fn => {},
1845 else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
1846 }
1847
1848 try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
1849 try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
1850
1851 const new_export = try self.gpa.create(Export);
1852 errdefer self.gpa.destroy(new_export);
1853
1854 const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
1855 errdefer self.gpa.free(symbol_name);
1856
1857 const owner_decl = scope.decl().?;
1858
1859 new_export.* = .{
1860 .options = .{ .name = symbol_name },
1861 .src = src,
1862 .link = .{},
1863 .owner_decl = owner_decl,
1864 .exported_decl = exported_decl,
1865 .status = .in_progress,
1866 };
1867
1868 // Add to export_owners table.
1869 const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
1870 if (!eo_gop.found_existing) {
1871 eo_gop.entry.value = &[0]*Export{};
1872 }
1873 eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
1874 eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
1875 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
1876
1877 // Add to exported_decl table.
1878 const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
1879 if (!de_gop.found_existing) {
1880 de_gop.entry.value = &[0]*Export{};
1881 }
1882 de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
1883 de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
1884 errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
1885
1886 if (self.symbol_exports.get(symbol_name)) |_| {
1887 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
1888 self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
1889 self.gpa,
1890 src,
1891 "exported symbol collision: {}",
1892 .{symbol_name},
1893 ));
1894 // TODO: add a note
1895 new_export.status = .failed;
1896 return;
1897 }
1898
1899 try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
1900 self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
1901 error.OutOfMemory => return error.OutOfMemory,
1902 else => {
1903 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
1904 self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
1905 self.gpa,
1906 src,
1907 "unable to export: {}",
1908 .{@errorName(err)},
1909 ));
1910 new_export.status = .failed_retryable;
1911 },
1912 };
1913}
1914
1915pub fn addNoOp(
1916 self: *Module,
1917 block: *Scope.Block,
1918 src: usize,
1919 ty: Type,
1920 comptime tag: Inst.Tag,
1921) !*Inst {
1922 const inst = try block.arena.create(tag.Type());
1923 inst.* = .{
1924 .base = .{
1925 .tag = tag,
1926 .ty = ty,
1927 .src = src,
1928 },
1929 };
1930 try block.instructions.append(self.gpa, &inst.base);
1931 return &inst.base;
1932}
1933
1934pub fn addUnOp(
1935 self: *Module,
1936 block: *Scope.Block,
1937 src: usize,
1938 ty: Type,
1939 tag: Inst.Tag,
1940 operand: *Inst,
1941) !*Inst {
1942 const inst = try block.arena.create(Inst.UnOp);
1943 inst.* = .{
1944 .base = .{
1945 .tag = tag,
1946 .ty = ty,
1947 .src = src,
1948 },
1949 .operand = operand,
1950 };
1951 try block.instructions.append(self.gpa, &inst.base);
1952 return &inst.base;
1953}
1954
1955pub fn addBinOp(
1956 self: *Module,
1957 block: *Scope.Block,
1958 src: usize,
1959 ty: Type,
1960 tag: Inst.Tag,
1961 lhs: *Inst,
1962 rhs: *Inst,
1963) !*Inst {
1964 const inst = try block.arena.create(Inst.BinOp);
1965 inst.* = .{
1966 .base = .{
1967 .tag = tag,
1968 .ty = ty,
1969 .src = src,
1970 },
1971 .lhs = lhs,
1972 .rhs = rhs,
1973 };
1974 try block.instructions.append(self.gpa, &inst.base);
1975 return &inst.base;
1976}
1977
1978pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
1979 const inst = try block.arena.create(Inst.Arg);
1980 inst.* = .{
1981 .base = .{
1982 .tag = .arg,
1983 .ty = ty,
1984 .src = src,
1985 },
1986 .name = name,
1987 };
1988 try block.instructions.append(self.gpa, &inst.base);
1989 return &inst.base;
1990}
1991
1992pub fn addBr(
1993 self: *Module,
1994 scope_block: *Scope.Block,
1995 src: usize,
1996 target_block: *Inst.Block,
1997 operand: *Inst,
1998) !*Inst {
1999 const inst = try scope_block.arena.create(Inst.Br);
2000 inst.* = .{
2001 .base = .{
2002 .tag = .br,
2003 .ty = Type.initTag(.noreturn),
2004 .src = src,
2005 },
2006 .operand = operand,
2007 .block = target_block,
2008 };
2009 try scope_block.instructions.append(self.gpa, &inst.base);
2010 return &inst.base;
2011}
2012
2013pub fn addCondBr(
2014 self: *Module,
2015 block: *Scope.Block,
2016 src: usize,
2017 condition: *Inst,
2018 then_body: ir.Body,
2019 else_body: ir.Body,
2020) !*Inst {
2021 const inst = try block.arena.create(Inst.CondBr);
2022 inst.* = .{
2023 .base = .{
2024 .tag = .condbr,
2025 .ty = Type.initTag(.noreturn),
2026 .src = src,
2027 },
2028 .condition = condition,
2029 .then_body = then_body,
2030 .else_body = else_body,
2031 };
2032 try block.instructions.append(self.gpa, &inst.base);
2033 return &inst.base;
2034}
2035
2036pub fn addCall(
2037 self: *Module,
2038 block: *Scope.Block,
2039 src: usize,
2040 ty: Type,
2041 func: *Inst,
2042 args: []const *Inst,
2043) !*Inst {
2044 const inst = try block.arena.create(Inst.Call);
2045 inst.* = .{
2046 .base = .{
2047 .tag = .call,
2048 .ty = ty,
2049 .src = src,
2050 },
2051 .func = func,
2052 .args = args,
2053 };
2054 try block.instructions.append(self.gpa, &inst.base);
2055 return &inst.base;
2056}
2057
2058pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
2059 const const_inst = try scope.arena().create(Inst.Constant);
2060 const_inst.* = .{
2061 .base = .{
2062 .tag = Inst.Constant.base_tag,
2063 .ty = typed_value.ty,
2064 .src = src,
2065 },
2066 .val = typed_value.val,
2067 };
2068 return &const_inst.base;
2069}
2070
2071pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2072 return self.constInst(scope, src, .{
2073 .ty = Type.initTag(.type),
2074 .val = try ty.toValue(scope.arena()),
2075 });
2076}
2077
2078pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
2079 return self.constInst(scope, src, .{
2080 .ty = Type.initTag(.void),
2081 .val = Value.initTag(.void_value),
2082 });
2083}
2084
2085pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
2086 return self.constInst(scope, src, .{
2087 .ty = Type.initTag(.noreturn),
2088 .val = Value.initTag(.unreachable_value),
2089 });
2090}
2091
2092pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2093 return self.constInst(scope, src, .{
2094 .ty = ty,
2095 .val = Value.initTag(.undef),
2096 });
2097}
2098
2099pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
2100 return self.constInst(scope, src, .{
2101 .ty = Type.initTag(.bool),
2102 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
2103 });
2104}
2105
2106pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
2107 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
2108 int_payload.* = .{ .int = int };
2109
2110 return self.constInst(scope, src, .{
2111 .ty = ty,
2112 .val = Value.initPayload(&int_payload.base),
2113 });
2114}
2115
2116pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
2117 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
2118 int_payload.* = .{ .int = int };
2119
2120 return self.constInst(scope, src, .{
2121 .ty = ty,
2122 .val = Value.initPayload(&int_payload.base),
2123 });
2124}
2125
2126pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
2127 const val_payload = if (big_int.positive) blk: {
2128 if (big_int.to(u64)) |x| {
2129 return self.constIntUnsigned(scope, src, ty, x);
2130 } else |err| switch (err) {
2131 error.NegativeIntoUnsigned => unreachable,
2132 error.TargetTooSmall => {}, // handled below
2133 }
2134 const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
2135 big_int_payload.* = .{ .limbs = big_int.limbs };
2136 break :blk &big_int_payload.base;
2137 } else blk: {
2138 if (big_int.to(i64)) |x| {
2139 return self.constIntSigned(scope, src, ty, x);
2140 } else |err| switch (err) {
2141 error.NegativeIntoUnsigned => unreachable,
2142 error.TargetTooSmall => {}, // handled below
2143 }
2144 const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
2145 big_int_payload.* = .{ .limbs = big_int.limbs };
2146 break :blk &big_int_payload.base;
2147 };
2148
2149 return self.constInst(scope, src, .{
2150 .ty = ty,
2151 .val = Value.initPayload(val_payload),
2152 });
2153}
2154
2155pub fn createAnonymousDecl(
2156 self: *Module,
2157 scope: *Scope,
2158 decl_arena: *std.heap.ArenaAllocator,
2159 typed_value: TypedValue,
2160) !*Decl {
2161 const name_index = self.getNextAnonNameIndex();
2162 const scope_decl = scope.decl().?;
2163 const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
2164 defer self.gpa.free(name);
2165 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
2166 const src_hash: std.zig.SrcHash = undefined;
2167 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
2168 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
2169
2170 decl_arena_state.* = decl_arena.state;
2171 new_decl.typed_value = .{
2172 .most_recent = .{
2173 .typed_value = typed_value,
2174 .arena = decl_arena_state,
2175 },
2176 };
2177 new_decl.analysis = .complete;
2178 new_decl.generation = self.generation;
2179
2180 // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
2181 // We should be able to further improve the compiler to not omit Decls which are only referenced at
2182 // compile-time and not runtime.
2183 if (typed_value.ty.hasCodeGenBits()) {
2184 try self.comp.bin_file.allocateDeclIndexes(new_decl);
2185 try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
2186 }
2187
2188 return new_decl;
2189}
2190
2191fn getNextAnonNameIndex(self: *Module) usize {
2192 return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
2193}
2194
2195pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
2196 const namespace = scope.namespace();
2197 const name_hash = namespace.fullyQualifiedNameHash(ident_name);
2198 return self.decl_table.get(name_hash);
2199}
2200
2201pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2202 const scope_decl = scope.decl().?;
2203 try self.declareDeclDependency(scope_decl, decl);
2204 self.ensureDeclAnalyzed(decl) catch |err| {
2205 if (scope.cast(Scope.Block)) |block| {
2206 if (block.func) |func| {
2207 func.analysis = .dependency_failure;
2208 } else {
2209 block.decl.analysis = .dependency_failure;
2210 }
2211 } else {
2212 scope_decl.analysis = .dependency_failure;
2213 }
2214 return err;
2215 };
2216
2217 const decl_tv = try decl.typedValue();
2218 if (decl_tv.val.tag() == .variable) {
2219 return self.analyzeVarRef(scope, src, decl_tv);
2220 }
2221 const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One);
2222 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
2223 val_payload.* = .{ .decl = decl };
2224
2225 return self.constInst(scope, src, .{
2226 .ty = ty,
2227 .val = Value.initPayload(&val_payload.base),
2228 });
2229}
2230
2231fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
2232 const variable = tv.val.cast(Value.Payload.Variable).?.variable;
2233
2234 const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
2235 if (!variable.is_mutable and !variable.is_extern) {
2236 const val_payload = try scope.arena().create(Value.Payload.RefVal);
2237 val_payload.* = .{ .val = variable.init };
2238 return self.constInst(scope, src, .{
2239 .ty = ty,
2240 .val = Value.initPayload(&val_payload.base),
2241 });
2242 }
2243
2244 const b = try self.requireRuntimeBlock(scope, src);
2245 const inst = try b.arena.create(Inst.VarPtr);
2246 inst.* = .{
2247 .base = .{
2248 .tag = .varptr,
2249 .ty = ty,
2250 .src = src,
2251 },
2252 .variable = variable,
2253 };
2254 try b.instructions.append(self.gpa, &inst.base);
2255 return &inst.base;
2256}
2257
2258pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
2259 const elem_ty = switch (ptr.ty.zigTypeTag()) {
2260 .Pointer => ptr.ty.elemType(),
2261 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
2262 };
2263 if (ptr.value()) |val| {
2264 return self.constInst(scope, src, .{
2265 .ty = elem_ty,
2266 .val = try val.pointerDeref(scope.arena()),
2267 });
2268 }
2269
2270 const b = try self.requireRuntimeBlock(scope, src);
2271 return self.addUnOp(b, src, elem_ty, .load, ptr);
2272}
2273
2274pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
2275 const decl = self.lookupDeclName(scope, decl_name) orelse
2276 return self.fail(scope, src, "decl '{}' not found", .{decl_name});
2277 return self.analyzeDeclRef(scope, src, decl);
2278}
2279
2280pub fn wantSafety(self: *Module, scope: *Scope) bool {
2281 // TODO take into account scope's safety overrides
2282 return switch (self.optimizeMode()) {
2283 .Debug => true,
2284 .ReleaseSafe => true,
2285 .ReleaseFast => false,
2286 .ReleaseSmall => false,
2287 };
2288}
2289
2290pub fn analyzeIsNull(
2291 self: *Module,
2292 scope: *Scope,
2293 src: usize,
2294 operand: *Inst,
2295 invert_logic: bool,
2296) InnerError!*Inst {
2297 if (operand.value()) |opt_val| {
2298 const is_null = opt_val.isNull();
2299 const bool_value = if (invert_logic) !is_null else is_null;
2300 return self.constBool(scope, src, bool_value);
2301 }
2302 const b = try self.requireRuntimeBlock(scope, src);
2303 const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull;
2304 return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
2305}
2306
2307pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
2308 return self.fail(scope, src, "TODO implement analysis of iserr", .{});
2309}
2310
2311pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
2312 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
2313 .Pointer => array_ptr.ty.elemType(),
2314 else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
2315 };
2316
2317 var array_type = ptr_child;
2318 const elem_type = switch (ptr_child.zigTypeTag()) {
2319 .Array => ptr_child.elemType(),
2320 .Pointer => blk: {
2321 if (ptr_child.isSinglePointer()) {
2322 if (ptr_child.elemType().zigTypeTag() == .Array) {
2323 array_type = ptr_child.elemType();
2324 break :blk ptr_child.elemType().elemType();
2325 }
2326
2327 return self.fail(scope, src, "slice of single-item pointer", .{});
2328 }
2329 break :blk ptr_child.elemType();
2330 },
2331 else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
2332 };
2333
2334 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
2335 const casted = try self.coerce(scope, elem_type, sentinel);
2336 break :blk try self.resolveConstValue(scope, casted);
2337 } else null;
2338
2339 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
2340 var return_elem_type = elem_type;
2341 if (end_opt) |end| {
2342 if (end.value()) |end_val| {
2343 if (start.value()) |start_val| {
2344 const start_u64 = start_val.toUnsignedInt();
2345 const end_u64 = end_val.toUnsignedInt();
2346 if (start_u64 > end_u64) {
2347 return self.fail(scope, src, "out of bounds slice", .{});
2348 }
2349
2350 const len = end_u64 - start_u64;
2351 const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
2352 array_type.sentinel()
2353 else
2354 slice_sentinel;
2355 return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
2356 return_ptr_size = .One;
2357 }
2358 }
2359 }
2360 const return_type = try self.ptrType(
2361 scope,
2362 src,
2363 return_elem_type,
2364 if (end_opt == null) slice_sentinel else null,
2365 0, // TODO alignment
2366 0,
2367 0,
2368 !ptr_child.isConstPtr(),
2369 ptr_child.isAllowzeroPtr(),
2370 ptr_child.isVolatilePtr(),
2371 return_ptr_size,
2372 );
2373
2374 return self.fail(scope, src, "TODO implement analysis of slice", .{});
2375}
2376
2377/// Asserts that lhs and rhs types are both numeric.
2378pub fn cmpNumeric(
2379 self: *Module,
2380 scope: *Scope,
2381 src: usize,
2382 lhs: *Inst,
2383 rhs: *Inst,
2384 op: std.math.CompareOperator,
2385) !*Inst {
2386 assert(lhs.ty.isNumeric());
2387 assert(rhs.ty.isNumeric());
2388
2389 const lhs_ty_tag = lhs.ty.zigTypeTag();
2390 const rhs_ty_tag = rhs.ty.zigTypeTag();
2391
2392 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
2393 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
2394 return self.fail(scope, src, "vector length mismatch: {} and {}", .{
2395 lhs.ty.arrayLen(),
2396 rhs.ty.arrayLen(),
2397 });
2398 }
2399 return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
2400 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
2401 return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
2402 lhs.ty,
2403 rhs.ty,
2404 });
2405 }
2406
2407 if (lhs.value()) |lhs_val| {
2408 if (rhs.value()) |rhs_val| {
2409 return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
2410 }
2411 }
2412
2413 // TODO handle comparisons against lazy zero values
2414 // Some values can be compared against zero without being runtime known or without forcing
2415 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
2416 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
2417 // of this function if we don't need to.
2418
2419 // It must be a runtime comparison.
2420 const b = try self.requireRuntimeBlock(scope, src);
2421 // For floats, emit a float comparison instruction.
2422 const lhs_is_float = switch (lhs_ty_tag) {
2423 .Float, .ComptimeFloat => true,
2424 else => false,
2425 };
2426 const rhs_is_float = switch (rhs_ty_tag) {
2427 .Float, .ComptimeFloat => true,
2428 else => false,
2429 };
2430 if (lhs_is_float and rhs_is_float) {
2431 // Implicit cast the smaller one to the larger one.
2432 const dest_type = x: {
2433 if (lhs_ty_tag == .ComptimeFloat) {
2434 break :x rhs.ty;
2435 } else if (rhs_ty_tag == .ComptimeFloat) {
2436 break :x lhs.ty;
2437 }
2438 if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
2439 break :x lhs.ty;
2440 } else {
2441 break :x rhs.ty;
2442 }
2443 };
2444 const casted_lhs = try self.coerce(scope, dest_type, lhs);
2445 const casted_rhs = try self.coerce(scope, dest_type, rhs);
2446 return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
2447 }
2448 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
2449 // For mixed signed and unsigned integers, implicit cast both operands to a signed
2450 // integer with + 1 bit.
2451 // For mixed floats and integers, extract the integer part from the float, cast that to
2452 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
2453 // add/subtract 1.
2454 const lhs_is_signed = if (lhs.value()) |lhs_val|
2455 lhs_val.compareWithZero(.lt)
2456 else
2457 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
2458 const rhs_is_signed = if (rhs.value()) |rhs_val|
2459 rhs_val.compareWithZero(.lt)
2460 else
2461 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
2462 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
2463
2464 var dest_float_type: ?Type = null;
2465
2466 var lhs_bits: usize = undefined;
2467 if (lhs.value()) |lhs_val| {
2468 if (lhs_val.isUndef())
2469 return self.constUndef(scope, src, Type.initTag(.bool));
2470 const is_unsigned = if (lhs_is_float) x: {
2471 var bigint_space: Value.BigIntSpace = undefined;
2472 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
2473 defer bigint.deinit();
2474 const zcmp = lhs_val.orderAgainstZero();
2475 if (lhs_val.floatHasFraction()) {
2476 switch (op) {
2477 .eq => return self.constBool(scope, src, false),
2478 .neq => return self.constBool(scope, src, true),
2479 else => {},
2480 }
2481 if (zcmp == .lt) {
2482 try bigint.addScalar(bigint.toConst(), -1);
2483 } else {
2484 try bigint.addScalar(bigint.toConst(), 1);
2485 }
2486 }
2487 lhs_bits = bigint.toConst().bitCountTwosComp();
2488 break :x (zcmp != .lt);
2489 } else x: {
2490 lhs_bits = lhs_val.intBitCountTwosComp();
2491 break :x (lhs_val.orderAgainstZero() != .lt);
2492 };
2493 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
2494 } else if (lhs_is_float) {
2495 dest_float_type = lhs.ty;
2496 } else {
2497 const int_info = lhs.ty.intInfo(self.getTarget());
2498 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
2499 }
2500
2501 var rhs_bits: usize = undefined;
2502 if (rhs.value()) |rhs_val| {
2503 if (rhs_val.isUndef())
2504 return self.constUndef(scope, src, Type.initTag(.bool));
2505 const is_unsigned = if (rhs_is_float) x: {
2506 var bigint_space: Value.BigIntSpace = undefined;
2507 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
2508 defer bigint.deinit();
2509 const zcmp = rhs_val.orderAgainstZero();
2510 if (rhs_val.floatHasFraction()) {
2511 switch (op) {
2512 .eq => return self.constBool(scope, src, false),
2513 .neq => return self.constBool(scope, src, true),
2514 else => {},
2515 }
2516 if (zcmp == .lt) {
2517 try bigint.addScalar(bigint.toConst(), -1);
2518 } else {
2519 try bigint.addScalar(bigint.toConst(), 1);
2520 }
2521 }
2522 rhs_bits = bigint.toConst().bitCountTwosComp();
2523 break :x (zcmp != .lt);
2524 } else x: {
2525 rhs_bits = rhs_val.intBitCountTwosComp();
2526 break :x (rhs_val.orderAgainstZero() != .lt);
2527 };
2528 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
2529 } else if (rhs_is_float) {
2530 dest_float_type = rhs.ty;
2531 } else {
2532 const int_info = rhs.ty.intInfo(self.getTarget());
2533 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
2534 }
2535
2536 const dest_type = if (dest_float_type) |ft| ft else blk: {
2537 const max_bits = std.math.max(lhs_bits, rhs_bits);
2538 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
2539 error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
2540 };
2541 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
2542 };
2543 const casted_lhs = try self.coerce(scope, dest_type, lhs);
2544 const casted_rhs = try self.coerce(scope, dest_type, rhs);
2545
2546 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
2547}
2548
2549fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2550 if (inst.value()) |val| {
2551 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2552 }
2553
2554 const b = try self.requireRuntimeBlock(scope, inst.src);
2555 return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
2556}
2557
2558fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
2559 if (signed) {
2560 const int_payload = try scope.arena().create(Type.Payload.IntSigned);
2561 int_payload.* = .{ .bits = bits };
2562 return Type.initPayload(&int_payload.base);
2563 } else {
2564 const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
2565 int_payload.* = .{ .bits = bits };
2566 return Type.initPayload(&int_payload.base);
2567 }
2568}
2569
2570pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
2571 if (instructions.len == 0)
2572 return Type.initTag(.noreturn);
2573
2574 if (instructions.len == 1)
2575 return instructions[0].ty;
2576
2577 var prev_inst = instructions[0];
2578 for (instructions[1..]) |next_inst| {
2579 if (next_inst.ty.eql(prev_inst.ty))
2580 continue;
2581 if (next_inst.ty.zigTypeTag() == .NoReturn)
2582 continue;
2583 if (prev_inst.ty.zigTypeTag() == .NoReturn) {
2584 prev_inst = next_inst;
2585 continue;
2586 }
2587 if (next_inst.ty.zigTypeTag() == .Undefined)
2588 continue;
2589 if (prev_inst.ty.zigTypeTag() == .Undefined) {
2590 prev_inst = next_inst;
2591 continue;
2592 }
2593 if (prev_inst.ty.isInt() and
2594 next_inst.ty.isInt() and
2595 prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
2596 {
2597 if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {
2598 prev_inst = next_inst;
2599 }
2600 continue;
2601 }
2602 if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
2603 if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
2604 prev_inst = next_inst;
2605 }
2606 continue;
2607 }
2608
2609 // TODO error notes pointing out each type
2610 return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
2611 }
2612
2613 return prev_inst.ty;
2614}
2615
2616pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2617 // If the types are the same, we can return the operand.
2618 if (dest_type.eql(inst.ty))
2619 return inst;
2620
2621 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
2622 if (in_memory_result == .ok) {
2623 return self.bitcast(scope, dest_type, inst);
2624 }
2625
2626 // undefined to anything
2627 if (inst.value()) |val| {
2628 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
2629 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2630 }
2631 }
2632 assert(inst.ty.zigTypeTag() != .Undefined);
2633
2634 // null to ?T
2635 if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
2636 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
2637 }
2638
2639 // T to ?T
2640 if (dest_type.zigTypeTag() == .Optional) {
2641 var buf: Type.Payload.PointerSimple = undefined;
2642 const child_type = dest_type.optionalChild(&buf);
2643 if (child_type.eql(inst.ty)) {
2644 return self.wrapOptional(scope, dest_type, inst);
2645 } else if (try self.coerceNum(scope, child_type, inst)) |some| {
2646 return self.wrapOptional(scope, dest_type, some);
2647 }
2648 }
2649
2650 // *[N]T to []T
2651 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
2652 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
2653 {
2654 const array_type = inst.ty.elemType();
2655 const dst_elem_type = dest_type.elemType();
2656 if (array_type.zigTypeTag() == .Array and
2657 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
2658 {
2659 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
2660 }
2661 }
2662
2663 // comptime known number to other number
2664 if (try self.coerceNum(scope, dest_type, inst)) |some|
2665 return some;
2666
2667 // integer widening
2668 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
2669 assert(inst.value() == null); // handled above
2670
2671 const src_info = inst.ty.intInfo(self.getTarget());
2672 const dst_info = dest_type.intInfo(self.getTarget());
2673 if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
2674 // small enough unsigned ints can get casted to large enough signed ints
2675 (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
2676 {
2677 const b = try self.requireRuntimeBlock(scope, inst.src);
2678 return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
2679 }
2680 }
2681
2682 // float widening
2683 if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
2684 assert(inst.value() == null); // handled above
2685
2686 const src_bits = inst.ty.floatBits(self.getTarget());
2687 const dst_bits = dest_type.floatBits(self.getTarget());
2688 if (dst_bits >= src_bits) {
2689 const b = try self.requireRuntimeBlock(scope, inst.src);
2690 return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
2691 }
2692 }
2693
2694 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
2695}
2696
2697pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
2698 const val = inst.value() orelse return null;
2699 const src_zig_tag = inst.ty.zigTypeTag();
2700 const dst_zig_tag = dest_type.zigTypeTag();
2701
2702 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
2703 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2704 if (val.floatHasFraction()) {
2705 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
2706 }
2707 return self.fail(scope, inst.src, "TODO float to int", .{});
2708 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2709 if (!val.intFitsInType(dest_type, self.getTarget())) {
2710 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
2711 }
2712 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2713 }
2714 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
2715 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2716 const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) {
2717 error.Overflow => return self.fail(
2718 scope,
2719 inst.src,
2720 "cast of value {} to type '{}' loses information",
2721 .{ val, dest_type },
2722 ),
2723 error.OutOfMemory => return error.OutOfMemory,
2724 };
2725 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
2726 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2727 return self.fail(scope, inst.src, "TODO int to float", .{});
2728 }
2729 }
2730 return null;
2731}
2732
2733pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
2734 if (ptr.ty.isConstPtr())
2735 return self.fail(scope, src, "cannot assign to constant", .{});
2736
2737 const elem_ty = ptr.ty.elemType();
2738 const value = try self.coerce(scope, elem_ty, uncasted_value);
2739 if (elem_ty.onePossibleValue() != null)
2740 return self.constVoid(scope, src);
2741
2742 // TODO handle comptime pointer writes
2743 // TODO handle if the element type requires comptime
2744
2745 const b = try self.requireRuntimeBlock(scope, src);
2746 return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
2747}
2748
2749pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2750 if (inst.value()) |val| {
2751 // Keep the comptime Value representation; take the new type.
2752 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2753 }
2754 // TODO validate the type size and other compile errors
2755 const b = try self.requireRuntimeBlock(scope, inst.src);
2756 return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
2757}
2758
2759fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2760 if (inst.value()) |val| {
2761 // The comptime Value representation is compatible with both types.
2762 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2763 }
2764 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
2765}
2766
2767pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
2768 @setCold(true);
2769 const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
2770 return self.failWithOwnedErrorMsg(scope, src, err_msg);
2771}
2772
2773pub fn failTok(
2774 self: *Module,
2775 scope: *Scope,
2776 token_index: ast.TokenIndex,
2777 comptime format: []const u8,
2778 args: anytype,
2779) InnerError {
2780 @setCold(true);
2781 const src = scope.tree().token_locs[token_index].start;
2782 return self.fail(scope, src, format, args);
2783}
2784
2785pub fn failNode(
2786 self: *Module,
2787 scope: *Scope,
2788 ast_node: *ast.Node,
2789 comptime format: []const u8,
2790 args: anytype,
2791) InnerError {
2792 @setCold(true);
2793 const src = scope.tree().token_locs[ast_node.firstToken()].start;
2794 return self.fail(scope, src, format, args);
2795}
2796
2797fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError {
2798 {
2799 errdefer err_msg.destroy(self.gpa);
2800 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
2801 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
2802 }
2803 switch (scope.tag) {
2804 .decl => {
2805 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
2806 decl.analysis = .sema_failure;
2807 decl.generation = self.generation;
2808 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
2809 },
2810 .block => {
2811 const block = scope.cast(Scope.Block).?;
2812 if (block.func) |func| {
2813 func.analysis = .sema_failure;
2814 } else {
2815 block.decl.analysis = .sema_failure;
2816 block.decl.generation = self.generation;
2817 }
2818 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
2819 },
2820 .gen_zir => {
2821 const gen_zir = scope.cast(Scope.GenZIR).?;
2822 gen_zir.decl.analysis = .sema_failure;
2823 gen_zir.decl.generation = self.generation;
2824 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
2825 },
2826 .local_val => {
2827 const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
2828 gen_zir.decl.analysis = .sema_failure;
2829 gen_zir.decl.generation = self.generation;
2830 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
2831 },
2832 .local_ptr => {
2833 const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
2834 gen_zir.decl.analysis = .sema_failure;
2835 gen_zir.decl.generation = self.generation;
2836 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
2837 },
2838 .zir_module => {
2839 const zir_module = scope.cast(Scope.ZIRModule).?;
2840 zir_module.status = .loaded_sema_failure;
2841 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
2842 },
2843 .file => unreachable,
2844 .container => unreachable,
2845 }
2846 return error.AnalysisFail;
2847}
2848
2849const InMemoryCoercionResult = enum {
2850 ok,
2851 no_match,
2852};
2853
2854fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
2855 if (dest_type.eql(src_type))
2856 return .ok;
2857
2858 // TODO: implement more of this function
2859
2860 return .no_match;
2861}
2862
2863fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
2864 return @bitCast(u128, a) == @bitCast(u128, b);
2865}
2866
2867pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
2868 // TODO is this a performance issue? maybe we should try the operation without
2869 // resorting to BigInt first.
2870 var lhs_space: Value.BigIntSpace = undefined;
2871 var rhs_space: Value.BigIntSpace = undefined;
2872 const lhs_bigint = lhs.toBigInt(&lhs_space);
2873 const rhs_bigint = rhs.toBigInt(&rhs_space);
2874 const limbs = try allocator.alloc(
2875 std.math.big.Limb,
2876 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2877 );
2878 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2879 result_bigint.add(lhs_bigint, rhs_bigint);
2880 const result_limbs = result_bigint.limbs[0..result_bigint.len];
2881
2882 const val_payload = if (result_bigint.positive) blk: {
2883 const val_payload = try allocator.create(Value.Payload.IntBigPositive);
2884 val_payload.* = .{ .limbs = result_limbs };
2885 break :blk &val_payload.base;
2886 } else blk: {
2887 const val_payload = try allocator.create(Value.Payload.IntBigNegative);
2888 val_payload.* = .{ .limbs = result_limbs };
2889 break :blk &val_payload.base;
2890 };
2891
2892 return Value.initPayload(val_payload);
2893}
2894
2895pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
2896 // TODO is this a performance issue? maybe we should try the operation without
2897 // resorting to BigInt first.
2898 var lhs_space: Value.BigIntSpace = undefined;
2899 var rhs_space: Value.BigIntSpace = undefined;
2900 const lhs_bigint = lhs.toBigInt(&lhs_space);
2901 const rhs_bigint = rhs.toBigInt(&rhs_space);
2902 const limbs = try allocator.alloc(
2903 std.math.big.Limb,
2904 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2905 );
2906 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2907 result_bigint.sub(lhs_bigint, rhs_bigint);
2908 const result_limbs = result_bigint.limbs[0..result_bigint.len];
2909
2910 const val_payload = if (result_bigint.positive) blk: {
2911 const val_payload = try allocator.create(Value.Payload.IntBigPositive);
2912 val_payload.* = .{ .limbs = result_limbs };
2913 break :blk &val_payload.base;
2914 } else blk: {
2915 const val_payload = try allocator.create(Value.Payload.IntBigNegative);
2916 val_payload.* = .{ .limbs = result_limbs };
2917 break :blk &val_payload.base;
2918 };
2919
2920 return Value.initPayload(val_payload);
2921}
2922
2923pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
2924 var bit_count = switch (float_type.tag()) {
2925 .comptime_float => 128,
2926 else => float_type.floatBits(self.getTarget()),
2927 };
2928
2929 const allocator = scope.arena();
2930 const val_payload = switch (bit_count) {
2931 16 => {
2932 return self.fail(scope, src, "TODO Implement addition for soft floats", .{});
2933 },
2934 32 => blk: {
2935 const lhs_val = lhs.toFloat(f32);
2936 const rhs_val = rhs.toFloat(f32);
2937 const val_payload = try allocator.create(Value.Payload.Float_32);
2938 val_payload.* = .{ .val = lhs_val + rhs_val };
2939 break :blk &val_payload.base;
2940 },
2941 64 => blk: {
2942 const lhs_val = lhs.toFloat(f64);
2943 const rhs_val = rhs.toFloat(f64);
2944 const val_payload = try allocator.create(Value.Payload.Float_64);
2945 val_payload.* = .{ .val = lhs_val + rhs_val };
2946 break :blk &val_payload.base;
2947 },
2948 128 => {
2949 return self.fail(scope, src, "TODO Implement addition for big floats", .{});
2950 },
2951 else => unreachable,
2952 };
2953
2954 return Value.initPayload(val_payload);
2955}
2956
2957pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
2958 var bit_count = switch (float_type.tag()) {
2959 .comptime_float => 128,
2960 else => float_type.floatBits(self.getTarget()),
2961 };
2962
2963 const allocator = scope.arena();
2964 const val_payload = switch (bit_count) {
2965 16 => {
2966 return self.fail(scope, src, "TODO Implement substraction for soft floats", .{});
2967 },
2968 32 => blk: {
2969 const lhs_val = lhs.toFloat(f32);
2970 const rhs_val = rhs.toFloat(f32);
2971 const val_payload = try allocator.create(Value.Payload.Float_32);
2972 val_payload.* = .{ .val = lhs_val - rhs_val };
2973 break :blk &val_payload.base;
2974 },
2975 64 => blk: {
2976 const lhs_val = lhs.toFloat(f64);
2977 const rhs_val = rhs.toFloat(f64);
2978 const val_payload = try allocator.create(Value.Payload.Float_64);
2979 val_payload.* = .{ .val = lhs_val - rhs_val };
2980 break :blk &val_payload.base;
2981 },
2982 128 => {
2983 return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
2984 },
2985 else => unreachable,
2986 };
2987
2988 return Value.initPayload(val_payload);
2989}
2990
2991pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
2992 if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
2993 return Type.initTag(.const_slice_u8);
2994 }
2995 // TODO stage1 type inference bug
2996 const T = Type.Tag;
2997
2998 const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
2999 type_payload.* = .{
3000 .base = .{
3001 .tag = switch (size) {
3002 .One => if (mutable) T.single_mut_pointer else T.single_const_pointer,
3003 .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer,
3004 .C => if (mutable) T.c_mut_pointer else T.c_const_pointer,
3005 .Slice => if (mutable) T.mut_slice else T.const_slice,
3006 },
3007 },
3008 .pointee_type = elem_ty,
3009 };
3010 return Type.initPayload(&type_payload.base);
3011}
3012
3013pub fn ptrType(
3014 self: *Module,
3015 scope: *Scope,
3016 src: usize,
3017 elem_ty: Type,
3018 sentinel: ?Value,
3019 @"align": u32,
3020 bit_offset: u16,
3021 host_size: u16,
3022 mutable: bool,
3023 @"allowzero": bool,
3024 @"volatile": bool,
3025 size: std.builtin.TypeInfo.Pointer.Size,
3026) Allocator.Error!Type {
3027 assert(host_size == 0 or bit_offset < host_size * 8);
3028
3029 // TODO check if type can be represented by simplePtrType
3030 const type_payload = try scope.arena().create(Type.Payload.Pointer);
3031 type_payload.* = .{
3032 .pointee_type = elem_ty,
3033 .sentinel = sentinel,
3034 .@"align" = @"align",
3035 .bit_offset = bit_offset,
3036 .host_size = host_size,
3037 .@"allowzero" = @"allowzero",
3038 .mutable = mutable,
3039 .@"volatile" = @"volatile",
3040 .size = size,
3041 };
3042 return Type.initPayload(&type_payload.base);
3043}
3044
3045pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
3046 return Type.initPayload(switch (child_type.tag()) {
3047 .single_const_pointer => blk: {
3048 const payload = try scope.arena().create(Type.Payload.PointerSimple);
3049 payload.* = .{
3050 .base = .{ .tag = .optional_single_const_pointer },
3051 .pointee_type = child_type.elemType(),
3052 };
3053 break :blk &payload.base;
3054 },
3055 .single_mut_pointer => blk: {
3056 const payload = try scope.arena().create(Type.Payload.PointerSimple);
3057 payload.* = .{
3058 .base = .{ .tag = .optional_single_mut_pointer },
3059 .pointee_type = child_type.elemType(),
3060 };
3061 break :blk &payload.base;
3062 },
3063 else => blk: {
3064 const payload = try scope.arena().create(Type.Payload.Optional);
3065 payload.* = .{
3066 .child_type = child_type,
3067 };
3068 break :blk &payload.base;
3069 },
3070 });
3071}
3072
3073pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type {
3074 if (elem_type.eql(Type.initTag(.u8))) {
3075 if (sentinel) |some| {
3076 if (some.eql(Value.initTag(.zero))) {
3077 const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
3078 payload.* = .{
3079 .len = len,
3080 };
3081 return Type.initPayload(&payload.base);
3082 }
3083 } else {
3084 const payload = try scope.arena().create(Type.Payload.Array_u8);
3085 payload.* = .{
3086 .len = len,
3087 };
3088 return Type.initPayload(&payload.base);
3089 }
3090 }
3091
3092 if (sentinel) |some| {
3093 const payload = try scope.arena().create(Type.Payload.ArraySentinel);
3094 payload.* = .{
3095 .len = len,
3096 .sentinel = some,
3097 .elem_type = elem_type,
3098 };
3099 return Type.initPayload(&payload.base);
3100 }
3101
3102 const payload = try scope.arena().create(Type.Payload.Array);
3103 payload.* = .{
3104 .len = len,
3105 .elem_type = elem_type,
3106 };
3107 return Type.initPayload(&payload.base);
3108}
3109
3110pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type {
3111 assert(error_set.zigTypeTag() == .ErrorSet);
3112 if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) {
3113 return Type.initTag(.anyerror_void_error_union);
3114 }
3115
3116 const result = try scope.arena().create(Type.Payload.ErrorUnion);
3117 result.* = .{
3118 .error_set = error_set,
3119 .payload = payload,
3120 };
3121 return Type.initPayload(&result.base);
3122}
3123
3124pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
3125 const result = try scope.arena().create(Type.Payload.AnyFrame);
3126 result.* = .{
3127 .return_type = return_type,
3128 };
3129 return Type.initPayload(&result.base);
3130}
3131
3132pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
3133 const zir_module = scope.namespace();
3134 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
3135 const loc = std.zig.findLineColumn(source, inst.src);
3136 if (inst.tag == .constant) {
3137 std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
3138 inst.ty,
3139 inst.castTag(.constant).?.val,
3140 zir_module.subFilePath(),
3141 loc.line + 1,
3142 loc.column + 1,
3143 });
3144 } else if (inst.deaths == 0) {
3145 std.debug.print("{} ty={} src={}:{}:{}\n", .{
3146 @tagName(inst.tag),
3147 inst.ty,
3148 zir_module.subFilePath(),
3149 loc.line + 1,
3150 loc.column + 1,
3151 });
3152 } else {
3153 std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
3154 @tagName(inst.tag),
3155 inst.ty,
3156 inst.deaths,
3157 zir_module.subFilePath(),
3158 loc.line + 1,
3159 loc.column + 1,
3160 });
3161 }
3162}
3163
3164pub const PanicId = enum {
3165 unreach,
3166 unwrap_null,
3167};
3168
3169pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
3170 const block_inst = try parent_block.arena.create(Inst.Block);
3171 block_inst.* = .{
3172 .base = .{
3173 .tag = Inst.Block.base_tag,
3174 .ty = Type.initTag(.void),
3175 .src = ok.src,
3176 },
3177 .body = .{
3178 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
3179 },
3180 };
3181
3182 const ok_body: ir.Body = .{
3183 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
3184 };
3185 const brvoid = try parent_block.arena.create(Inst.BrVoid);
3186 brvoid.* = .{
3187 .base = .{
3188 .tag = .brvoid,
3189 .ty = Type.initTag(.noreturn),
3190 .src = ok.src,
3191 },
3192 .block = block_inst,
3193 };
3194 ok_body.instructions[0] = &brvoid.base;
3195
3196 var fail_block: Scope.Block = .{
3197 .parent = parent_block,
3198 .func = parent_block.func,
3199 .decl = parent_block.decl,
3200 .instructions = .{},
3201 .arena = parent_block.arena,
3202 .is_comptime = parent_block.is_comptime,
3203 };
3204 defer fail_block.instructions.deinit(mod.gpa);
3205
3206 _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
3207
3208 const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
3209
3210 const condbr = try parent_block.arena.create(Inst.CondBr);
3211 condbr.* = .{
3212 .base = .{
3213 .tag = .condbr,
3214 .ty = Type.initTag(.noreturn),
3215 .src = ok.src,
3216 },
3217 .condition = ok,
3218 .then_body = ok_body,
3219 .else_body = fail_body,
3220 };
3221 block_inst.body.instructions[0] = &condbr.base;
3222
3223 try parent_block.instructions.append(mod.gpa, &block_inst.base);
3224}
3225
3226pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
3227 // TODO Once we have a panic function to call, call it here instead of breakpoint.
3228 _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
3229 return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
3230}
3231
3232pub fn getTarget(self: Module) Target {
3233 return self.comp.bin_file.options.target;
3234}
3235
3236pub fn optimizeMode(self: Module) std.builtin.Mode {
3237 return self.comp.bin_file.options.optimize_mode;
3238}
src-self-hosted/astgen.zig+1-1
......@@ -6,7 +6,7 @@ const Type = @import("type.zig").Type;
66const TypedValue = @import("TypedValue.zig");
77const assert = std.debug.assert;
88const zir = @import("zir.zig");
9const Module = @import("Module.zig");
9const Module = @import("ZigModule.zig");
1010const ast = std.zig.ast;
1111const trace = @import("tracy.zig").trace;
1212const Scope = Module.Scope;
src-self-hosted/codegen.zig+4-3
......@@ -7,8 +7,9 @@ const Type = @import("type.zig").Type;
77const Value = @import("value.zig").Value;
88const TypedValue = @import("TypedValue.zig");
99const link = @import("link.zig");
10const Module = @import("Module.zig");
11const ErrorMsg = Module.ErrorMsg;
10const Module = @import("ZigModule.zig");
11const Compilation = @import("Module.zig");
12const ErrorMsg = Compilation.ErrorMsg;
1213const Target = std.Target;
1314const Allocator = mem.Allocator;
1415const trace = @import("tracy.zig").trace;
......@@ -50,7 +51,7 @@ pub const Result = union(enum) {
5051 appended: void,
5152 /// The value is available externally, `code` is unused.
5253 externally_managed: []const u8,
53 fail: *Module.ErrorMsg,
54 fail: *ErrorMsg,
5455};
5556
5657pub const GenerateSymbolError = error{
src-self-hosted/codegen/c.zig+1-1
......@@ -1,7 +1,7 @@
11const std = @import("std");
22
33const link = @import("../link.zig");
4const Module = @import("../Module.zig");
4const Module = @import("../ZigModule.zig");
55
66const Inst = @import("../ir.zig").Inst;
77const Value = @import("../value.zig").Value;
src-self-hosted/codegen/wasm.zig+2-1
......@@ -5,7 +5,8 @@ const assert = std.debug.assert;
55const leb = std.debug.leb;
66const mem = std.mem;
77
8const Decl = @import("../Module.zig").Decl;
8const Module = @import("../ZigModule.zig");
9const Decl = Module.Decl;
910const Inst = @import("../ir.zig").Inst;
1011const Type = @import("../type.zig").Type;
1112const Value = @import("../value.zig").Value;
src-self-hosted/glibc.zig+13-3
......@@ -5,6 +5,7 @@ const mem = std.mem;
55const Module = @import("Module.zig");
66const path = std.fs.path;
77const build_options = @import("build_options");
8const trace = @import("tracy.zig").trace;
89
910pub const Lib = struct {
1011 name: []const u8,
......@@ -54,6 +55,9 @@ pub const LoadMetaDataError = error{
5455/// This function will emit a log error when there is a problem with the zig installation and then return
5556/// `error.ZigInstallationCorrupt`.
5657pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI {
58 const tracy = trace(@src());
59 defer tracy.end();
60
5761 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
5862 errdefer arena_allocator.deinit();
5963 const arena = &arena_allocator.allocator;
......@@ -584,6 +588,9 @@ fn lib_path(mod: *Module, arena: *Allocator, sub_path: []const u8) ![]const u8 {
584588}
585589
586590fn build_libc_object(mod: *Module, basename: []const u8, c_source_file: Module.CSourceFile) !void {
591 const tracy = trace(@src());
592 defer tracy.end();
593
587594 // TODO: This is extracted into a local variable to work around a stage1 miscompilation.
588595 const emit_bin = Module.EmitLoc{
589596 .directory = null, // Put it in the cache directory.
......@@ -618,8 +625,11 @@ fn build_libc_object(mod: *Module, basename: []const u8, c_source_file: Module.C
618625 try sub_module.update();
619626
620627 try mod.crt_files.ensureCapacity(mod.gpa, mod.crt_files.count() + 1);
621 const artifact_path = try std.fs.path.join(mod.gpa, &[_][]const u8{
622 sub_module.zig_cache_artifact_directory.path.?, basename,
623 });
628 const artifact_path = if (sub_module.bin_file.options.directory.path) |p|
629 try std.fs.path.join(mod.gpa, &[_][]const u8{ p, basename })
630 else
631 try mod.gpa.dupe(u8, basename);
632
633 // TODO obtain a lock on the artifact and put that in crt_files as well.
624634 mod.crt_files.putAssumeCapacityNoClobber(basename, artifact_path);
625635}
src-self-hosted/ir.zig+1-1
......@@ -1,7 +1,7 @@
11const std = @import("std");
22const Value = @import("value.zig").Value;
33const Type = @import("type.zig").Type;
4const Module = @import("Module.zig");
4const Module = @import("ZigModule.zig");
55const assert = std.debug.assert;
66const codegen = @import("codegen.zig");
77const ast = std.zig.ast;
src-self-hosted/link.zig+21-18
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const Allocator = std.mem.Allocator;
3const Module = @import("Module.zig");
3const Compilation = @import("Module.zig");
4const ZigModule = @import("ZigModule.zig");
45const fs = std.fs;
56const trace = @import("tracy.zig").trace;
67const Package = @import("Package.zig");
......@@ -12,7 +13,7 @@ pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ b
1213
1314pub const Options = struct {
1415 /// Where the output will go.
15 directory: Module.Directory,
16 directory: Compilation.Directory,
1617 /// Path to the output file, relative to `directory`.
1718 sub_path: []const u8,
1819 target: std.Target,
......@@ -21,7 +22,9 @@ pub const Options = struct {
2122 object_format: std.builtin.ObjectFormat,
2223 optimize_mode: std.builtin.Mode,
2324 root_name: []const u8,
24 root_pkg: ?*const Package,
25 /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
26 /// TODO rename Module to Compilation and then (as a separate commit) ZigModule to Module.
27 zig_module: ?*ZigModule,
2528 dynamic_linker: ?[]const u8 = null,
2629 /// Used for calculating how much space to reserve for symbols in case the binary file
2730 /// does not already have a symbol table.
......@@ -71,7 +74,7 @@ pub const Options = struct {
7174 lib_dirs: []const []const u8 = &[0][]const u8{},
7275 rpath_list: []const []const u8 = &[0][]const u8{},
7376
74 version: std.builtin.Version,
77 version: ?std.builtin.Version,
7578 libc_installation: ?*const LibCInstallation,
7679
7780 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
......@@ -184,7 +187,7 @@ pub const File = struct {
184187
185188 /// May be called before or after updateDeclExports but must be called
186189 /// after allocateDeclIndexes for any given Decl.
187 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
190 pub fn updateDecl(base: *File, module: *ZigModule, decl: *ZigModule.Decl) !void {
188191 switch (base.tag) {
189192 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
190193 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
......@@ -194,7 +197,7 @@ pub const File = struct {
194197 }
195198 }
196199
197 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
200 pub fn updateDeclLineNumber(base: *File, module: *ZigModule, decl: *ZigModule.Decl) !void {
198201 switch (base.tag) {
199202 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
200203 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
......@@ -205,7 +208,7 @@ pub const File = struct {
205208
206209 /// Must be called before any call to updateDecl or updateDeclExports for
207210 /// any given Decl.
208 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
211 pub fn allocateDeclIndexes(base: *File, decl: *ZigModule.Decl) !void {
209212 switch (base.tag) {
210213 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
211214 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
......@@ -256,20 +259,20 @@ pub const File = struct {
256259 }
257260 }
258261
259 pub fn flush(base: *File, module: *Module) !void {
262 pub fn flush(base: *File, comp: *Compilation) !void {
260263 const tracy = trace(@src());
261264 defer tracy.end();
262265
263266 try switch (base.tag) {
264 .coff => @fieldParentPtr(Coff, "base", base).flush(module),
265 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
266 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
267 .c => @fieldParentPtr(C, "base", base).flush(module),
268 .wasm => @fieldParentPtr(Wasm, "base", base).flush(module),
267 .coff => @fieldParentPtr(Coff, "base", base).flush(comp),
268 .elf => @fieldParentPtr(Elf, "base", base).flush(comp),
269 .macho => @fieldParentPtr(MachO, "base", base).flush(comp),
270 .c => @fieldParentPtr(C, "base", base).flush(comp),
271 .wasm => @fieldParentPtr(Wasm, "base", base).flush(comp),
269272 };
270273 }
271274
272 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
275 pub fn freeDecl(base: *File, decl: *ZigModule.Decl) void {
273276 switch (base.tag) {
274277 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
275278 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
......@@ -293,9 +296,9 @@ pub const File = struct {
293296 /// allocateDeclIndexes for any given Decl.
294297 pub fn updateDeclExports(
295298 base: *File,
296 module: *Module,
297 decl: *const Module.Decl,
298 exports: []const *Module.Export,
299 module: *ZigModule,
300 decl: *const ZigModule.Decl,
301 exports: []const *ZigModule.Export,
299302 ) !void {
300303 switch (base.tag) {
301304 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
......@@ -306,7 +309,7 @@ pub const File = struct {
306309 }
307310 }
308311
309 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
312 pub fn getDeclVAddr(base: *File, decl: *const ZigModule.Decl) u64 {
310313 switch (base.tag) {
311314 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
312315 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
src-self-hosted/link/C.zig+5-4
......@@ -2,7 +2,8 @@ const std = @import("std");
22const mem = std.mem;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
5const Module = @import("../Module.zig");
5const Module = @import("../ZigModule.zig");
6const Compilation = @import("../Module.zig");
67const fs = std.fs;
78const codegen = @import("../codegen/c.zig");
89const link = @import("../link.zig");
......@@ -20,7 +21,7 @@ main: std.ArrayList(u8),
2021called: std.StringHashMap(void),
2122need_stddef: bool = false,
2223need_stdint: bool = false,
23error_msg: *Module.ErrorMsg = undefined,
24error_msg: *Compilation.ErrorMsg = undefined,
2425
2526pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
2627 assert(options.object_format == .c);
......@@ -51,7 +52,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
5152}
5253
5354pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
54 self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
55 self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);
5556 return error.AnalysisFail;
5657}
5758
......@@ -71,7 +72,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
7172 };
7273}
7374
74pub fn flush(self: *C, module: *Module) !void {
75pub fn flush(self: *C, comp: *Compilation) !void {
7576 const writer = self.base.file.?.writer();
7677 try writer.writeAll(@embedFile("cbe.h"));
7778 var includes = false;
src-self-hosted/link/Coff.zig+5-4
......@@ -7,7 +7,8 @@ const assert = std.debug.assert;
77const fs = std.fs;
88
99const trace = @import("../tracy.zig").trace;
10const Module = @import("../Module.zig");
10const Module = @import("../ZigModule.zig");
11const Compilation = @import("../Module.zig");
1112const codegen = @import("../codegen.zig");
1213const link = @import("../link.zig");
1314
......@@ -732,7 +733,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
732733 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
733734 module.failed_exports.putAssumeCapacityNoClobber(
734735 exp,
735 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
736 try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
736737 );
737738 continue;
738739 }
......@@ -743,14 +744,14 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
743744 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
744745 module.failed_exports.putAssumeCapacityNoClobber(
745746 exp,
746 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
747 try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
747748 );
748749 continue;
749750 }
750751 }
751752}
752753
753pub fn flush(self: *Coff, module: *Module) !void {
754pub fn flush(self: *Coff, comp: *Compilation) !void {
754755 if (self.text_section_size_dirty) {
755756 // Write the new raw size in the .text header
756757 var buf: [4]u8 = undefined;
src-self-hosted/link/Elf.zig+168-53
......@@ -3,7 +3,8 @@ const mem = std.mem;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
55const ir = @import("../ir.zig");
6const Module = @import("../Module.zig");
6const Module = @import("../ZigModule.zig");
7const Compilation = @import("../Module.zig");
78const fs = std.fs;
89const elf = std.elf;
910const codegen = @import("../codegen.zig");
......@@ -122,6 +123,9 @@ dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
122123dbg_info_decl_first: ?*TextBlock = null,
123124dbg_info_decl_last: ?*TextBlock = null,
124125
126/// Prevents other processes from clobbering the output file this is linking.
127lock: ?std.cache_hash.Lock = null,
128
125129/// `alloc_num / alloc_den` is the factor of padding when allocating.
126130const alloc_num = 4;
127131const alloc_den = 3;
......@@ -285,7 +289,21 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf
285289 return self;
286290}
287291
292pub fn releaseLock(self: *Elf) void {
293 if (self.lock) |*lock| {
294 lock.release();
295 self.lock = null;
296 }
297}
298
299pub fn toOwnedLock(self: *Elf) std.cache_hash.Lock {
300 const lock = self.lock.?;
301 self.lock = null;
302 return lock;
303}
304
288305pub fn deinit(self: *Elf) void {
306 self.releaseLock();
289307 self.sections.deinit(self.base.allocator);
290308 self.program_headers.deinit(self.base.allocator);
291309 self.shstrtab.deinit(self.base.allocator);
......@@ -709,20 +727,24 @@ pub const abbrev_base_type = 4;
709727pub const abbrev_pad1 = 5;
710728pub const abbrev_parameter = 6;
711729
712pub fn flush(self: *Elf, module: *Module) !void {
730pub fn flush(self: *Elf, comp: *Compilation) !void {
713731 if (build_options.have_llvm and self.base.options.use_lld) {
714 return self.linkWithLLD(module);
732 return self.linkWithLLD(comp);
715733 } else {
716734 switch (self.base.options.effectiveOutputMode()) {
717735 .Exe, .Obj => {},
718736 .Lib => return error.TODOImplementWritingLibFiles,
719737 }
720 return self.flushInner(module);
738 return self.flushInner(comp);
721739 }
722740}
723741
724742/// Commit pending changes and write headers.
725fn flushInner(self: *Elf, module: *Module) !void {
743fn flushInner(self: *Elf, comp: *Compilation) !void {
744 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
745 // Zig source code.
746 const zig_module = self.base.options.zig_module orelse return error.LinkingWithoutZigSourceUnimplemented;
747
726748 const target_endian = self.base.options.target.cpu.arch.endian();
727749 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
728750 const ptr_width_bytes: u8 = self.ptrWidthBytes();
......@@ -844,8 +866,8 @@ fn flushInner(self: *Elf, module: *Module) !void {
844866 },
845867 }
846868 // Write the form for the compile unit, which must match the abbrev table above.
847 const name_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_path);
848 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_directory.path.?);
869 const name_strp = try self.makeDebugString(zig_module.root_pkg.root_src_path);
870 const comp_dir_strp = try self.makeDebugString(zig_module.root_pkg.root_src_directory.path.?);
849871 const producer_strp = try self.makeDebugString(link.producer_string);
850872 // Currently only one compilation unit is supported, so the address range is simply
851873 // identical to the main program header virtual address and memory size.
......@@ -1014,7 +1036,7 @@ fn flushInner(self: *Elf, module: *Module) !void {
10141036 0, // include_directories (none except the compilation unit cwd)
10151037 });
10161038 // file_names[0]
1017 di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.?.root_src_path); // relative path name
1039 di_buf.appendSliceAssumeCapacity(zig_module.root_pkg.root_src_path); // relative path name
10181040 di_buf.appendSliceAssumeCapacity(&[_]u8{
10191041 0, // null byte for the relative path name
10201042 0, // directory_index
......@@ -1199,11 +1221,105 @@ fn flushInner(self: *Elf, module: *Module) !void {
11991221 assert(!self.debug_strtab_dirty);
12001222}
12011223
1202fn linkWithLLD(self: *Elf, module: *Module) !void {
1224fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12031225 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
12041226 defer arena_allocator.deinit();
12051227 const arena = &arena_allocator.allocator;
12061228
1229 const directory = self.base.options.directory; // Just an alias to make it shorter to type.
1230
1231 // If there is no Zig code to compile, then we should skip flushing the output file because it
1232 // will not be part of the linker line anyway.
1233 const zig_module_obj_path: ?[]const u8 = if (self.base.options.zig_module) |module| blk: {
1234 try self.flushInner(comp);
1235
1236 const obj_basename = self.base.intermediary_basename.?;
1237 const full_obj_path = if (directory.path) |dir_path|
1238 try std.fs.path.join(arena, &[_][]const u8{dir_path, obj_basename})
1239 else
1240 obj_basename;
1241 break :blk full_obj_path;
1242 } else null;
1243
1244 // Here we want to determine whether we can save time by not invoking LLD when the
1245 // output is unchanged. None of the linker options or the object files that are being
1246 // linked are in the hash that namespaces the directory we are outputting to. Therefore,
1247 // we must hash those now, and the resulting digest will form the "id" of the linking
1248 // job we are about to perform.
1249 // After a successful link, we store the id in the metadata of a symlink named "id.txt" in
1250 // the artifact directory. So, now, we check if this symlink exists, and if it matches
1251 // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
1252 const id_symlink_basename = "id.txt";
1253
1254 // We are about to obtain this lock, so here we give other processes a chance first.
1255 self.releaseLock();
1256
1257 var ch = comp.cache_parent.obtain();
1258 defer ch.deinit();
1259
1260 const is_lib = self.base.options.output_mode == .Lib;
1261 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
1262 const have_dynamic_linker = self.base.options.link_libc and
1263 self.base.options.link_mode == .Dynamic and (is_dyn_lib or self.base.options.output_mode == .Exe);
1264
1265 try ch.addOptionalFile(self.base.options.linker_script);
1266 try ch.addOptionalFile(self.base.options.version_script);
1267 try ch.addListOfFiles(self.base.options.objects);
1268 for (comp.c_object_table.items()) |entry| switch (entry.key.status) {
1269 .new => unreachable,
1270 .failure => return error.NotAllCSourceFilesAvailableToLink,
1271 .success => |success| _ = try ch.addFile(success.object_path, null),
1272 };
1273 try ch.addOptionalFile(zig_module_obj_path);
1274 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
1275 // installation sources because they are always a product of the compiler version + target information.
1276 ch.hash.addOptional(self.base.options.stack_size_override);
1277 ch.hash.addOptional(self.base.options.gc_sections);
1278 ch.hash.add(self.base.options.eh_frame_hdr);
1279 ch.hash.add(self.base.options.rdynamic);
1280 ch.hash.addListOfBytes(self.base.options.extra_lld_args);
1281 ch.hash.addListOfBytes(self.base.options.lib_dirs);
1282 ch.hash.add(self.base.options.z_nodelete);
1283 ch.hash.add(self.base.options.z_defs);
1284 if (self.base.options.link_libc) {
1285 ch.hash.add(self.base.options.libc_installation != null);
1286 if (self.base.options.libc_installation) |libc_installation| {
1287 ch.hash.addBytes(libc_installation.crt_dir.?);
1288 }
1289 if (have_dynamic_linker) {
1290 ch.hash.addOptionalBytes(self.base.options.dynamic_linker);
1291 }
1292 }
1293 if (is_dyn_lib) {
1294 ch.hash.addOptionalBytes(self.base.options.override_soname);
1295 ch.hash.addOptional(self.base.options.version);
1296 }
1297 ch.hash.addListOfBytes(self.base.options.system_libs);
1298 ch.hash.addOptional(self.base.options.allow_shlib_undefined);
1299 ch.hash.add(self.base.options.bind_global_refs_locally);
1300
1301 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1302 _ = try ch.hit();
1303 const digest = ch.final();
1304
1305 var prev_digest_buf: [digest.len]u8 = undefined;
1306 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch blk: {
1307 // Handle this as a cache miss.
1308 mem.set(u8, &prev_digest_buf, 0);
1309 break :blk &prev_digest_buf;
1310 };
1311 if (mem.eql(u8, prev_digest, &digest)) {
1312 // Hot diggity dog! The output binary is already there.
1313 self.lock = ch.toOwnedLock();
1314 return;
1315 }
1316
1317 // We are about to change the output file to be different, so we invalidate the build hash now.
1318 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1319 error.FileNotFound => {},
1320 else => |e| return e,
1321 };
1322
12071323 const target = self.base.options.target;
12081324 const is_obj = self.base.options.output_mode == .Obj;
12091325
......@@ -1272,8 +1388,6 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
12721388 try argv.append(arg);
12731389 }
12741390
1275 const is_lib = self.base.options.output_mode == .Lib;
1276 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
12771391 if (self.base.options.link_mode == .Static) {
12781392 if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
12791393 try argv.append("-Bstatic");
......@@ -1288,7 +1402,7 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
12881402 try argv.append("-pie");
12891403 }
12901404
1291 const full_out_path = if (self.base.options.directory.path) |dir_path|
1405 const full_out_path = if (directory.path) |dir_path|
12921406 try std.fs.path.join(arena, &[_][]const u8{dir_path, self.base.options.sub_path})
12931407 else
12941408 self.base.options.sub_path;
......@@ -1311,13 +1425,14 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
13111425 break :o "Scrt1.o";
13121426 }
13131427 };
1314 try argv.append(try module.get_libc_crt_file(arena, crt1o));
1428 try argv.append(try comp.get_libc_crt_file(arena, crt1o));
13151429 if (target_util.libc_needs_crti_crtn(target)) {
1316 try argv.append(try module.get_libc_crt_file(arena, "crti.o"));
1430 try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
13171431 }
13181432 }
13191433
13201434 // TODO rpaths
1435 // TODO add to cache hash above too
13211436 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
13221437 // Buf *rpath = g->rpath_list.at(i);
13231438 // add_rpath(lj, rpath);
......@@ -1354,7 +1469,7 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
13541469 try argv.append(libc_installation.crt_dir.?);
13551470 }
13561471
1357 if (self.base.options.link_mode == .Dynamic and (is_dyn_lib or self.base.options.output_mode == .Exe)) {
1472 if (have_dynamic_linker) {
13581473 if (self.base.options.dynamic_linker) |dynamic_linker| {
13591474 try argv.append("-dynamic-linker");
13601475 try argv.append(dynamic_linker);
......@@ -1363,9 +1478,10 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
13631478 }
13641479
13651480 if (is_dyn_lib) {
1366 const soname = self.base.options.override_soname orelse
1367 try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name,
1368 self.base.options.version.major,});
1481 const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver|
1482 try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major})
1483 else
1484 try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name});
13691485 try argv.append("-soname");
13701486 try argv.append(soname);
13711487
......@@ -1378,28 +1494,14 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
13781494 // Positional arguments to the linker such as object files.
13791495 try argv.appendSlice(self.base.options.objects);
13801496
1381 for (module.c_object_table.items()) |entry| {
1382 const c_object = entry.key;
1383 switch (c_object.status) {
1384 .new => unreachable,
1385 .failure => return error.NotAllCSourceFilesAvailableToLink,
1386 .success => |full_obj_path| {
1387 try argv.append(full_obj_path);
1388 },
1389 }
1390 }
1391
1392 // If there is no Zig code to compile, then we should skip flushing the output file because it
1393 // will not be part of the linker line anyway.
1394 if (module.root_pkg != null) {
1395 try self.flushInner(module);
1497 for (comp.c_object_table.items()) |entry| switch (entry.key.status) {
1498 .new => unreachable,
1499 .failure => unreachable, // Checked during cache hashing.
1500 .success => |success| try argv.append(success.object_path),
1501 };
13961502
1397 const obj_basename = self.base.intermediary_basename.?;
1398 const full_obj_path = if (self.base.options.directory.path) |dir_path|
1399 try std.fs.path.join(arena, &[_][]const u8{dir_path, obj_basename})
1400 else
1401 obj_basename;
1402 try argv.append(full_obj_path);
1503 if (zig_module_obj_path) |p| {
1504 try argv.append(p);
14031505 }
14041506
14051507 // TODO compiler-rt and libc
......@@ -1419,7 +1521,7 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
14191521 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
14201522 // (the check for that needs to be earlier), but they could be full paths to .so files, in which
14211523 // case we want to avoid prepending "-l".
1422 const ext = Module.classifyFileExt(link_lib);
1524 const ext = Compilation.classifyFileExt(link_lib);
14231525 const arg = if (ext == .so) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
14241526 argv.appendAssumeCapacity(arg);
14251527 }
......@@ -1427,8 +1529,8 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
14271529 if (!is_obj) {
14281530 // libc++ dep
14291531 if (self.base.options.link_libcpp) {
1430 try argv.append(module.libcxxabi_static_lib.?);
1431 try argv.append(module.libcxx_static_lib.?);
1532 try argv.append(comp.libcxxabi_static_lib.?);
1533 try argv.append(comp.libcxx_static_lib.?);
14321534 }
14331535
14341536 // libc dep
......@@ -1448,15 +1550,15 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
14481550 try argv.append("-lpthread");
14491551 }
14501552 } else if (target.isGnuLibC()) {
1451 try argv.append(module.libunwind_static_lib.?);
1553 try argv.append(comp.libunwind_static_lib.?);
14521554 // TODO here we need to iterate over the glibc libs and add the .so files to the linker line.
14531555 std.log.warn("TODO port add_glibc_libs to stage2", .{});
1454 try argv.append(try module.get_libc_crt_file(arena, "libc_nonshared.a"));
1556 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
14551557 } else if (target.isMusl()) {
1456 try argv.append(module.libunwind_static_lib.?);
1457 try argv.append(module.libc_static_lib.?);
1558 try argv.append(comp.libunwind_static_lib.?);
1559 try argv.append(comp.libc_static_lib.?);
14581560 } else if (self.base.options.link_libcpp) {
1459 try argv.append(module.libunwind_static_lib.?);
1561 try argv.append(comp.libunwind_static_lib.?);
14601562 } else {
14611563 unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
14621564 }
......@@ -1466,9 +1568,9 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
14661568 // crt end
14671569 if (link_in_crt) {
14681570 if (target.isAndroid()) {
1469 try argv.append(try module.get_libc_crt_file(arena, "crtend_android.o"));
1571 try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
14701572 } else if (target_util.libc_needs_crti_crtn(target)) {
1471 try argv.append(try module.get_libc_crt_file(arena, "crtn.o"));
1573 try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
14721574 }
14731575 }
14741576
......@@ -1500,6 +1602,19 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
15001602 const ZigLLDLink = @import("../llvm.zig").ZigLLDLink;
15011603 const ok = ZigLLDLink(.ELF, new_argv.ptr, new_argv.len, append_diagnostic, 0, 0);
15021604 if (!ok) return error.LLDReportedFailure;
1605
1606 // Update the dangling symlink "id.txt" with the digest. If it fails we can continue; it only
1607 // means that the next invocation will have an unnecessary cache miss.
1608 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
1609 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
1610 };
1611 // Again failure here only means an unnecessary cache miss.
1612 ch.writeManifest() catch |err| {
1613 std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) });
1614 };
1615 // We hang on to this lock so that the output file path can be used without
1616 // other processes clobbering it.
1617 self.lock = ch.toOwnedLock();
15031618}
15041619
15051620fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
......@@ -2396,7 +2511,7 @@ pub fn updateDeclExports(
23962511 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
23972512 module.failed_exports.putAssumeCapacityNoClobber(
23982513 exp,
2399 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
2514 try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
24002515 );
24012516 continue;
24022517 }
......@@ -2414,7 +2529,7 @@ pub fn updateDeclExports(
24142529 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
24152530 module.failed_exports.putAssumeCapacityNoClobber(
24162531 exp,
2417 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
2532 try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
24182533 );
24192534 continue;
24202535 },
......@@ -2722,8 +2837,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 {
27222837 directory_count * 8 + file_name_count * 8 +
27232838 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
27242839 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2725 self.base.options.root_pkg.?.root_src_directory.path.?.len +
2726 self.base.options.root_pkg.?.root_src_path.len);
2840 self.base.options.zig_module.?.root_pkg.root_src_directory.path.?.len +
2841 self.base.options.zig_module.?.root_pkg.root_src_path.len);
27272842}
27282843
27292844fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
src-self-hosted/link/MachO.zig+3-2
......@@ -12,7 +12,8 @@ const mem = std.mem;
1212const trace = @import("../tracy.zig").trace;
1313const Type = @import("../type.zig").Type;
1414
15const Module = @import("../Module.zig");
15const Module = @import("../ZigModule.zig");
16const Compilation = @import("../Module.zig");
1617const link = @import("../link.zig");
1718const File = link.File;
1819
......@@ -205,7 +206,7 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
205206 return self;
206207}
207208
208pub fn flush(self: *MachO, module: *Module) !void {
209pub fn flush(self: *MachO, comp: *Compilation) !void {
209210 switch (self.base.options.output_mode) {
210211 .Exe => {
211212 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
src-self-hosted/link/Wasm.zig+4-3
......@@ -6,7 +6,8 @@ const assert = std.debug.assert;
66const fs = std.fs;
77const leb = std.debug.leb;
88
9const Module = @import("../Module.zig");
9const Module = @import("../ZigModule.zig");
10const Compilation = @import("../Module.zig");
1011const codegen = @import("../codegen/wasm.zig");
1112const link = @import("../link.zig");
1213
......@@ -126,7 +127,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
126127 decl.fn_link.wasm = null;
127128}
128129
129pub fn flush(self: *Wasm, module: *Module) !void {
130pub fn flush(self: *Wasm, comp: *Compilation) !void {
130131 const file = self.base.file.?;
131132 const header_size = 5 + 1;
132133
......@@ -164,7 +165,7 @@ pub fn flush(self: *Wasm, module: *Module) !void {
164165 }
165166
166167 // Export section
167 {
168 if (self.base.options.zig_module) |module| {
168169 const header_offset = try reserveVecSectionHeader(file);
169170 const writer = file.writer();
170171 var count: u32 = 0;
src-self-hosted/main.zig+8-2
......@@ -268,6 +268,7 @@ pub fn buildOutputType(
268268 var link_mode: ?std.builtin.LinkMode = null;
269269 var root_src_file: ?[]const u8 = null;
270270 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
271 var have_version = false;
271272 var strip = false;
272273 var single_threaded = false;
273274 var watch = false;
......@@ -445,6 +446,7 @@ pub fn buildOutputType(
445446 version = std.builtin.Version.parse(args[i]) catch |err| {
446447 fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
447448 };
449 have_version = true;
448450 } else if (mem.eql(u8, arg, "-target")) {
449451 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
450452 i += 1;
......@@ -799,6 +801,7 @@ pub fn buildOutputType(
799801 version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
800802 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
801803 };
804 have_version = true;
802805 } else if (mem.eql(u8, arg, "--minor-image-version")) {
803806 i += 1;
804807 if (i >= linker_args.items.len) {
......@@ -807,6 +810,7 @@ pub fn buildOutputType(
807810 version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
808811 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
809812 };
813 have_version = true;
810814 } else if (mem.eql(u8, arg, "--stack")) {
811815 i += 1;
812816 if (i >= linker_args.items.len) {
......@@ -1161,7 +1165,7 @@ pub fn buildOutputType(
11611165 .self_exe_path = self_exe_path,
11621166 .rand = &default_prng.random,
11631167 .clang_passthrough_mode = arg_mode != .build,
1164 .version = version,
1168 .version = if (have_version) version else null,
11651169 .libc_installation = if (libc_installation) |*lci| lci else null,
11661170 .debug_cc = debug_cc,
11671171 .debug_link = debug_link,
......@@ -1228,7 +1232,9 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
12281232 }
12291233
12301234 if (zir_out_path) |zop| {
1231 var new_zir_module = try zir.emit(gpa, module);
1235 const zig_module = module.bin_file.options.zig_module orelse
1236 fatal("-femit-zir with no zig source code", .{});
1237 var new_zir_module = try zir.emit(gpa, zig_module);
12321238 defer new_zir_module.deinit(gpa);
12331239
12341240 const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
src-self-hosted/test.zig+1-1
......@@ -549,7 +549,7 @@ pub const TestContext = struct {
549549 update_node.estimated_total_items = 5;
550550 var emit_node = update_node.start("emit", null);
551551 emit_node.activate();
552 var new_zir_module = try zir.emit(allocator, module);
552 var new_zir_module = try zir.emit(allocator, module.bin_file.options.zig_module.?);
553553 defer new_zir_module.deinit(allocator);
554554 emit_node.end();
555555
src-self-hosted/type.zig+1-1
......@@ -3,7 +3,7 @@ const Value = @import("value.zig").Value;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
55const Target = std.Target;
6const Module = @import("Module.zig");
6const Module = @import("ZigModule.zig");
77
88/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
99/// It's important for this type to be small.
src-self-hosted/value.zig+1-1
......@@ -6,7 +6,7 @@ const BigIntConst = std.math.big.int.Const;
66const BigIntMutable = std.math.big.int.Mutable;
77const Target = std.Target;
88const Allocator = std.mem.Allocator;
9const Module = @import("Module.zig");
9const Module = @import("ZigModule.zig");
1010
1111/// This is the raw data, with no bookkeeping, no memory awareness,
1212/// no de-duplication, and no type system awareness.
src-self-hosted/zir.zig+1-1
......@@ -10,7 +10,7 @@ const Type = @import("type.zig").Type;
1010const Value = @import("value.zig").Value;
1111const TypedValue = @import("TypedValue.zig");
1212const ir = @import("ir.zig");
13const IrModule = @import("Module.zig");
13const IrModule = @import("ZigModule.zig");
1414
1515/// This struct is relevent only for the ZIR Module text format. It is not used for
1616/// semantic analysis of Zig source code.
src-self-hosted/zir_sema.zig+4-4
......@@ -16,7 +16,7 @@ const TypedValue = @import("TypedValue.zig");
1616const assert = std.debug.assert;
1717const ir = @import("ir.zig");
1818const zir = @import("zir.zig");
19const Module = @import("Module.zig");
19const Module = @import("ZigModule.zig");
2020const Inst = ir.Inst;
2121const Body = ir.Body;
2222const trace = @import("tracy.zig").trace;
......@@ -199,10 +199,10 @@ pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError
199199 // We don't fully codegen the decl until later, but we do need to reserve a global
200200 // offset table index for it. This allows us to codegen decls out of dependency order,
201201 // increasing how many computations can be done in parallel.
202 try mod.bin_file.allocateDeclIndexes(decl);
203 try mod.work_queue.writeItem(.{ .codegen_decl = decl });
202 try mod.comp.bin_file.allocateDeclIndexes(decl);
203 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
204204 } else if (prev_type_has_bits) {
205 mod.bin_file.freeDecl(decl);
205 mod.comp.bin_file.freeDecl(decl);
206206 }
207207
208208 return type_changed;
test/stage2/test.zig+5-2
......@@ -1,8 +1,11 @@
11const std = @import("std");
22const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
4// self-hosted does not yet support PE executable files / COFF object files
5// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
4// Self-hosted has differing levels of support for various architectures. For now we pass explicit
5// target parameters to each test case. At some point we will take this to the next level and have
6// a set of targets that all test cases run on unless specifically overridden. For now, each test
7// case applies to only the specified target.
8
69const linux_x64 = std.zig.CrossTarget{
710 .cpu_arch = .x86_64,
811 .os_tag = .linux,