authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-05 12:17:13+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-02-05 12:17:13+00:00
logf01f1e33c96f0b00db8e036a654c1b3bf8531cd8
tree3daca71f83a02d73d4c93d973d7022776e476274
parentcf059ee08716300e924bced08ebdd5bd8f97d789
parentbebfa036ba52076cd03f9ef943f61da64ba6e97b
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22754 from mlugg/files-and-stuff

ZON and incremental bits

16 files changed, 788 insertions(+), 587 deletions(-)

lib/std/zig/Zoir.zig+25
......@@ -10,6 +10,31 @@ string_bytes: []u8,
1010compile_errors: []Zoir.CompileError,
1111error_notes: []Zoir.CompileError.Note,
1212
13/// The data stored at byte offset 0 when ZOIR is stored in a file.
14pub const Header = extern struct {
15 nodes_len: u32,
16 extra_len: u32,
17 limbs_len: u32,
18 string_bytes_len: u32,
19 compile_errors_len: u32,
20 error_notes_len: u32,
21
22 /// We could leave this as padding, however it triggers a Valgrind warning because
23 /// we read and write undefined bytes to the file system. This is harmless, but
24 /// it's essentially free to have a zero field here and makes the warning go away,
25 /// making it more likely that following Valgrind warnings will be taken seriously.
26 unused: u64 = 0,
27
28 stat_inode: std.fs.File.INode,
29 stat_size: u64,
30 stat_mtime: i128,
31
32 comptime {
33 // Check that `unused` is working as expected
34 assert(std.meta.hasUniqueRepresentation(Header));
35 }
36};
37
1338pub fn hasCompileErrors(zoir: Zoir) bool {
1439 if (zoir.compile_errors.len > 0) {
1540 assert(zoir.nodes.len == 0);
src/Builtin.zig+10-14
......@@ -264,14 +264,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
264264}
265265
266266pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void {
267 assert(file.source_loaded == true);
268
269267 if (mod.root.statFile(mod.root_src_path)) |stat| {
270 if (stat.size != file.source.len) {
268 if (stat.size != file.source.?.len) {
271269 std.log.warn(
272270 "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++
273271 "Overwriting with correct file contents now",
274 .{ mod.root, mod.root_src_path, file.source.len, stat.size },
272 .{ mod.root, mod.root_src_path, file.source.?.len, stat.size },
275273 );
276274
277275 try writeFile(file, mod);
......@@ -296,15 +294,13 @@ pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void {
296294
297295 log.debug("parsing and generating '{s}'", .{mod.root_src_path});
298296
299 file.tree = try std.zig.Ast.parse(comp.gpa, file.source, .zig);
300 assert(file.tree.errors.len == 0); // builtin.zig must parse
301 file.tree_loaded = true;
297 file.tree = try std.zig.Ast.parse(comp.gpa, file.source.?, .zig);
298 assert(file.tree.?.errors.len == 0); // builtin.zig must parse
302299
303 file.zir = try AstGen.generate(comp.gpa, file.tree);
304 assert(!file.zir.hasCompileErrors()); // builtin.zig must not have astgen errors
305 file.zir_loaded = true;
306 file.status = .success_zir;
307 // Note that whilst we set `zir_loaded` here, we populated `path_digest`
300 file.zir = try AstGen.generate(comp.gpa, file.tree.?);
301 assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors
302 file.status = .success;
303 // Note that whilst we set `zir` here, we populated `path_digest`
308304 // all the way back in `Package.Module.create`.
309305}
310306
......@@ -312,7 +308,7 @@ fn writeFile(file: *File, mod: *Module) !void {
312308 var buf: [std.fs.max_path_bytes]u8 = undefined;
313309 var af = try mod.root.atomicFile(mod.root_src_path, .{ .make_path = true }, &buf);
314310 defer af.deinit();
315 try af.file.writeAll(file.source);
311 try af.file.writeAll(file.source.?);
316312 af.finish() catch |err| switch (err) {
317313 error.AccessDenied => switch (builtin.os.tag) {
318314 .windows => {
......@@ -326,7 +322,7 @@ fn writeFile(file: *File, mod: *Module) !void {
326322 };
327323
328324 file.stat = .{
329 .size = file.source.len,
325 .size = file.source.?.len,
330326 .inode = 0, // dummy value
331327 .mtime = 0, // dummy value
332328 };
src/Compilation.zig+92-54
......@@ -2220,10 +2220,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
22202220 try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count());
22212221 for (zcu.import_table.values()) |file_index| {
22222222 if (zcu.fileByIndex(file_index).mod.isBuiltin()) continue;
2223 const file = zcu.fileByIndex(file_index);
2224 if (file.getMode() == .zig) {
2225 comp.astgen_work_queue.writeItemAssumeCapacity(file_index);
2226 }
2223 comp.astgen_work_queue.writeItemAssumeCapacity(file_index);
22272224 }
22282225 if (comp.file_system_inputs) |fsi| {
22292226 for (zcu.import_table.values()) |file_index| {
......@@ -2906,10 +2903,12 @@ pub fn makeBinFileWritable(comp: *Compilation) !void {
29062903const Header = extern struct {
29072904 intern_pool: extern struct {
29082905 thread_count: u32,
2909 file_deps_len: u32,
29102906 src_hash_deps_len: u32,
29112907 nav_val_deps_len: u32,
29122908 nav_ty_deps_len: u32,
2909 interned_deps_len: u32,
2910 zon_file_deps_len: u32,
2911 embed_file_deps_len: u32,
29132912 namespace_deps_len: u32,
29142913 namespace_name_deps_len: u32,
29152914 first_dependency_len: u32,
......@@ -2950,10 +2949,12 @@ pub fn saveState(comp: *Compilation) !void {
29502949 const header: Header = .{
29512950 .intern_pool = .{
29522951 .thread_count = @intCast(ip.locals.len),
2953 .file_deps_len = @intCast(ip.file_deps.count()),
29542952 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
29552953 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
29562954 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
2955 .interned_deps_len = @intCast(ip.interned_deps.count()),
2956 .zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
2957 .embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
29572958 .namespace_deps_len = @intCast(ip.namespace_deps.count()),
29582959 .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
29592960 .first_dependency_len = @intCast(ip.first_dependency.count()),
......@@ -2978,14 +2979,18 @@ pub fn saveState(comp: *Compilation) !void {
29782979 addBuf(&bufs, mem.asBytes(&header));
29792980 addBuf(&bufs, mem.sliceAsBytes(pt_headers.items));
29802981
2981 addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.keys()));
2982 addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.values()));
29832982 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys()));
29842983 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
29852984 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
29862985 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
29872986 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
29882987 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
2988 addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.keys()));
2989 addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.values()));
2990 addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.keys()));
2991 addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.values()));
2992 addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.keys()));
2993 addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.values()));
29892994 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
29902995 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
29912996 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
......@@ -3203,15 +3208,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32033208 }
32043209
32053210 if (comp.zcu) |zcu| {
3206 const ip = &zcu.intern_pool;
3207
32083211 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
32093212 if (error_msg) |msg| {
32103213 try addModuleErrorMsg(zcu, &bundle, msg.*);
32113214 } else {
32123215 // Must be ZIR or Zoir errors. Note that this may include AST errors.
32133216 _ = try file.getTree(gpa); // Tree must be loaded.
3214 if (file.zir_loaded) {
3217 if (file.zir != null) {
32153218 try addZirErrorMessages(&bundle, file);
32163219 } else if (file.zoir != null) {
32173220 try addZoirErrorMessages(&bundle, file);
......@@ -3277,20 +3280,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32773280 if (!refs.contains(anal_unit)) continue;
32783281 }
32793282
3280 report_ok: {
3281 const file_index = switch (anal_unit.unwrap()) {
3282 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
3283 .nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
3284 .type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
3285 .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
3286 .memoized_state => break :report_ok, // always report std.builtin errors
3287 };
3288
3289 // Skip errors for AnalUnits within files that had a parse failure.
3290 // We'll try again once parsing succeeds.
3291 if (!zcu.fileByIndex(file_index).okToReportErrors()) continue;
3292 }
3293
32943283 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{
32953284 error_msg.msg,
32963285 zcu.fmtAnalUnit(anal_unit),
......@@ -3318,12 +3307,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
33183307 }
33193308 }
33203309 }
3321 for (zcu.failed_codegen.keys(), zcu.failed_codegen.values()) |nav, error_msg| {
3322 if (!zcu.navFileScope(nav).okToReportErrors()) continue;
3310 for (zcu.failed_codegen.values()) |error_msg| {
33233311 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
33243312 }
3325 for (zcu.failed_types.keys(), zcu.failed_types.values()) |ty_index, error_msg| {
3326 if (!zcu.typeFileScope(ty_index).okToReportErrors()) continue;
3313 for (zcu.failed_types.values()) |error_msg| {
33273314 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
33283315 }
33293316 for (zcu.failed_exports.values()) |value| {
......@@ -3623,22 +3610,17 @@ pub fn addModuleErrorMsg(
36233610}
36243611
36253612pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void {
3626 assert(file.zir_loaded);
3627 assert(file.tree_loaded);
3628 assert(file.source_loaded);
36293613 const gpa = eb.gpa;
36303614 const src_path = try file.fullPath(gpa);
36313615 defer gpa.free(src_path);
3632 return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path);
3616 return eb.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, src_path);
36333617}
36343618
36353619pub fn addZoirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void {
3636 assert(file.source_loaded);
3637 assert(file.tree_loaded);
36383620 const gpa = eb.gpa;
36393621 const src_path = try file.fullPath(gpa);
36403622 defer gpa.free(src_path);
3641 return eb.addZoirErrorMessages(file.zoir.?, file.tree, file.source, src_path);
3623 return eb.addZoirErrorMessages(file.zoir.?, file.tree.?, file.source.?, src_path);
36423624}
36433625
36443626pub fn performAllTheWork(
......@@ -3802,7 +3784,7 @@ fn performAllTheWorkInner(
38023784 // will be needed by the worker threads.
38033785 const path_digest = zcu.filePathDigest(file_index);
38043786 const file = zcu.fileByIndex(file_index);
3805 comp.thread_pool.spawnWgId(&astgen_wait_group, workerAstGenFile, .{
3787 comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateFile, .{
38063788 comp, file, file_index, path_digest, zir_prog_node, &astgen_wait_group, .root,
38073789 });
38083790 }
......@@ -3810,7 +3792,7 @@ fn performAllTheWorkInner(
38103792
38113793 for (0.., zcu.embed_table.values()) |ef_index_usize, ef| {
38123794 const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize);
3813 comp.thread_pool.spawnWgId(&astgen_wait_group, workerCheckEmbedFile, .{
3795 comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateEmbedFile, .{
38143796 comp, ef_index, ef,
38153797 });
38163798 }
......@@ -3832,12 +3814,64 @@ fn performAllTheWorkInner(
38323814 if (comp.zcu) |zcu| {
38333815 const pt: Zcu.PerThread = .activate(zcu, .main);
38343816 defer pt.deactivate();
3817
3818 // If the cache mode is `whole`, then add every source file to the cache manifest.
3819 switch (comp.cache_use) {
3820 .whole => |whole| if (whole.cache_manifest) |man| {
3821 const gpa = zcu.gpa;
3822 for (zcu.import_table.values()) |file_index| {
3823 const file = zcu.fileByIndex(file_index);
3824 const source = file.getSource(gpa) catch |err| {
3825 try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)});
3826 continue;
3827 };
3828 const resolved_path = try std.fs.path.resolve(gpa, &.{
3829 file.mod.root.root_dir.path orelse ".",
3830 file.mod.root.sub_path,
3831 file.sub_file_path,
3832 });
3833 errdefer gpa.free(resolved_path);
3834 whole.cache_manifest_mutex.lock();
3835 defer whole.cache_manifest_mutex.unlock();
3836 man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) {
3837 error.OutOfMemory => |e| return e,
3838 else => {
3839 try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
3840 continue;
3841 },
3842 };
3843 }
3844 },
3845 .incremental => {},
3846 }
3847
3848 try reportMultiModuleErrors(pt);
3849
3850 const any_fatal_files = for (zcu.import_table.values()) |file_index| {
3851 const file = zcu.fileByIndex(file_index);
3852 switch (file.status) {
3853 .never_loaded => unreachable, // everything is loaded by the workers
3854 .retryable_failure, .astgen_failure => break true,
3855 .success => {},
3856 }
3857 } else false;
3858
3859 if (any_fatal_files or comp.alloc_failure_occurred) {
3860 // We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents
3861 // us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
3862 // However, this means our analysis data is invalid, so we want to omit all analysis errors.
3863 // To do that, let's just clear the analysis roots!
3864
3865 assert(zcu.failed_files.count() > 0); // we will get an error
3866 zcu.analysis_roots.clear(); // no analysis happened
3867 return;
3868 }
3869
38353870 if (comp.incremental) {
38363871 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
38373872 defer update_zir_refs_node.end();
38383873 try pt.updateZirRefs();
38393874 }
3840 try reportMultiModuleErrors(pt);
38413875 try zcu.flushRetryableFailures();
38423876
38433877 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
......@@ -4280,7 +4314,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
42804314 };
42814315}
42824316
4283fn workerAstGenFile(
4317fn workerUpdateFile(
42844318 tid: usize,
42854319 comp: *Compilation,
42864320 file: *Zcu.File,
......@@ -4290,40 +4324,44 @@ fn workerAstGenFile(
42904324 wg: *WaitGroup,
42914325 src: Zcu.AstGenSrc,
42924326) void {
4293 assert(file.getMode() == .zig);
42944327 const child_prog_node = prog_node.start(file.sub_file_path, 0);
42954328 defer child_prog_node.end();
42964329
42974330 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
42984331 defer pt.deactivate();
4299 pt.astGenFile(file, path_digest) catch |err| switch (err) {
4332 pt.updateFile(file, path_digest) catch |err| switch (err) {
43004333 error.AnalysisFail => return,
43014334 else => {
4302 file.status = .retryable_failure;
43034335 pt.reportRetryableAstGenError(src, file_index, err) catch |oom| switch (oom) {
4304 // Swallowing this error is OK because it's implied to be OOM when
4305 // there is a missing `failed_files` error message.
4306 error.OutOfMemory => {},
4336 error.OutOfMemory => {
4337 comp.mutex.lock();
4338 defer comp.mutex.unlock();
4339 comp.setAllocFailure();
4340 },
43074341 };
43084342 return;
43094343 },
43104344 };
43114345
4346 switch (file.getMode()) {
4347 .zig => {}, // continue to logic below
4348 .zon => return, // ZON can't import anything so we're done
4349 }
4350
43124351 // Pre-emptively look for `@import` paths and queue them up.
43134352 // If we experience an error preemptively fetching the
43144353 // file, just ignore it and let it happen again later during Sema.
4315 assert(file.zir_loaded);
4316 const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
4354 const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
43174355 if (imports_index != 0) {
4318 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
4356 const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index);
43194357 var import_i: u32 = 0;
43204358 var extra_index = extra.end;
43214359
43224360 while (import_i < extra.data.imports_len) : (import_i += 1) {
4323 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
4361 const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
43244362 extra_index = item.end;
43254363
4326 const import_path = file.zir.nullTerminatedString(item.data.name);
4364 const import_path = file.zir.?.nullTerminatedString(item.data.name);
43274365 // `@import("builtin")` is handled specially.
43284366 if (mem.eql(u8, import_path, "builtin")) continue;
43294367
......@@ -4344,7 +4382,7 @@ fn workerAstGenFile(
43444382 const imported_path_digest = pt.zcu.filePathDigest(res.file_index);
43454383 break :blk .{ res, imported_path_digest };
43464384 };
4347 if (import_result.is_new and import_result.file.getMode() == .zig) {
4385 if (import_result.is_new) {
43484386 log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{
43494387 file.sub_file_path, import_path, import_result.file.sub_file_path,
43504388 });
......@@ -4352,7 +4390,7 @@ fn workerAstGenFile(
43524390 .importing_file = file_index,
43534391 .import_tok = item.data.token,
43544392 } };
4355 comp.thread_pool.spawnWgId(wg, workerAstGenFile, .{
4393 comp.thread_pool.spawnWgId(wg, workerUpdateFile, .{
43564394 comp, import_result.file, import_result.file_index, imported_path_digest, prog_node, wg, sub_src,
43574395 });
43584396 }
......@@ -4375,7 +4413,7 @@ fn workerUpdateBuiltinZigFile(
43754413 };
43764414}
43774415
4378fn workerCheckEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
4416fn workerUpdateEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
43794417 comp.detectEmbedFileUpdate(@enumFromInt(tid), ef_index, ef) catch |err| switch (err) {
43804418 error.OutOfMemory => {
43814419 comp.mutex.lock();
src/InternPool.zig+8-12
......@@ -17,13 +17,6 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32),
1717/// Cached shift amount to put a `tid` in the top bits of a 32-bit value.
1818tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32),
1919
20/// Dependencies on whether an entire file gets past AstGen.
21/// These are triggered by `@import`, so that:
22/// * if a file initially fails AstGen, triggering a transitive failure, when a future update
23/// causes it to succeed AstGen, the `@import` is re-analyzed, allowing analysis to proceed
24/// * if a file initially succeds AstGen, but a future update causes the file to fail it,
25/// the `@import` is re-analyzed, registering a transitive failure
26file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
2720/// Dependencies on the source code hash associated with a ZIR instruction.
2821/// * For a `declaration`, this is the entire declaration body.
2922/// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations).
......@@ -42,6 +35,9 @@ nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
4235/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)
4336/// Value is index into `dep_entries` of the first dependency on this interned value.
4437interned_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
38/// Dependencies on a ZON file. Triggered by `@import` of ZON.
39/// Value is index into `dep_entries` of the first dependency on this ZON file.
40zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
4541/// Dependencies on an embedded file.
4642/// Introduced by `@embedFile`; invalidated when the file changes.
4743/// Value is index into `dep_entries` of the first dependency on this `Zcu.EmbedFile`.
......@@ -89,11 +85,11 @@ pub const empty: InternPool = .{
8985 .tid_shift_30 = if (single_threaded) 0 else 31,
9086 .tid_shift_31 = if (single_threaded) 0 else 31,
9187 .tid_shift_32 = if (single_threaded) 0 else 31,
92 .file_deps = .empty,
9388 .src_hash_deps = .empty,
9489 .nav_val_deps = .empty,
9590 .nav_ty_deps = .empty,
9691 .interned_deps = .empty,
92 .zon_file_deps = .empty,
9793 .embed_file_deps = .empty,
9894 .namespace_deps = .empty,
9995 .namespace_name_deps = .empty,
......@@ -824,11 +820,11 @@ pub const Nav = struct {
824820};
825821
826822pub const Dependee = union(enum) {
827 file: FileIndex,
828823 src_hash: TrackedInst.Index,
829824 nav_val: Nav.Index,
830825 nav_ty: Nav.Index,
831826 interned: Index,
827 zon_file: FileIndex,
832828 embed_file: Zcu.EmbedFile.Index,
833829 namespace: TrackedInst.Index,
834830 namespace_name: NamespaceNameKey,
......@@ -876,11 +872,11 @@ pub const DependencyIterator = struct {
876872
877873pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator {
878874 const first_entry = switch (dependee) {
879 .file => |x| ip.file_deps.get(x),
880875 .src_hash => |x| ip.src_hash_deps.get(x),
881876 .nav_val => |x| ip.nav_val_deps.get(x),
882877 .nav_ty => |x| ip.nav_ty_deps.get(x),
883878 .interned => |x| ip.interned_deps.get(x),
879 .zon_file => |x| ip.zon_file_deps.get(x),
884880 .embed_file => |x| ip.embed_file_deps.get(x),
885881 .namespace => |x| ip.namespace_deps.get(x),
886882 .namespace_name => |x| ip.namespace_name_deps.get(x),
......@@ -947,11 +943,11 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
947943 },
948944 inline else => |dependee_payload, tag| new_index: {
949945 const gop = try switch (tag) {
950 .file => ip.file_deps,
951946 .src_hash => ip.src_hash_deps,
952947 .nav_val => ip.nav_val_deps,
953948 .nav_ty => ip.nav_ty_deps,
954949 .interned => ip.interned_deps,
950 .zon_file => ip.zon_file_deps,
955951 .embed_file => ip.embed_file_deps,
956952 .namespace => ip.namespace_deps,
957953 .namespace_name => ip.namespace_name_deps,
......@@ -6688,11 +6684,11 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
66886684pub fn deinit(ip: *InternPool, gpa: Allocator) void {
66896685 if (debug_state.enable_checks) std.debug.assert(debug_state.intern_pool == null);
66906686
6691 ip.file_deps.deinit(gpa);
66926687 ip.src_hash_deps.deinit(gpa);
66936688 ip.nav_val_deps.deinit(gpa);
66946689 ip.nav_ty_deps.deinit(gpa);
66956690 ip.interned_deps.deinit(gpa);
6691 ip.zon_file_deps.deinit(gpa);
66966692 ip.embed_file_deps.deinit(gpa);
66976693 ip.namespace_deps.deinit(gpa);
66986694 ip.namespace_name_deps.deinit(gpa);
src/Package/Module.zig+4-7
......@@ -482,15 +482,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
482482 };
483483 new_file.* = .{
484484 .sub_file_path = "builtin.zig",
485 .source = generated_builtin_source,
486 .source_loaded = true,
487 .tree_loaded = false,
488 .zir_loaded = false,
489485 .stat = undefined,
490 .tree = undefined,
491 .zir = undefined,
486 .source = generated_builtin_source,
487 .tree = null,
488 .zir = null,
489 .zoir = null,
492490 .status = .never_loaded,
493 .prev_status = .never_loaded,
494491 .mod = new,
495492 };
496493 break :b new;
src/Sema.zig+8-16
......@@ -6140,10 +6140,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
61406140 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
61416141
61426142 const path_digest = zcu.filePathDigest(result.file_index);
6143 pt.astGenFile(result.file, path_digest) catch |err|
6143 pt.updateFile(result.file, path_digest) catch |err|
61446144 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
61456145
6146 try sema.declareDependency(.{ .file = result.file_index });
61476146 try pt.ensureFileAnalyzed(result.file_index);
61486147 const ty = zcu.fileRootType(result.file_index);
61496148 try sema.declareDependency(.{ .interned = ty });
......@@ -7649,9 +7648,8 @@ fn analyzeCall(
76497648 const nav = ip.getNav(info.owner_nav);
76507649 const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
76517650 const file = zcu.fileByIndex(resolved_func_inst.file);
7652 assert(file.zir_loaded);
7653 const zir_info = file.zir.getFnInfo(resolved_func_inst.inst);
7654 break :b .{ nav, file.zir, info.zir_body_inst, resolved_func_inst.inst, zir_info };
7651 const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst);
7652 break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info };
76557653 } else .{ undefined, undefined, undefined, undefined, undefined };
76567654
76577655 // This is the `inst_map` used when evaluating generic parameters and return types.
......@@ -13987,7 +13985,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1398713985 };
1398813986 switch (result.file.getMode()) {
1398913987 .zig => {
13990 try sema.declareDependency(.{ .file = result.file_index });
1399113988 try pt.ensureFileAnalyzed(result.file_index);
1399213989 const ty = zcu.fileRootType(result.file_index);
1399313990 try sema.declareDependency(.{ .interned = ty });
......@@ -13995,12 +13992,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1399513992 return Air.internedToRef(ty);
1399613993 },
1399713994 .zon => {
13998 _ = result.file.getTree(zcu.gpa) catch |err| {
13999 // TODO: these errors are file system errors; make sure an update() will
14000 // retry this and not cache the file system error, which may be transient.
14001 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ result.file.sub_file_path, @errorName(err) });
14002 };
14003
1400413995 if (extra.res_ty == .none) {
1400513996 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
1400613997 }
......@@ -14010,6 +14001,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1401014001 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
1401114002 }
1401214003
14004 try sema.declareDependency(.{ .zon_file = result.file_index });
1401314005 const interned = try LowerZon.run(
1401414006 sema,
1401514007 result.file,
......@@ -35328,7 +35320,7 @@ fn backingIntType(
3532835320 break :blk accumulator;
3532935321 };
3533035322
35331 const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir;
35323 const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?;
3533235324 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
3533335325 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
3533435326 assert(extended.opcode == .struct_decl);
......@@ -35948,7 +35940,7 @@ fn structFields(
3594835940 const gpa = zcu.gpa;
3594935941 const ip = &zcu.intern_pool;
3595035942 const namespace_index = struct_type.namespace;
35951 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
35943 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
3595235944 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
3595335945
3595435946 const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
......@@ -36149,7 +36141,7 @@ fn structFieldInits(
3614936141 assert(!struct_type.haveFieldInits(ip));
3615036142
3615136143 const namespace_index = struct_type.namespace;
36152 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
36144 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
3615336145 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
3615436146 const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
3615536147
......@@ -36268,7 +36260,7 @@ fn unionFields(
3626836260 const zcu = pt.zcu;
3626936261 const gpa = zcu.gpa;
3627036262 const ip = &zcu.intern_pool;
36271 const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir;
36263 const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?;
3627236264 const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
3627336265 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
3627436266 assert(extended.opcode == .union_decl);
src/Sema/LowerZon.zig-2
......@@ -39,8 +39,6 @@ pub fn run(
3939) CompileError!InternPool.Index {
4040 const pt = sema.pt;
4141
42 _ = try file.getZoir(pt.zcu);
43
4442 const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
4543 .file = file_index,
4644 .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file
src/Type.zig+3-4
......@@ -3587,8 +3587,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
35873587 };
35883588 const info = tracked.resolveFull(&zcu.intern_pool) orelse return null;
35893589 const file = zcu.fileByIndex(info.file);
3590 assert(file.zir_loaded);
3591 const zir = file.zir;
3590 const zir = file.zir.?;
35923591 const inst = zir.instructions.get(@intFromEnum(info.inst));
35933592 return switch (inst.tag) {
35943593 .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line,
......@@ -3905,7 +3904,7 @@ fn resolveStructInner(
39053904 var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);
39063905 defer comptime_err_ret_trace.deinit();
39073906
3908 const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir;
3907 const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir.?;
39093908 var sema: Sema = .{
39103909 .pt = pt,
39113910 .gpa = gpa,
......@@ -3959,7 +3958,7 @@ fn resolveUnionInner(
39593958 var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);
39603959 defer comptime_err_ret_trace.deinit();
39613960
3962 const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir;
3961 const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir.?;
39633962 var sema: Sema = .{
39643963 .pt = pt,
39653964 .gpa = gpa,
src/Zcu.zig+262-90
......@@ -658,24 +658,35 @@ pub const Namespace = struct {
658658};
659659
660660pub const File = struct {
661 status: Status,
662 prev_status: Status,
663 source_loaded: bool,
664 tree_loaded: bool,
665 zir_loaded: bool,
666661 /// Relative to the owning package's root source directory.
667662 /// Memory is stored in gpa, owned by File.
668663 sub_file_path: []const u8,
669 /// Whether this is populated depends on `source_loaded`.
670 source: [:0]const u8,
664
665 status: enum {
666 /// We have not yet attempted to load this file.
667 /// `stat` is not populated and may be `undefined`.
668 never_loaded,
669 /// A filesystem access failed. It should be retried on the next update.
670 /// There is a `failed_files` entry containing a non-`null` message.
671 /// `stat` is not populated and may be `undefined`.
672 retryable_failure,
673 /// Parsing/AstGen/ZonGen of this file has failed.
674 /// There is an error in `zir` or `zoir`.
675 /// There is a `failed_files` entry (with a `null` message).
676 /// `stat` is populated.
677 astgen_failure,
678 /// Parsing and AstGen/ZonGen of this file has succeeded.
679 /// `stat` is populated.
680 success,
681 },
671682 /// Whether this is populated depends on `status`.
672683 stat: Cache.File.Stat,
673 /// Whether this is populated or not depends on `tree_loaded`.
674 tree: Ast,
675 /// Whether this is populated or not depends on `zir_loaded`.
676 zir: Zir,
677 /// Cached Zoir, generated lazily.
678 zoir: ?Zoir = null,
684
685 source: ?[:0]const u8,
686 tree: ?Ast,
687 zir: ?Zir,
688 zoir: ?Zoir,
689
679690 /// Module that this file is a part of, managed externally.
680691 mod: *Package.Module,
681692 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
......@@ -683,19 +694,24 @@ pub const File = struct {
683694 /// List of references to this file, used for multi-package errors.
684695 references: std.ArrayListUnmanaged(File.Reference) = .empty,
685696
686 /// The most recent successful ZIR for this file, with no errors.
687 /// This is only populated when a previously successful ZIR
688 /// newly introduces compile errors during an update. When ZIR is
689 /// successful, this field is unloaded.
697 /// The ZIR for this file from the last update with no file failures. As such, this ZIR is never
698 /// failed (although it may have compile errors).
699 ///
700 /// Because updates with file failures do not perform ZIR mapping or semantic analysis, we keep
701 /// this around so we have the "old" ZIR to map when an update is ready to do so. Once such an
702 /// update occurs, this field is unloaded, since it is no longer necessary.
703 ///
704 /// In other words, if `TrackedInst`s are tied to ZIR other than what's in the `zir` field, this
705 /// field is populated with that old ZIR.
690706 prev_zir: ?*Zir = null,
691707
692 pub const Status = enum {
693 never_loaded,
694 retryable_failure,
695 parse_failure,
696 astgen_failure,
697 success_zir,
698 };
708 /// This field serves a similar purpose to `prev_zir`, but for ZOIR. However, since we do not
709 /// need to map old ZOIR to new ZOIR -- instead only invalidating dependencies if the ZOIR
710 /// changed -- this field is just a simple boolean.
711 ///
712 /// When `zoir` is updated, this field is set to `true`. In `updateZirRefs`, if this is `true`,
713 /// we invalidate the corresponding `zon_file` dependency, and reset it to `false`.
714 zoir_invalidated: bool = false,
699715
700716 /// A single reference to a file.
701717 pub const Reference = union(enum) {
......@@ -727,23 +743,23 @@ pub const File = struct {
727743 }
728744
729745 pub fn unloadTree(file: *File, gpa: Allocator) void {
730 if (file.tree_loaded) {
731 file.tree_loaded = false;
732 file.tree.deinit(gpa);
746 if (file.tree) |*tree| {
747 tree.deinit(gpa);
748 file.tree = null;
733749 }
734750 }
735751
736752 pub fn unloadSource(file: *File, gpa: Allocator) void {
737 if (file.source_loaded) {
738 file.source_loaded = false;
739 gpa.free(file.source);
753 if (file.source) |source| {
754 gpa.free(source);
755 file.source = null;
740756 }
741757 }
742758
743759 pub fn unloadZir(file: *File, gpa: Allocator) void {
744 if (file.zir_loaded) {
745 file.zir_loaded = false;
746 file.zir.deinit(gpa);
760 if (file.zir) |*zir| {
761 zir.deinit(gpa);
762 file.zir = null;
747763 }
748764 }
749765
......@@ -753,8 +769,8 @@ pub const File = struct {
753769 };
754770
755771 pub fn getSource(file: *File, gpa: Allocator) !Source {
756 if (file.source_loaded) return Source{
757 .bytes = file.source,
772 if (file.source) |source| return .{
773 .bytes = source,
758774 .stat = file.stat,
759775 };
760776
......@@ -769,18 +785,20 @@ pub const File = struct {
769785 return error.FileTooBig;
770786
771787 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
772 defer if (!file.source_loaded) gpa.free(source);
788 errdefer gpa.free(source);
789
773790 const amt = try f.readAll(source);
774791 if (amt != stat.size)
775792 return error.UnexpectedEndOfFile;
776793
777794 // Here we do not modify stat fields because this function is the one
778795 // used for error reporting. We need to keep the stat fields stale so that
779 // astGenFile can know to regenerate ZIR.
796 // updateFile can know to regenerate ZIR.
780797
781798 file.source = source;
782 file.source_loaded = true;
783 return Source{
799 errdefer comptime unreachable; // don't error after populating `source`
800
801 return .{
784802 .bytes = source,
785803 .stat = .{
786804 .size = stat.size,
......@@ -791,20 +809,20 @@ pub const File = struct {
791809 }
792810
793811 pub fn getTree(file: *File, gpa: Allocator) !*const Ast {
794 if (file.tree_loaded) return &file.tree;
812 if (file.tree) |*tree| return tree;
795813
796814 const source = try file.getSource(gpa);
797 file.tree = try Ast.parse(gpa, source.bytes, file.getMode());
798 file.tree_loaded = true;
799 return &file.tree;
815 file.tree = try .parse(gpa, source.bytes, file.getMode());
816 return &file.tree.?;
800817 }
801818
802819 pub fn getZoir(file: *File, zcu: *Zcu) !*const Zoir {
803820 if (file.zoir) |*zoir| return zoir;
804821
805 assert(file.tree_loaded);
806 assert(file.tree.mode == .zon);
807 file.zoir = try ZonGen.generate(zcu.gpa, file.tree, .{});
822 const tree = file.tree.?;
823 assert(tree.mode == .zon);
824
825 file.zoir = try ZonGen.generate(zcu.gpa, tree, .{});
808826 if (file.zoir.?.hasCompileErrors()) {
809827 try zcu.failed_files.putNoClobber(zcu.gpa, file, null);
810828 return error.AnalysisFail;
......@@ -854,13 +872,6 @@ pub const File = struct {
854872 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
855873 }
856874
857 pub fn okToReportErrors(file: File) bool {
858 return switch (file.status) {
859 .parse_failure, .astgen_failure => false,
860 else => true,
861 };
862 }
863
864875 /// Add a reference to this file during AstGen.
865876 pub fn addReference(file: *File, zcu: *Zcu, ref: File.Reference) !void {
866877 // Don't add the same module root twice. Note that since we always add module roots at the
......@@ -900,18 +911,18 @@ pub const File = struct {
900911
901912 // We can only mark children as failed if the ZIR is loaded, which may not
902913 // be the case if there were other astgen failures in this file
903 if (!file.zir_loaded) return;
914 if (file.zir == null) return;
904915
905 const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
916 const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
906917 if (imports_index == 0) return;
907 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
918 const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index);
908919
909920 var extra_index = extra.end;
910921 for (0..extra.data.imports_len) |_| {
911 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
922 const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
912923 extra_index = item.end;
913924
914 const import_path = file.zir.nullTerminatedString(item.data.name);
925 const import_path = file.zir.?.nullTerminatedString(item.data.name);
915926 if (mem.eql(u8, import_path, "builtin")) continue;
916927
917928 const res = pt.importFile(file, import_path) catch continue;
......@@ -1012,7 +1023,7 @@ pub const SrcLoc = struct {
10121023 lazy: LazySrcLoc.Offset,
10131024
10141025 pub fn baseSrcToken(src_loc: SrcLoc) Ast.TokenIndex {
1015 const tree = src_loc.file_scope.tree;
1026 const tree = src_loc.file_scope.tree.?;
10161027 return tree.firstToken(src_loc.base_node);
10171028 }
10181029
......@@ -1057,7 +1068,6 @@ pub const SrcLoc = struct {
10571068 const node_off = traced_off.x;
10581069 const tree = try src_loc.file_scope.getTree(gpa);
10591070 const node = src_loc.relativeToNodeIndex(node_off);
1060 assert(src_loc.file_scope.tree_loaded);
10611071 return tree.nodeToSpan(node);
10621072 },
10631073 .node_offset_main_token => |node_off| {
......@@ -1069,7 +1079,6 @@ pub const SrcLoc = struct {
10691079 .node_offset_bin_op => |node_off| {
10701080 const tree = try src_loc.file_scope.getTree(gpa);
10711081 const node = src_loc.relativeToNodeIndex(node_off);
1072 assert(src_loc.file_scope.tree_loaded);
10731082 return tree.nodeToSpan(node);
10741083 },
10751084 .node_offset_initializer => |node_off| {
......@@ -2408,9 +2417,8 @@ pub const LazySrcLoc = struct {
24082417 if (zir_inst == .main_struct_inst) return .{ file, 0 };
24092418
24102419 // Otherwise, make sure ZIR is loaded.
2411 assert(file.zir_loaded);
2420 const zir = file.zir.?;
24122421
2413 const zir = file.zir;
24142422 const inst = zir.instructions.get(@intFromEnum(zir_inst));
24152423 const base_node: Ast.Node.Index = switch (inst.tag) {
24162424 .declaration => inst.data.declaration.src_node,
......@@ -2643,6 +2651,189 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.F
26432651 return zir;
26442652}
26452653
2654pub fn saveZirCache(gpa: Allocator, cache_file: std.fs.File, stat: std.fs.File.Stat, zir: Zir) (std.fs.File.WriteError || Allocator.Error)!void {
2655 const safety_buffer = if (data_has_safety_tag)
2656 try gpa.alloc([8]u8, zir.instructions.len)
2657 else
2658 undefined;
2659 defer if (data_has_safety_tag) gpa.free(safety_buffer);
2660
2661 const data_ptr: [*]const u8 = if (data_has_safety_tag)
2662 if (zir.instructions.len == 0)
2663 undefined
2664 else
2665 @ptrCast(safety_buffer.ptr)
2666 else
2667 @ptrCast(zir.instructions.items(.data).ptr);
2668
2669 if (data_has_safety_tag) {
2670 // The `Data` union has a safety tag but in the file format we store it without.
2671 for (zir.instructions.items(.data), 0..) |*data, i| {
2672 const as_struct: *const HackDataLayout = @ptrCast(data);
2673 safety_buffer[i] = as_struct.data;
2674 }
2675 }
2676
2677 const header: Zir.Header = .{
2678 .instructions_len = @intCast(zir.instructions.len),
2679 .string_bytes_len = @intCast(zir.string_bytes.len),
2680 .extra_len = @intCast(zir.extra.len),
2681
2682 .stat_size = stat.size,
2683 .stat_inode = stat.inode,
2684 .stat_mtime = stat.mtime,
2685 };
2686 var iovecs: [5]std.posix.iovec_const = .{
2687 .{
2688 .base = @ptrCast(&header),
2689 .len = @sizeOf(Zir.Header),
2690 },
2691 .{
2692 .base = @ptrCast(zir.instructions.items(.tag).ptr),
2693 .len = zir.instructions.len,
2694 },
2695 .{
2696 .base = data_ptr,
2697 .len = zir.instructions.len * 8,
2698 },
2699 .{
2700 .base = zir.string_bytes.ptr,
2701 .len = zir.string_bytes.len,
2702 },
2703 .{
2704 .base = @ptrCast(zir.extra.ptr),
2705 .len = zir.extra.len * 4,
2706 },
2707 };
2708 try cache_file.writevAll(&iovecs);
2709}
2710
2711pub fn saveZoirCache(cache_file: std.fs.File, stat: std.fs.File.Stat, zoir: Zoir) std.fs.File.WriteError!void {
2712 const header: Zoir.Header = .{
2713 .nodes_len = @intCast(zoir.nodes.len),
2714 .extra_len = @intCast(zoir.extra.len),
2715 .limbs_len = @intCast(zoir.limbs.len),
2716 .string_bytes_len = @intCast(zoir.string_bytes.len),
2717 .compile_errors_len = @intCast(zoir.compile_errors.len),
2718 .error_notes_len = @intCast(zoir.error_notes.len),
2719
2720 .stat_size = stat.size,
2721 .stat_inode = stat.inode,
2722 .stat_mtime = stat.mtime,
2723 };
2724 var iovecs: [9]std.posix.iovec_const = .{
2725 .{
2726 .base = @ptrCast(&header),
2727 .len = @sizeOf(Zoir.Header),
2728 },
2729 .{
2730 .base = @ptrCast(zoir.nodes.items(.tag)),
2731 .len = zoir.nodes.len * @sizeOf(Zoir.Node.Repr.Tag),
2732 },
2733 .{
2734 .base = @ptrCast(zoir.nodes.items(.data)),
2735 .len = zoir.nodes.len * 4,
2736 },
2737 .{
2738 .base = @ptrCast(zoir.nodes.items(.ast_node)),
2739 .len = zoir.nodes.len * 4,
2740 },
2741 .{
2742 .base = @ptrCast(zoir.extra),
2743 .len = zoir.extra.len * 4,
2744 },
2745 .{
2746 .base = @ptrCast(zoir.limbs),
2747 .len = zoir.limbs.len * 4,
2748 },
2749 .{
2750 .base = zoir.string_bytes.ptr,
2751 .len = zoir.string_bytes.len,
2752 },
2753 .{
2754 .base = @ptrCast(zoir.compile_errors),
2755 .len = zoir.compile_errors.len * @sizeOf(Zoir.CompileError),
2756 },
2757 .{
2758 .base = @ptrCast(zoir.error_notes),
2759 .len = zoir.error_notes.len * @sizeOf(Zoir.CompileError.Note),
2760 },
2761 };
2762 try cache_file.writevAll(&iovecs);
2763}
2764
2765pub fn loadZoirCacheBody(gpa: Allocator, header: Zoir.Header, cache_file: std.fs.File) !Zoir {
2766 var zoir: Zoir = .{
2767 .nodes = .empty,
2768 .extra = &.{},
2769 .limbs = &.{},
2770 .string_bytes = &.{},
2771 .compile_errors = &.{},
2772 .error_notes = &.{},
2773 };
2774 errdefer zoir.deinit(gpa);
2775
2776 zoir.nodes = nodes: {
2777 var nodes: std.MultiArrayList(Zoir.Node.Repr) = .empty;
2778 defer nodes.deinit(gpa);
2779 try nodes.setCapacity(gpa, header.nodes_len);
2780 nodes.len = header.nodes_len;
2781 break :nodes nodes.toOwnedSlice();
2782 };
2783
2784 zoir.extra = try gpa.alloc(u32, header.extra_len);
2785 zoir.limbs = try gpa.alloc(std.math.big.Limb, header.limbs_len);
2786 zoir.string_bytes = try gpa.alloc(u8, header.string_bytes_len);
2787
2788 zoir.compile_errors = try gpa.alloc(Zoir.CompileError, header.compile_errors_len);
2789 zoir.error_notes = try gpa.alloc(Zoir.CompileError.Note, header.error_notes_len);
2790
2791 var iovecs: [8]std.posix.iovec = .{
2792 .{
2793 .base = @ptrCast(zoir.nodes.items(.tag)),
2794 .len = header.nodes_len * @sizeOf(Zoir.Node.Repr.Tag),
2795 },
2796 .{
2797 .base = @ptrCast(zoir.nodes.items(.data)),
2798 .len = header.nodes_len * 4,
2799 },
2800 .{
2801 .base = @ptrCast(zoir.nodes.items(.ast_node)),
2802 .len = header.nodes_len * 4,
2803 },
2804 .{
2805 .base = @ptrCast(zoir.extra),
2806 .len = header.extra_len * 4,
2807 },
2808 .{
2809 .base = @ptrCast(zoir.limbs),
2810 .len = header.limbs_len * @sizeOf(std.math.big.Limb),
2811 },
2812 .{
2813 .base = zoir.string_bytes.ptr,
2814 .len = header.string_bytes_len,
2815 },
2816 .{
2817 .base = @ptrCast(zoir.compile_errors),
2818 .len = header.compile_errors_len * @sizeOf(Zoir.CompileError),
2819 },
2820 .{
2821 .base = @ptrCast(zoir.error_notes),
2822 .len = header.error_notes_len * @sizeOf(Zoir.CompileError.Note),
2823 },
2824 };
2825
2826 const bytes_expected = expected: {
2827 var n: usize = 0;
2828 for (iovecs) |v| n += v.len;
2829 break :expected n;
2830 };
2831
2832 const bytes_read = try cache_file.readvAll(&iovecs);
2833 if (bytes_read != bytes_expected) return error.UnexpectedFileSize;
2834 return zoir;
2835}
2836
26462837pub fn markDependeeOutdated(
26472838 zcu: *Zcu,
26482839 /// When we are diffing ZIR and marking things as outdated, we won't yet have marked the dependencies as PO.
......@@ -3303,19 +3494,6 @@ pub fn optimizeMode(zcu: *const Zcu) std.builtin.OptimizeMode {
33033494 return zcu.root_mod.optimize_mode;
33043495}
33053496
3306fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void {
3307 switch (file.status) {
3308 .success_zir, .retryable_failure => {},
3309 .never_loaded, .parse_failure, .astgen_failure => {
3310 zcu.comp.mutex.lock();
3311 defer zcu.comp.mutex.unlock();
3312 if (zcu.failed_files.fetchSwapRemove(file)) |kv| {
3313 if (kv.value) |msg| msg.destroy(zcu.gpa); // Delete previous error message.
3314 }
3315 },
3316 }
3317}
3318
33193497pub fn handleUpdateExports(
33203498 zcu: *Zcu,
33213499 export_indices: []const Export.Index,
......@@ -3670,9 +3848,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
36703848 // `test` declarations are analyzed depending on the test filter.
36713849 const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;
36723850 const file = zcu.fileByIndex(inst_info.file);
3673 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3674 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3675 const decl = zir.getDeclaration(inst_info.inst);
3851 const decl = file.zir.?.getDeclaration(inst_info.inst);
36763852
36773853 if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
36783854
......@@ -3702,9 +3878,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
37023878 // These are named declarations. They are analyzed only if marked `export`.
37033879 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
37043880 const file = zcu.fileByIndex(inst_info.file);
3705 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3706 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3707 const decl = zir.getDeclaration(inst_info.inst);
3881 const decl = file.zir.?.getDeclaration(inst_info.inst);
37083882 if (decl.linkage == .@"export") {
37093883 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
37103884 if (!result.contains(unit)) {
......@@ -3720,9 +3894,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
37203894 // These are named declarations. They are analyzed only if marked `export`.
37213895 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
37223896 const file = zcu.fileByIndex(inst_info.file);
3723 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3724 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3725 const decl = zir.getDeclaration(inst_info.inst);
3897 const decl = file.zir.?.getDeclaration(inst_info.inst);
37263898 if (decl.linkage == .@"export") {
37273899 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
37283900 if (!result.contains(unit)) {
......@@ -3858,7 +4030,7 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
38584030 const ip = &zcu.intern_pool;
38594031 const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;
38604032 const zir = zcu.fileByIndex(inst_info.file).zir;
3861 return zir.getDeclaration(inst_info.inst).src_line;
4033 return zir.?.getDeclaration(inst_info.inst).src_line;
38624034}
38634035
38644036pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
......@@ -3910,10 +4082,6 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
39104082 const zcu = data.zcu;
39114083 const ip = &zcu.intern_pool;
39124084 switch (data.dependee) {
3913 .file => |file| {
3914 const file_path = zcu.fileByIndex(file).sub_file_path;
3915 return writer.print("file('{s}')", .{file_path});
3916 },
39174085 .src_hash => |ti| {
39184086 const info = ti.resolveFull(ip) orelse {
39194087 return writer.writeAll("inst(<lost>)");
......@@ -3934,6 +4102,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
39344102 .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
39354103 else => unreachable,
39364104 },
4105 .zon_file => |file| {
4106 const file_path = zcu.fileByIndex(file).sub_file_path;
4107 return writer.print("zon_file('{s}')", .{file_path});
4108 },
39374109 .embed_file => |ef_idx| {
39384110 const ef = ef_idx.get(zcu);
39394111 return writer.print("embed_file('{s}')", .{std.fs.path.fmtJoin(&.{
src/Zcu/PerThread.zig+263-288
......@@ -26,6 +26,8 @@ const Type = @import("../Type.zig");
2626const Value = @import("../Value.zig");
2727const Zcu = @import("../Zcu.zig");
2828const Zir = std.zig.Zir;
29const Zoir = std.zig.Zoir;
30const ZonGen = std.zig.ZonGen;
2931
3032zcu: *Zcu,
3133
......@@ -73,7 +75,9 @@ pub fn destroyFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void {
7375 if (!is_builtin) gpa.destroy(file);
7476}
7577
76pub fn astGenFile(
78/// Ensures that `file` has up-to-date ZIR. If not, loads the ZIR cache or runs
79/// AstGen as needed. Also updates `file.status`.
80pub fn updateFile(
7781 pt: Zcu.PerThread,
7882 file: *Zcu.File,
7983 path_digest: Cache.BinDigest,
......@@ -109,7 +113,7 @@ pub fn astGenFile(
109113
110114 break :lock .shared;
111115 },
112 .parse_failure, .astgen_failure, .success_zir => lock: {
116 .astgen_failure, .success => lock: {
113117 const unchanged_metadata =
114118 stat.size == file.stat.size and
115119 stat.mtime == file.stat.mtime and
......@@ -126,6 +130,27 @@ pub fn astGenFile(
126130 },
127131 };
128132
133 // The old compile error, if any, is no longer relevant.
134 pt.lockAndClearFileCompileError(file);
135
136 // If `zir` is not null, and `prev_zir` is null, then `TrackedInst`s are associated with `zir`.
137 // We need to keep it around!
138 // As an optimization, also check `loweringFailed`; if true, but `prev_zir == null`, then this
139 // file has never passed AstGen, so we actually need not cache the old ZIR.
140 if (file.zir != null and file.prev_zir == null and !file.zir.?.loweringFailed()) {
141 assert(file.prev_zir == null);
142 const prev_zir_ptr = try gpa.create(Zir);
143 file.prev_zir = prev_zir_ptr;
144 prev_zir_ptr.* = file.zir.?;
145 file.zir = null;
146 }
147
148 // If ZOIR is changing, then we need to invalidate dependencies on it
149 if (file.zoir != null) file.zoir_invalidated = true;
150
151 // We're going to re-load everything, so unload source, AST, ZIR, ZOIR.
152 file.unload(gpa);
153
129154 // We ask for a lock in order to coordinate with other zig processes.
130155 // If another process is already working on this file, we will get the cached
131156 // version. Likewise if we're working on AstGen and another process asks for
......@@ -180,190 +205,164 @@ pub fn astGenFile(
180205 };
181206 defer cache_file.close();
182207
183 while (true) {
184 update: {
185 // First we read the header to determine the lengths of arrays.
186 const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) {
187 // This can happen if Zig bails out of this function between creating
188 // the cached file and writing it.
189 error.EndOfStream => break :update,
190 else => |e| return e,
191 };
192 const unchanged_metadata =
193 stat.size == header.stat_size and
194 stat.mtime == header.stat_mtime and
195 stat.inode == header.stat_inode;
196
197 if (!unchanged_metadata) {
198 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
199 break :update;
200 }
201 log.debug("AstGen cache hit: {s} instructions_len={d}", .{
202 file.sub_file_path, header.instructions_len,
203 });
204
205 file.zir = Zcu.loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
206 error.UnexpectedFileSize => {
207 log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
208 break :update;
209 },
210 else => |e| return e,
211 };
212 file.zir_loaded = true;
213 file.stat = .{
214 .size = header.stat_size,
215 .inode = header.stat_inode,
216 .mtime = header.stat_mtime,
217 };
218 file.prev_status = file.status;
219 file.status = .success_zir;
220 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
221
222 if (file.zir.hasCompileErrors()) {
223 comp.mutex.lock();
224 defer comp.mutex.unlock();
225 try zcu.failed_files.putNoClobber(gpa, file, null);
226 }
227 if (file.zir.loweringFailed()) {
228 file.status = .astgen_failure;
229 return error.AnalysisFail;
230 }
231 return;
208 const need_update = while (true) {
209 const result = switch (file.getMode()) {
210 inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode),
211 };
212 switch (result) {
213 .success => {
214 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
215 break false;
216 },
217 .invalid => {},
218 .truncated => log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}),
219 .stale => log.debug("AstGen cache stale: {s}", .{file.sub_file_path}),
232220 }
233221
234222 // If we already have the exclusive lock then it is our job to update.
235 if (builtin.os.tag == .wasi or lock == .exclusive) break;
223 if (builtin.os.tag == .wasi or lock == .exclusive) break true;
236224 // Otherwise, unlock to give someone a chance to get the exclusive lock
237225 // and then upgrade to an exclusive lock.
238226 cache_file.unlock();
239227 lock = .exclusive;
240228 try cache_file.lock(lock);
241 }
229 };
242230
243 // The cache is definitely stale so delete the contents to avoid an underwrite later.
244 cache_file.setEndPos(0) catch |err| switch (err) {
245 error.FileTooBig => unreachable, // 0 is not too big
231 if (need_update) {
232 // The cache is definitely stale so delete the contents to avoid an underwrite later.
233 cache_file.setEndPos(0) catch |err| switch (err) {
234 error.FileTooBig => unreachable, // 0 is not too big
235 else => |e| return e,
236 };
246237
247 else => |e| return e,
248 };
238 if (stat.size > std.math.maxInt(u32))
239 return error.FileTooBig;
249240
250 pt.lockAndClearFileCompileError(file);
241 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
242 defer if (file.source == null) gpa.free(source);
243 const amt = try source_file.readAll(source);
244 if (amt != stat.size)
245 return error.UnexpectedEndOfFile;
251246
252 // Previous ZIR is kept for two reasons:
253 //
254 // 1. In case an update to the file causes a Parse or AstGen failure, we
255 // need to compare two successful ZIR files in order to proceed with an
256 // incremental update. This avoids needlessly tossing out semantic
257 // analysis work when an error is temporarily introduced.
258 //
259 // 2. In order to detect updates, we need to iterate over the intern pool
260 // values while comparing old ZIR to new ZIR. This is better done in a
261 // single-threaded context, so we need to keep both versions around
262 // until that point in the pipeline. Previous ZIR data is freed after
263 // that.
264 if (file.zir_loaded and !file.zir.loweringFailed()) {
265 assert(file.prev_zir == null);
266 const prev_zir_ptr = try gpa.create(Zir);
267 file.prev_zir = prev_zir_ptr;
268 prev_zir_ptr.* = file.zir;
269 file.zir = undefined;
270 file.zir_loaded = false;
271 }
272 file.unload(gpa);
247 file.source = source;
248
249 // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.
250 file.tree = try Ast.parse(gpa, source, file.getMode());
273251
274 if (stat.size > std.math.maxInt(u32))
275 return error.FileTooBig;
252 switch (file.getMode()) {
253 .zig => {
254 file.zir = try AstGen.generate(gpa, file.tree.?);
255 Zcu.saveZirCache(gpa, cache_file, stat, file.zir.?) catch |err| switch (err) {
256 error.OutOfMemory => |e| return e,
257 else => log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{
258 file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
259 }),
260 };
261 },
262 .zon => {
263 file.zoir = try ZonGen.generate(gpa, file.tree.?, .{});
264 Zcu.saveZoirCache(cache_file, stat, file.zoir.?) catch |err| {
265 log.warn("unable to write cached ZOIR code for {}{s} to {}{s}: {s}", .{
266 file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
267 });
268 };
269 },
270 }
276271
277 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
278 defer if (!file.source_loaded) gpa.free(source);
279 const amt = try source_file.readAll(source);
280 if (amt != stat.size)
281 return error.UnexpectedEndOfFile;
272 log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
273 }
282274
283275 file.stat = .{
284276 .size = stat.size,
285277 .inode = stat.inode,
286278 .mtime = stat.mtime,
287279 };
288 file.source = source;
289 file.source_loaded = true;
290280
291 file.tree = try Ast.parse(gpa, source, .zig);
292 file.tree_loaded = true;
281 // Now, `zir` or `zoir` is definitely populated and up-to-date.
282 // Mark file successes/failures as needed.
293283
294 // Any potential AST errors are converted to ZIR errors here.
295 file.zir = try AstGen.generate(gpa, file.tree);
296 file.zir_loaded = true;
297 file.prev_status = file.status;
298 file.status = .success_zir;
299 log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
284 switch (file.getMode()) {
285 .zig => {
286 if (file.zir.?.hasCompileErrors()) {
287 comp.mutex.lock();
288 defer comp.mutex.unlock();
289 try zcu.failed_files.putNoClobber(gpa, file, null);
290 }
291 if (file.zir.?.loweringFailed()) {
292 file.status = .astgen_failure;
293 } else {
294 file.status = .success;
295 }
296 },
297 .zon => {
298 if (file.zoir.?.hasCompileErrors()) {
299 file.status = .astgen_failure;
300 comp.mutex.lock();
301 defer comp.mutex.unlock();
302 try zcu.failed_files.putNoClobber(gpa, file, null);
303 } else {
304 file.status = .success;
305 }
306 },
307 }
300308
301 const safety_buffer = if (Zcu.data_has_safety_tag)
302 try gpa.alloc([8]u8, file.zir.instructions.len)
303 else
304 undefined;
305 defer if (Zcu.data_has_safety_tag) gpa.free(safety_buffer);
306 const data_ptr = if (Zcu.data_has_safety_tag)
307 if (file.zir.instructions.len == 0)
308 @as([*]const u8, undefined)
309 else
310 @as([*]const u8, @ptrCast(safety_buffer.ptr))
311 else
312 @as([*]const u8, @ptrCast(file.zir.instructions.items(.data).ptr));
313 if (Zcu.data_has_safety_tag) {
314 // The `Data` union has a safety tag but in the file format we store it without.
315 for (file.zir.instructions.items(.data), 0..) |*data, i| {
316 const as_struct: *const Zcu.HackDataLayout = @ptrCast(data);
317 safety_buffer[i] = as_struct.data;
318 }
309 switch (file.status) {
310 .never_loaded => unreachable,
311 .retryable_failure => unreachable,
312 .astgen_failure => return error.AnalysisFail,
313 .success => return,
319314 }
315}
320316
321 const header: Zir.Header = .{
322 .instructions_len = @as(u32, @intCast(file.zir.instructions.len)),
323 .string_bytes_len = @as(u32, @intCast(file.zir.string_bytes.len)),
324 .extra_len = @as(u32, @intCast(file.zir.extra.len)),
317fn loadZirZoirCache(
318 zcu: *Zcu,
319 cache_file: std.fs.File,
320 stat: std.fs.File.Stat,
321 file: *Zcu.File,
322 comptime mode: Ast.Mode,
323) !enum { success, invalid, truncated, stale } {
324 assert(file.getMode() == mode);
325325
326 .stat_size = stat.size,
327 .stat_inode = stat.inode,
328 .stat_mtime = stat.mtime,
329 };
330 var iovecs = [_]std.posix.iovec_const{
331 .{
332 .base = @as([*]const u8, @ptrCast(&header)),
333 .len = @sizeOf(Zir.Header),
334 },
335 .{
336 .base = @as([*]const u8, @ptrCast(file.zir.instructions.items(.tag).ptr)),
337 .len = file.zir.instructions.len,
338 },
339 .{
340 .base = data_ptr,
341 .len = file.zir.instructions.len * 8,
342 },
343 .{
344 .base = file.zir.string_bytes.ptr,
345 .len = file.zir.string_bytes.len,
346 },
347 .{
348 .base = @as([*]const u8, @ptrCast(file.zir.extra.ptr)),
349 .len = file.zir.extra.len * 4,
350 },
326 const gpa = zcu.gpa;
327
328 const Header = switch (mode) {
329 .zig => Zir.Header,
330 .zon => Zoir.Header,
351331 };
352 cache_file.writevAll(&iovecs) catch |err| {
353 log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{
354 file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
355 });
332
333 // First we read the header to determine the lengths of arrays.
334 const header = cache_file.reader().readStruct(Header) catch |err| switch (err) {
335 // This can happen if Zig bails out of this function between creating
336 // the cached file and writing it.
337 error.EndOfStream => return .invalid,
338 else => |e| return e,
356339 };
357340
358 if (file.zir.hasCompileErrors()) {
359 comp.mutex.lock();
360 defer comp.mutex.unlock();
361 try zcu.failed_files.putNoClobber(gpa, file, null);
341 const unchanged_metadata =
342 stat.size == header.stat_size and
343 stat.mtime == header.stat_mtime and
344 stat.inode == header.stat_inode;
345
346 if (!unchanged_metadata) {
347 return .stale;
362348 }
363 if (file.zir.loweringFailed()) {
364 file.status = .astgen_failure;
365 return error.AnalysisFail;
349
350 switch (mode) {
351 .zig => {
352 file.zir = Zcu.loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
353 error.UnexpectedFileSize => return .truncated,
354 else => |e| return e,
355 };
356 },
357 .zon => {
358 file.zoir = Zcu.loadZoirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
359 error.UnexpectedFileSize => return .truncated,
360 else => |e| return e,
361 };
362 },
366363 }
364
365 return .success;
367366}
368367
369368const UpdatedFile = struct {
......@@ -384,24 +383,32 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
384383 const gpa = zcu.gpa;
385384
386385 // We need to visit every updated File for every TrackedInst in InternPool.
386 // This only includes Zig files; ZON files are omitted.
387387 var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .empty;
388388 defer cleanupUpdatedFiles(gpa, &updated_files);
389
389390 for (zcu.import_table.values()) |file_index| {
390391 const file = zcu.fileByIndex(file_index);
391 if (file.prev_status != file.status and file.prev_status != .never_loaded) {
392 try zcu.markDependeeOutdated(.not_marked_po, .{ .file = file_index });
392 assert(file.status == .success);
393 switch (file.getMode()) {
394 .zig => {}, // logic below
395 .zon => {
396 if (file.zoir_invalidated) {
397 try zcu.markDependeeOutdated(.not_marked_po, .{ .zon_file = file_index });
398 file.zoir_invalidated = false;
399 }
400 continue;
401 },
393402 }
394403 const old_zir = file.prev_zir orelse continue;
395 const new_zir = file.zir;
404 const new_zir = file.zir.?;
396405 const gop = try updated_files.getOrPut(gpa, file_index);
397406 assert(!gop.found_existing);
398407 gop.value_ptr.* = .{
399408 .file = file,
400409 .inst_map = .{},
401410 };
402 if (!new_zir.loweringFailed()) {
403 try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map);
404 }
411 try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map);
405412 }
406413
407414 if (updated_files.count() == 0)
......@@ -421,13 +428,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
421428 .index = @intCast(tracked_inst_unwrapped_index),
422429 }).wrap(ip);
423430 const new_inst = updated_file.inst_map.get(old_inst) orelse {
424 // Tracking failed for this instruction.
425 // This may be due to changes in the ZIR, or AstGen might have failed due to a very broken file.
426 // Either way, invalidate associated `src_hash` deps.
427 log.debug("tracking failed for %{d}{s}", .{
428 old_inst,
429 if (file.zir.loweringFailed()) " due to AstGen failure" else "",
430 });
431 // Tracking failed for this instruction due to changes in the ZIR.
432 // Invalidate associated `src_hash` deps.
433 log.debug("tracking failed for %{d}", .{old_inst});
431434 tracked_inst.inst = .lost;
432435 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
433436 continue;
......@@ -435,7 +438,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
435438 tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst);
436439
437440 const old_zir = file.prev_zir.?.*;
438 const new_zir = file.zir;
441 const new_zir = file.zir.?;
439442 const old_tag = old_zir.instructions.items(.tag)[@intFromEnum(old_inst)];
440443 const old_data = old_zir.instructions.items(.data)[@intFromEnum(old_inst)];
441444
......@@ -532,23 +535,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
532535
533536 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
534537 const file = updated_file.file;
535 if (file.zir.loweringFailed()) {
536 // Keep `prev_zir` around: it's the last usable ZIR.
537 // Don't update the namespace, as we have no new data to update *to*.
538 } else {
539 const prev_zir = file.prev_zir.?;
540 file.prev_zir = null;
541 prev_zir.deinit(gpa);
542 gpa.destroy(prev_zir);
543
544 // For every file which has changed, re-scan the namespace of the file's root struct type.
545 // These types are special-cased because they don't have an enclosing declaration which will
546 // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
547 // now because this work is fast (no actual Sema work is happening, we're just updating the
548 // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
549 // will track some instructions.
550 try pt.updateFileNamespace(file_index);
551 }
538
539 const prev_zir = file.prev_zir.?;
540 file.prev_zir = null;
541 prev_zir.deinit(gpa);
542 gpa.destroy(prev_zir);
543
544 // For every file which has changed, re-scan the namespace of the file's root struct type.
545 // These types are special-cased because they don't have an enclosing declaration which will
546 // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
547 // now because this work is fast (no actual Sema work is happening, we're just updating the
548 // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
549 // will track some instructions.
550 try pt.updateFileNamespace(file_index);
552551 }
553552}
554553
......@@ -750,6 +749,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
750749 kv.value.destroy(gpa);
751750 }
752751 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
752 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
753753 }
754754 } else {
755755 // We can trust the current information about this unit.
......@@ -801,14 +801,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu
801801
802802 const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
803803 const file = zcu.fileByIndex(inst_resolved.file);
804 // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
805 // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
806 // in `ensureComptimeUnitUpToDate`.
807 if (file.status != .success_zir) return error.AnalysisFail;
808 const zir = file.zir;
809
810 // We are about to re-analyze this unit; drop its depenndencies.
811 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
804 const zir = file.zir.?;
812805
813806 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
814807 defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
......@@ -928,6 +921,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
928921 kv.value.destroy(gpa);
929922 }
930923 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
924 ip.removeDependenciesForDepender(gpa, anal_unit);
931925 } else {
932926 // We can trust the current information about this unit.
933927 if (prev_failed) return error.AnalysisFail;
......@@ -998,14 +992,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
998992
999993 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
1000994 const file = zcu.fileByIndex(inst_resolved.file);
1001 // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
1002 // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
1003 // in `ensureComptimeUnitUpToDate`.
1004 if (file.status != .success_zir) return error.AnalysisFail;
1005 const zir = file.zir;
1006
1007 // We are about to re-analyze this unit; drop its depenndencies.
1008 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
995 const zir = file.zir.?;
1009996
1010997 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
1011998 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
......@@ -1306,6 +1293,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
13061293 kv.value.destroy(gpa);
13071294 }
13081295 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
1296 ip.removeDependenciesForDepender(gpa, anal_unit);
13091297 } else {
13101298 // We can trust the current information about this unit.
13111299 if (prev_failed) return error.AnalysisFail;
......@@ -1376,14 +1364,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
13761364
13771365 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
13781366 const file = zcu.fileByIndex(inst_resolved.file);
1379 // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
1380 // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
1381 // in `ensureComptimeUnitUpToDate`.
1382 if (file.status != .success_zir) return error.AnalysisFail;
1383 const zir = file.zir;
1384
1385 // We are about to re-analyze this unit; drop its depenndencies.
1386 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
1367 const zir = file.zir.?;
13871368
13881369 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
13891370 defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
......@@ -1758,7 +1739,7 @@ fn createFileRootStruct(
17581739 const gpa = zcu.gpa;
17591740 const ip = &zcu.intern_pool;
17601741 const file = zcu.fileByIndex(file_index);
1761 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
1742 const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
17621743 assert(extended.opcode == .struct_decl);
17631744 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
17641745 assert(!small.has_captures_len);
......@@ -1766,16 +1747,16 @@ fn createFileRootStruct(
17661747 assert(small.layout == .auto);
17671748 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
17681749 const fields_len = if (small.has_fields_len) blk: {
1769 const fields_len = file.zir.extra[extra_index];
1750 const fields_len = file.zir.?.extra[extra_index];
17701751 extra_index += 1;
17711752 break :blk fields_len;
17721753 } else 0;
17731754 const decls_len = if (small.has_decls_len) blk: {
1774 const decls_len = file.zir.extra[extra_index];
1755 const decls_len = file.zir.?.extra[extra_index];
17751756 extra_index += 1;
17761757 break :blk decls_len;
17771758 } else 0;
1778 const decls = file.zir.bodySlice(extra_index, decls_len);
1759 const decls = file.zir.?.bodySlice(extra_index, decls_len);
17791760 extra_index += decls_len;
17801761
17811762 const tracked_inst = try ip.trackZir(gpa, pt.tid, .{
......@@ -1833,7 +1814,6 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.
18331814 const zcu = pt.zcu;
18341815
18351816 const file = zcu.fileByIndex(file_index);
1836 assert(file.status == .success_zir);
18371817 const file_root_type = zcu.fileRootType(file_index);
18381818 if (file_root_type == .none) return;
18391819
......@@ -1844,17 +1824,17 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.
18441824
18451825 const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
18461826 const decls = decls: {
1847 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
1827 const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
18481828 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
18491829
18501830 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
18511831 extra_index += @intFromBool(small.has_fields_len);
18521832 const decls_len = if (small.has_decls_len) blk: {
1853 const decls_len = file.zir.extra[extra_index];
1833 const decls_len = file.zir.?.extra[extra_index];
18541834 extra_index += 1;
18551835 break :blk decls_len;
18561836 } else 0;
1857 break :decls file.zir.bodySlice(extra_index, decls_len);
1837 break :decls file.zir.?.bodySlice(extra_index, decls_len);
18581838 };
18591839 try pt.scanNamespace(namespace_index, decls);
18601840 zcu.namespacePtr(namespace_index).generation = zcu.generation;
......@@ -1865,15 +1845,11 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
18651845 defer tracy.end();
18661846
18671847 const zcu = pt.zcu;
1868 const gpa = zcu.gpa;
18691848 const file = zcu.fileByIndex(file_index);
18701849 assert(file.getMode() == .zig);
18711850 assert(zcu.fileRootType(file_index) == .none);
18721851
1873 if (file.status != .success_zir) {
1874 return error.AnalysisFail;
1875 }
1876 assert(file.zir_loaded);
1852 assert(file.zir != null);
18771853
18781854 const new_namespace_index = try pt.createNamespace(.{
18791855 .parent = .none,
......@@ -1883,39 +1859,9 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
18831859 });
18841860 const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
18851861 errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
1886
1887 switch (zcu.comp.cache_use) {
1888 .whole => |whole| if (whole.cache_manifest) |man| {
1889 const source = file.getSource(gpa) catch |err| {
1890 try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)});
1891 return error.AnalysisFail;
1892 };
1893
1894 const resolved_path = std.fs.path.resolve(gpa, &.{
1895 file.mod.root.root_dir.path orelse ".",
1896 file.mod.root.sub_path,
1897 file.sub_file_path,
1898 }) catch |err| {
1899 try pt.reportRetryableFileError(file_index, "unable to resolve path: {s}", .{@errorName(err)});
1900 return error.AnalysisFail;
1901 };
1902 errdefer gpa.free(resolved_path);
1903
1904 whole.cache_manifest_mutex.lock();
1905 defer whole.cache_manifest_mutex.unlock();
1906 man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) {
1907 error.OutOfMemory => |e| return e,
1908 else => {
1909 try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
1910 return error.AnalysisFail;
1911 },
1912 };
1913 },
1914 .incremental => {},
1915 }
19161862}
19171863
1918pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
1864pub fn importPkg(pt: Zcu.PerThread, mod: *Module) Allocator.Error!Zcu.ImportFileResult {
19191865 const zcu = pt.zcu;
19201866 const gpa = zcu.gpa;
19211867
......@@ -1983,15 +1929,12 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
19831929 gop.value_ptr.* = new_file_index;
19841930 new_file.* = .{
19851931 .sub_file_path = sub_file_path,
1986 .source = undefined,
1987 .source_loaded = false,
1988 .tree_loaded = false,
1989 .zir_loaded = false,
19901932 .stat = undefined,
1991 .tree = undefined,
1992 .zir = undefined,
1933 .source = null,
1934 .tree = null,
1935 .zir = null,
1936 .zoir = null,
19931937 .status = .never_loaded,
1994 .prev_status = .never_loaded,
19951938 .mod = mod,
19961939 };
19971940
......@@ -2004,13 +1947,19 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
20041947 };
20051948}
20061949
2007/// Called from a worker thread during AstGen.
1950/// Called from a worker thread during AstGen (with the Compilation mutex held).
20081951/// Also called from Sema during semantic analysis.
1952/// Does not attempt to load the file from disk; just returns a corresponding `*Zcu.File`.
20091953pub fn importFile(
20101954 pt: Zcu.PerThread,
20111955 cur_file: *Zcu.File,
20121956 import_string: []const u8,
2013) !Zcu.ImportFileResult {
1957) error{
1958 OutOfMemory,
1959 ModuleNotFound,
1960 ImportOutsideModulePath,
1961 CurrentWorkingDirectoryUnlinked,
1962}!Zcu.ImportFileResult {
20141963 const zcu = pt.zcu;
20151964 const mod = cur_file.mod;
20161965
......@@ -2068,7 +2017,10 @@ pub fn importFile(
20682017 defer gpa.free(resolved_root_path);
20692018
20702019 const sub_file_path = p: {
2071 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);
2020 const relative = std.fs.path.relative(gpa, resolved_root_path, resolved_path) catch |err| switch (err) {
2021 error.Unexpected => unreachable,
2022 else => |e| return e,
2023 };
20722024 errdefer gpa.free(relative);
20732025
20742026 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
......@@ -2096,15 +2048,15 @@ pub fn importFile(
20962048 gop.value_ptr.* = new_file_index;
20972049 new_file.* = .{
20982050 .sub_file_path = sub_file_path,
2099 .source = undefined,
2100 .source_loaded = false,
2101 .tree_loaded = false,
2102 .zir_loaded = false,
2103 .stat = undefined,
2104 .tree = undefined,
2105 .zir = undefined,
2051
21062052 .status = .never_loaded,
2107 .prev_status = .never_loaded,
2053 .stat = undefined,
2054
2055 .source = null,
2056 .tree = null,
2057 .zir = null,
2058 .zoir = null,
2059
21082060 .mod = mod,
21092061 };
21102062
......@@ -2441,7 +2393,7 @@ const ScanDeclIter = struct {
24412393 const namespace = zcu.namespacePtr(namespace_index);
24422394 const gpa = zcu.gpa;
24432395 const file = namespace.fileScope(zcu);
2444 const zir = file.zir;
2396 const zir = file.zir.?;
24452397 const ip = &zcu.intern_pool;
24462398
24472399 const decl = zir.getDeclaration(decl_inst);
......@@ -2591,7 +2543,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
25912543 const func = zcu.funcInfo(func_index);
25922544 const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
25932545 const file = zcu.fileByIndex(inst_info.file);
2594 const zir = file.zir;
2546 const zir = file.zir.?;
25952547
25962548 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
25972549 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
......@@ -2843,11 +2795,32 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err
28432795/// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed.
28442796/// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry.
28452797fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void {
2846 if (!file.zir_loaded or !file.zir.hasCompileErrors()) return;
2798 const maybe_has_error = switch (file.status) {
2799 .never_loaded => false,
2800 .retryable_failure => true,
2801 .astgen_failure => true,
2802 .success => switch (file.getMode()) {
2803 .zig => has_error: {
2804 const zir = file.zir orelse break :has_error false;
2805 break :has_error zir.hasCompileErrors();
2806 },
2807 .zon => has_error: {
2808 const zoir = file.zoir orelse break :has_error false;
2809 break :has_error zoir.hasCompileErrors();
2810 },
2811 },
2812 };
2813
2814 // If runtime safety is on, let's quickly lock the mutex and check anyway.
2815 if (!maybe_has_error and !std.debug.runtime_safety) {
2816 return;
2817 }
2818
28472819 pt.zcu.comp.mutex.lock();
28482820 defer pt.zcu.comp.mutex.unlock();
28492821 if (pt.zcu.failed_files.fetchSwapRemove(file)) |kv| {
2850 if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // Delete previous error message.
2822 assert(maybe_has_error); // the runtime safety case above
2823 if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // delete previous error message
28512824 }
28522825}
28532826
......@@ -3203,6 +3176,7 @@ pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Inde
32033176 }
32043177}
32053178
3179/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`.
32063180pub fn reportRetryableAstGenError(
32073181 pt: Zcu.PerThread,
32083182 src: Zcu.AstGenSrc,
......@@ -3238,13 +3212,18 @@ pub fn reportRetryableAstGenError(
32383212 });
32393213 errdefer err_msg.destroy(gpa);
32403214
3241 {
3242 zcu.comp.mutex.lock();
3243 defer zcu.comp.mutex.unlock();
3244 try zcu.failed_files.putNoClobber(gpa, file, err_msg);
3215 zcu.comp.mutex.lock();
3216 defer zcu.comp.mutex.unlock();
3217 const gop = try zcu.failed_files.getOrPut(gpa, file);
3218 if (gop.found_existing) {
3219 if (gop.value_ptr.*) |old_err_msg| {
3220 old_err_msg.destroy(gpa);
3221 }
32453222 }
3223 gop.value_ptr.* = err_msg;
32463224}
32473225
3226/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`.
32483227pub fn reportRetryableFileError(
32493228 pt: Zcu.PerThread,
32503229 file_index: Zcu.File.Index,
......@@ -3778,8 +3757,7 @@ fn recreateStructType(
37783757
37793758 const inst_info = key.zir_index.resolveFull(ip).?;
37803759 const file = zcu.fileByIndex(inst_info.file);
3781 assert(file.status == .success_zir); // otherwise inst tracking failed
3782 const zir = file.zir;
3760 const zir = file.zir.?;
37833761
37843762 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
37853763 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
......@@ -3851,8 +3829,7 @@ fn recreateUnionType(
38513829
38523830 const inst_info = key.zir_index.resolveFull(ip).?;
38533831 const file = zcu.fileByIndex(inst_info.file);
3854 assert(file.status == .success_zir); // otherwise inst tracking failed
3855 const zir = file.zir;
3832 const zir = file.zir.?;
38563833
38573834 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
38583835 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
......@@ -3938,8 +3915,7 @@ fn recreateEnumType(
39383915
39393916 const inst_info = key.zir_index.resolveFull(ip).?;
39403917 const file = zcu.fileByIndex(inst_info.file);
3941 assert(file.status == .success_zir); // otherwise inst tracking failed
3942 const zir = file.zir;
3918 const zir = file.zir.?;
39433919
39443920 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
39453921 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
......@@ -4082,8 +4058,7 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
40824058
40834059 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
40844060 const file = zcu.fileByIndex(inst_info.file);
4085 if (file.status != .success_zir) return error.AnalysisFail;
4086 const zir = file.zir;
4061 const zir = file.zir.?;
40874062
40884063 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
40894064 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
src/link.zig+1-2
......@@ -750,8 +750,7 @@ pub const File = struct {
750750 {
751751 const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?;
752752 const file = pt.zcu.fileByIndex(ti.file);
753 assert(file.zir_loaded);
754 const inst = file.zir.instructions.get(@intFromEnum(ti.inst));
753 const inst = file.zir.?.instructions.get(@intFromEnum(ti.inst));
755754 assert(inst.tag == .declaration);
756755 }
757756
src/link/Dwarf.zig+7-10
......@@ -2358,8 +2358,7 @@ fn initWipNavInner(
23582358 const nav = ip.getNav(nav_index);
23592359 const inst_info = nav.srcInst(ip).resolveFull(ip).?;
23602360 const file = zcu.fileByIndex(inst_info.file);
2361 assert(file.zir_loaded);
2362 const decl = file.zir.getDeclaration(inst_info.inst);
2361 const decl = file.zir.?.getDeclaration(inst_info.inst);
23632362 log.debug("initWipNav({s}:{d}:{d} %{d} = {})", .{
23642363 file.sub_file_path,
23652364 decl.src_line + 1,
......@@ -2373,7 +2372,7 @@ fn initWipNavInner(
23732372 switch (nav_key) {
23742373 // Ignore @extern
23752374 .@"extern" => |@"extern"| if (decl.linkage != .@"extern" or
2376 !@"extern".name.eqlSlice(file.zir.nullTerminatedString(decl.name), ip)) return null,
2375 !@"extern".name.eqlSlice(file.zir.?.nullTerminatedString(decl.name), ip)) return null,
23772376 else => {},
23782377 }
23792378
......@@ -2696,8 +2695,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
26962695 const nav = ip.getNav(nav_index);
26972696 const inst_info = nav.srcInst(ip).resolveFull(ip).?;
26982697 const file = zcu.fileByIndex(inst_info.file);
2699 assert(file.zir_loaded);
2700 const decl = file.zir.getDeclaration(inst_info.inst);
2698 const decl = file.zir.?.getDeclaration(inst_info.inst);
27012699 log.debug("updateComptimeNav({s}:{d}:{d} %{d} = {})", .{
27022700 file.sub_file_path,
27032701 decl.src_line + 1,
......@@ -4097,7 +4095,7 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
40974095 // if a newly-tracked instruction can be a type's owner `zir_index`.
40984096 comptime assert(Zir.inst_tracking_version == 0);
40994097
4100 const decl_inst = file.zir.instructions.get(@intFromEnum(inst_info.inst));
4098 const decl_inst = file.zir.?.instructions.get(@intFromEnum(inst_info.inst));
41014099 const name_strat: Zir.Inst.NameStrategy = switch (decl_inst.tag) {
41024100 .struct_init, .struct_init_ref, .struct_init_anon => .anon,
41034101 .extended => switch (decl_inst.data.extended.opcode) {
......@@ -4301,14 +4299,13 @@ pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedI
43014299 const inst_info = zir_index.resolveFull(ip).?;
43024300 assert(inst_info.inst != .main_struct_inst);
43034301 const file = zcu.fileByIndex(inst_info.file);
4304 assert(file.zir_loaded);
4305 const decl = file.zir.getDeclaration(inst_info.inst);
4302 const decl = file.zir.?.getDeclaration(inst_info.inst);
43064303 log.debug("updateLineNumber({s}:{d}:{d} %{d} = {s})", .{
43074304 file.sub_file_path,
43084305 decl.src_line + 1,
43094306 decl.src_column + 1,
43104307 @intFromEnum(inst_info.inst),
4311 file.zir.nullTerminatedString(decl.name),
4308 file.zir.?.nullTerminatedString(decl.name),
43124309 });
43134310
43144311 var line_buf: [4]u8 = undefined;
......@@ -4661,7 +4658,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
46614658 .target_unit = StringSection.unit,
46624659 .target_entry = (try dwarf.debug_line_str.addString(
46634660 dwarf,
4664 if (file.mod.builtin_file == file) file.source else "",
4661 if (file.mod.builtin_file == file) file.source.? else "",
46654662 )).toOptional(),
46664663 });
46674664 header.appendNTimesAssumeCapacity(0, dwarf.sectionOffsetBytes());
src/main.zig+49-66
......@@ -3636,7 +3636,7 @@ fn buildOutputType(
36363636
36373637 if (show_builtin) {
36383638 const builtin_mod = comp.root_mod.getBuiltinDependency();
3639 const source = builtin_mod.builtin_file.?.source;
3639 const source = builtin_mod.builtin_file.?.source.?;
36403640 return std.io.getStdOut().writeAll(source);
36413641 }
36423642 switch (listen) {
......@@ -6134,15 +6134,12 @@ fn cmdAstCheck(
61346134
61356135 var file: Zcu.File = .{
61366136 .status = .never_loaded,
6137 .prev_status = .never_loaded,
6138 .source_loaded = false,
6139 .tree_loaded = false,
6140 .zir_loaded = false,
61416137 .sub_file_path = undefined,
6142 .source = undefined,
61436138 .stat = undefined,
6144 .tree = undefined,
6145 .zir = undefined,
6139 .source = null,
6140 .tree = null,
6141 .zir = null,
6142 .zoir = null,
61466143 .mod = undefined,
61476144 };
61486145 if (zig_source_file) |file_name| {
......@@ -6163,7 +6160,6 @@ fn cmdAstCheck(
61636160
61646161 file.sub_file_path = file_name;
61656162 file.source = source;
6166 file.source_loaded = true;
61676163 file.stat = .{
61686164 .size = stat.size,
61696165 .inode = stat.inode,
......@@ -6176,7 +6172,6 @@ fn cmdAstCheck(
61766172 };
61776173 file.sub_file_path = "<stdin>";
61786174 file.source = source;
6179 file.source_loaded = true;
61806175 file.stat.size = source.len;
61816176 }
61826177
......@@ -6196,17 +6191,15 @@ fn cmdAstCheck(
61966191 .fully_qualified_name = "root",
61976192 });
61986193
6199 file.tree = try Ast.parse(gpa, file.source, mode);
6200 file.tree_loaded = true;
6201 defer file.tree.deinit(gpa);
6194 file.tree = try Ast.parse(gpa, file.source.?, mode);
6195 defer file.tree.?.deinit(gpa);
62026196
62036197 switch (mode) {
62046198 .zig => {
6205 file.zir = try AstGen.generate(gpa, file.tree);
6206 file.zir_loaded = true;
6207 defer file.zir.deinit(gpa);
6199 file.zir = try AstGen.generate(gpa, file.tree.?);
6200 defer file.zir.?.deinit(gpa);
62086201
6209 if (file.zir.hasCompileErrors()) {
6202 if (file.zir.?.hasCompileErrors()) {
62106203 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
62116204 try wip_errors.init(gpa);
62126205 defer wip_errors.deinit();
......@@ -6215,13 +6208,13 @@ fn cmdAstCheck(
62156208 defer error_bundle.deinit(gpa);
62166209 error_bundle.renderToStdErr(color.renderOptions());
62176210
6218 if (file.zir.loweringFailed()) {
6211 if (file.zir.?.loweringFailed()) {
62196212 process.exit(1);
62206213 }
62216214 }
62226215
62236216 if (!want_output_text) {
6224 if (file.zir.hasCompileErrors()) {
6217 if (file.zir.?.hasCompileErrors()) {
62256218 process.exit(1);
62266219 } else {
62276220 return cleanExit();
......@@ -6233,18 +6226,18 @@ fn cmdAstCheck(
62336226
62346227 {
62356228 const token_bytes = @sizeOf(Ast.TokenList) +
6236 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
6237 const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len *
6229 file.tree.?.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
6230 const tree_bytes = @sizeOf(Ast) + file.tree.?.nodes.len *
62386231 (@sizeOf(Ast.Node.Tag) +
62396232 @sizeOf(Ast.Node.Data) +
62406233 @sizeOf(Ast.TokenIndex));
6241 const instruction_bytes = file.zir.instructions.len *
6234 const instruction_bytes = file.zir.?.instructions.len *
62426235 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
62436236 // the debug safety tag but we want to measure release size.
62446237 (@sizeOf(Zir.Inst.Tag) + 8);
6245 const extra_bytes = file.zir.extra.len * @sizeOf(u32);
6238 const extra_bytes = file.zir.?.extra.len * @sizeOf(u32);
62466239 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6247 file.zir.string_bytes.len * @sizeOf(u8);
6240 file.zir.?.string_bytes.len * @sizeOf(u8);
62486241 const stdout = io.getStdOut();
62496242 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
62506243 // zig fmt: off
......@@ -6258,27 +6251,27 @@ fn cmdAstCheck(
62586251 \\# Extra Data Items: {d} ({})
62596252 \\
62606253 , .{
6261 fmtIntSizeBin(file.source.len),
6262 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
6263 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
6254 fmtIntSizeBin(file.source.?.len),
6255 file.tree.?.tokens.len, fmtIntSizeBin(token_bytes),
6256 file.tree.?.nodes.len, fmtIntSizeBin(tree_bytes),
62646257 fmtIntSizeBin(total_bytes),
6265 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
6266 fmtIntSizeBin(file.zir.string_bytes.len),
6267 file.zir.extra.len, fmtIntSizeBin(extra_bytes),
6258 file.zir.?.instructions.len, fmtIntSizeBin(instruction_bytes),
6259 fmtIntSizeBin(file.zir.?.string_bytes.len),
6260 file.zir.?.extra.len, fmtIntSizeBin(extra_bytes),
62686261 });
62696262 // zig fmt: on
62706263 }
62716264
62726265 try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
62736266
6274 if (file.zir.hasCompileErrors()) {
6267 if (file.zir.?.hasCompileErrors()) {
62756268 process.exit(1);
62766269 } else {
62776270 return cleanExit();
62786271 }
62796272 },
62806273 .zon => {
6281 const zoir = try ZonGen.generate(gpa, file.tree, .{});
6274 const zoir = try ZonGen.generate(gpa, file.tree.?, .{});
62826275 defer zoir.deinit(gpa);
62836276
62846277 if (zoir.hasCompileErrors()) {
......@@ -6289,7 +6282,7 @@ fn cmdAstCheck(
62896282 {
62906283 const src_path = try file.fullPath(gpa);
62916284 defer gpa.free(src_path);
6292 try wip_errors.addZoirErrorMessages(zoir, file.tree, file.source, src_path);
6285 try wip_errors.addZoirErrorMessages(zoir, file.tree.?, file.source.?, src_path);
62936286 }
62946287
62956288 var error_bundle = try wip_errors.toOwnedBundle("");
......@@ -6518,27 +6511,24 @@ fn cmdDumpZir(
65186511
65196512 var file: Zcu.File = .{
65206513 .status = .never_loaded,
6521 .prev_status = .never_loaded,
6522 .source_loaded = false,
6523 .tree_loaded = false,
6524 .zir_loaded = true,
65256514 .sub_file_path = undefined,
6526 .source = undefined,
65276515 .stat = undefined,
6528 .tree = undefined,
6516 .source = null,
6517 .tree = null,
65296518 .zir = try Zcu.loadZirCache(gpa, f),
6519 .zoir = null,
65306520 .mod = undefined,
65316521 };
6532 defer file.zir.deinit(gpa);
6522 defer file.zir.?.deinit(gpa);
65336523
65346524 {
6535 const instruction_bytes = file.zir.instructions.len *
6525 const instruction_bytes = file.zir.?.instructions.len *
65366526 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
65376527 // the debug safety tag but we want to measure release size.
65386528 (@sizeOf(Zir.Inst.Tag) + 8);
6539 const extra_bytes = file.zir.extra.len * @sizeOf(u32);
6529 const extra_bytes = file.zir.?.extra.len * @sizeOf(u32);
65406530 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6541 file.zir.string_bytes.len * @sizeOf(u8);
6531 file.zir.?.string_bytes.len * @sizeOf(u8);
65426532 const stdout = io.getStdOut();
65436533 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
65446534 // zig fmt: off
......@@ -6550,9 +6540,9 @@ fn cmdDumpZir(
65506540 \\
65516541 , .{
65526542 fmtIntSizeBin(total_bytes),
6553 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
6554 fmtIntSizeBin(file.zir.string_bytes.len),
6555 file.zir.extra.len, fmtIntSizeBin(extra_bytes),
6543 file.zir.?.instructions.len, fmtIntSizeBin(instruction_bytes),
6544 fmtIntSizeBin(file.zir.?.string_bytes.len),
6545 file.zir.?.extra.len, fmtIntSizeBin(extra_bytes),
65566546 });
65576547 // zig fmt: on
65586548 }
......@@ -6586,19 +6576,16 @@ fn cmdChangelist(
65866576
65876577 var file: Zcu.File = .{
65886578 .status = .never_loaded,
6589 .prev_status = .never_loaded,
6590 .source_loaded = false,
6591 .tree_loaded = false,
6592 .zir_loaded = false,
65936579 .sub_file_path = old_source_file,
6594 .source = undefined,
65956580 .stat = .{
65966581 .size = stat.size,
65976582 .inode = stat.inode,
65986583 .mtime = stat.mtime,
65996584 },
6600 .tree = undefined,
6601 .zir = undefined,
6585 .source = null,
6586 .tree = null,
6587 .zir = null,
6588 .zoir = null,
66026589 .mod = undefined,
66036590 };
66046591
......@@ -6613,17 +6600,14 @@ fn cmdChangelist(
66136600 if (amt != stat.size)
66146601 return error.UnexpectedEndOfFile;
66156602 file.source = source;
6616 file.source_loaded = true;
66176603
6618 file.tree = try Ast.parse(gpa, file.source, .zig);
6619 file.tree_loaded = true;
6620 defer file.tree.deinit(gpa);
6604 file.tree = try Ast.parse(gpa, file.source.?, .zig);
6605 defer file.tree.?.deinit(gpa);
66216606
6622 file.zir = try AstGen.generate(gpa, file.tree);
6623 file.zir_loaded = true;
6624 defer file.zir.deinit(gpa);
6607 file.zir = try AstGen.generate(gpa, file.tree.?);
6608 defer file.zir.?.deinit(gpa);
66256609
6626 if (file.zir.loweringFailed()) {
6610 if (file.zir.?.loweringFailed()) {
66276611 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
66286612 try wip_errors.init(gpa);
66296613 defer wip_errors.deinit();
......@@ -6652,13 +6636,12 @@ fn cmdChangelist(
66526636 var new_tree = try Ast.parse(gpa, new_source, .zig);
66536637 defer new_tree.deinit(gpa);
66546638
6655 var old_zir = file.zir;
6639 var old_zir = file.zir.?;
66566640 defer old_zir.deinit(gpa);
6657 file.zir_loaded = false;
6641 file.zir = null;
66586642 file.zir = try AstGen.generate(gpa, new_tree);
6659 file.zir_loaded = true;
66606643
6661 if (file.zir.loweringFailed()) {
6644 if (file.zir.?.loweringFailed()) {
66626645 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
66636646 try wip_errors.init(gpa);
66646647 defer wip_errors.deinit();
......@@ -6672,7 +6655,7 @@ fn cmdChangelist(
66726655 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty;
66736656 defer inst_map.deinit(gpa);
66746657
6675 try Zcu.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map);
6658 try Zcu.mapOldZirToNew(gpa, old_zir, file.zir.?, &inst_map);
66766659
66776660 var bw = io.bufferedWriter(io.getStdOut().writer());
66786661 const stdout = bw.writer();
src/print_zir.zig+10-13
......@@ -22,7 +22,7 @@ pub fn renderAsTextToFile(
2222 .gpa = gpa,
2323 .arena = arena.allocator(),
2424 .file = scope_file,
25 .code = scope_file.zir,
25 .code = scope_file.zir.?,
2626 .indent = 0,
2727 .parent_decl_node = 0,
2828 .recurse_decls = true,
......@@ -36,18 +36,18 @@ pub fn renderAsTextToFile(
3636 try stream.print("%{d} ", .{@intFromEnum(main_struct_inst)});
3737 try writer.writeInstToStream(stream, main_struct_inst);
3838 try stream.writeAll("\n");
39 const imports_index = scope_file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
39 const imports_index = scope_file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
4040 if (imports_index != 0) {
4141 try stream.writeAll("Imports:\n");
4242
43 const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index);
43 const extra = scope_file.zir.?.extraData(Zir.Inst.Imports, imports_index);
4444 var extra_index = extra.end;
4545
4646 for (0..extra.data.imports_len) |_| {
47 const item = scope_file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
47 const item = scope_file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
4848 extra_index = item.end;
4949
50 const import_path = scope_file.zir.nullTerminatedString(item.data.name);
50 const import_path = scope_file.zir.?.nullTerminatedString(item.data.name);
5151 try stream.print(" @import(\"{}\") ", .{
5252 std.zig.fmtEscapes(import_path),
5353 });
......@@ -75,7 +75,7 @@ pub fn renderInstructionContext(
7575 .gpa = gpa,
7676 .arena = arena.allocator(),
7777 .file = scope_file,
78 .code = scope_file.zir,
78 .code = scope_file.zir.?,
7979 .indent = if (indent < 2) 2 else indent,
8080 .parent_decl_node = parent_decl_node,
8181 .recurse_decls = false,
......@@ -107,7 +107,7 @@ pub fn renderSingleInstruction(
107107 .gpa = gpa,
108108 .arena = arena.allocator(),
109109 .file = scope_file,
110 .code = scope_file.zir,
110 .code = scope_file.zir.?,
111111 .indent = indent,
112112 .parent_decl_node = parent_decl_node,
113113 .recurse_decls = false,
......@@ -2759,8 +2759,7 @@ const Writer = struct {
27592759 }
27602760
27612761 fn writeSrcNode(self: *Writer, stream: anytype, src_node: i32) !void {
2762 if (!self.file.tree_loaded) return;
2763 const tree = self.file.tree;
2762 const tree = self.file.tree orelse return;
27642763 const abs_node = self.relativeToNodeIndex(src_node);
27652764 const src_span = tree.nodeToSpan(abs_node);
27662765 const start = self.line_col_cursor.find(tree.source, src_span.start);
......@@ -2772,8 +2771,7 @@ const Writer = struct {
27722771 }
27732772
27742773 fn writeSrcTok(self: *Writer, stream: anytype, src_tok: u32) !void {
2775 if (!self.file.tree_loaded) return;
2776 const tree = self.file.tree;
2774 const tree = self.file.tree orelse return;
27772775 const abs_tok = tree.firstToken(self.parent_decl_node) + src_tok;
27782776 const span_start = tree.tokens.items(.start)[abs_tok];
27792777 const span_end = span_start + @as(u32, @intCast(tree.tokenSlice(abs_tok).len));
......@@ -2786,8 +2784,7 @@ const Writer = struct {
27862784 }
27872785
27882786 fn writeSrcTokAbs(self: *Writer, stream: anytype, src_tok: u32) !void {
2789 if (!self.file.tree_loaded) return;
2790 const tree = self.file.tree;
2787 const tree = self.file.tree orelse return;
27912788 const span_start = tree.tokens.items(.start)[src_tok];
27922789 const span_end = span_start + @as(u32, @intCast(tree.tokenSlice(src_tok).len));
27932790 const start = self.line_col_cursor.find(tree.source, span_start);
test/cases/compile_errors/@import_zon_bad_import.zig deleted-9
......@@ -1,9 +0,0 @@
1export fn entry() void {
2 _ = @import(
3 "bogus-does-not-exist.zon",
4 );
5}
6
7// error
8//
9// :3:9: error: unable to open 'bogus-does-not-exist.zon': FileNotFound
test/incremental/change_zon_file created+46
......@@ -0,0 +1,46 @@
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe
4//#target=wasm32-wasi-selfhosted
5#update=initial version
6#file=main.zig
7const std = @import("std");
8const message: []const u8 = @import("message.zon");
9pub fn main() !void {
10 try std.io.getStdOut().writeAll(message);
11}
12#file=message.zon
13"Hello, World!\n"
14#expect_stdout="Hello, World!\n"
15
16#update=change ZON file contents
17#file=message.zon
18"Hello again, World!\n"
19#expect_stdout="Hello again, World!\n"
20
21#update=delete file
22#rm_file=message.zon
23#expect_error=message.zon:1:1: error: unable to load './message.zon': FileNotFound
24
25#update=remove reference to ZON file
26#file=main.zig
27const std = @import("std");
28const message: []const u8 = @import("message.zon");
29pub fn main() !void {
30 try std.io.getStdOut().writeAll("a hardcoded string\n");
31}
32#expect_error=message.zon:1:1: error: unable to load './message.zon': FileNotFound
33
34#update=recreate ZON file
35#file=message.zon
36"We're back, World!\n"
37#expect_stdout="a hardcoded string\n"
38
39#update=re-introduce reference to ZON file
40#file=main.zig
41const std = @import("std");
42const message: []const u8 = @import("message.zon");
43pub fn main() !void {
44 try std.io.getStdOut().writeAll(message);
45}
46#expect_stdout="We're back, World!\n"