diff options
| author | Matthew Lugg <mlugg@mlugg.co.uk> | 2025-02-05 12:17:13 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-05 12:17:13 +0000 |
| commit | f01f1e33c96f0b00db8e036a654c1b3bf8531cd8 (patch) | |
| tree | 3daca71f83a02d73d4c93d973d7022776e476274 /src | |
| parent | cf059ee08716300e924bced08ebdd5bd8f97d789 (diff) | |
| parent | bebfa036ba52076cd03f9ef943f61da64ba6e97b (diff) | |
| download | zig-f01f1e33c96f0b00db8e036a654c1b3bf8531cd8.tar.gz zig-f01f1e33c96f0b00db8e036a654c1b3bf8531cd8.zip | |
Merge pull request #22754 from mlugg/files-and-stuff
ZON and incremental bits
Diffstat (limited to 'src')
| -rw-r--r-- | src/Builtin.zig | 24 | ||||
| -rw-r--r-- | src/Compilation.zig | 146 | ||||
| -rw-r--r-- | src/InternPool.zig | 20 | ||||
| -rw-r--r-- | src/Package/Module.zig | 11 | ||||
| -rw-r--r-- | src/Sema.zig | 24 | ||||
| -rw-r--r-- | src/Sema/LowerZon.zig | 2 | ||||
| -rw-r--r-- | src/Type.zig | 7 | ||||
| -rw-r--r-- | src/Zcu.zig | 352 | ||||
| -rw-r--r-- | src/Zcu/PerThread.zig | 551 | ||||
| -rw-r--r-- | src/link.zig | 3 | ||||
| -rw-r--r-- | src/link/Dwarf.zig | 17 | ||||
| -rw-r--r-- | src/main.zig | 115 | ||||
| -rw-r--r-- | src/print_zir.zig | 23 |
13 files changed, 717 insertions, 578 deletions
diff --git a/src/Builtin.zig b/src/Builtin.zig index 1782527f69..ac23cafb3c 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -264,14 +264,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { } pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void { - assert(file.source_loaded == true); - if (mod.root.statFile(mod.root_src_path)) |stat| { - if (stat.size != file.source.len) { + if (stat.size != file.source.?.len) { std.log.warn( "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++ "Overwriting with correct file contents now", - .{ mod.root, mod.root_src_path, file.source.len, stat.size }, + .{ mod.root, mod.root_src_path, file.source.?.len, stat.size }, ); try writeFile(file, mod); @@ -296,15 +294,13 @@ pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void { log.debug("parsing and generating '{s}'", .{mod.root_src_path}); - file.tree = try std.zig.Ast.parse(comp.gpa, file.source, .zig); - assert(file.tree.errors.len == 0); // builtin.zig must parse - file.tree_loaded = true; + file.tree = try std.zig.Ast.parse(comp.gpa, file.source.?, .zig); + assert(file.tree.?.errors.len == 0); // builtin.zig must parse - file.zir = try AstGen.generate(comp.gpa, file.tree); - assert(!file.zir.hasCompileErrors()); // builtin.zig must not have astgen errors - file.zir_loaded = true; - file.status = .success_zir; - // Note that whilst we set `zir_loaded` here, we populated `path_digest` + file.zir = try AstGen.generate(comp.gpa, file.tree.?); + assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors + file.status = .success; + // Note that whilst we set `zir` here, we populated `path_digest` // all the way back in `Package.Module.create`. } @@ -312,7 +308,7 @@ fn writeFile(file: *File, mod: *Module) !void { var buf: [std.fs.max_path_bytes]u8 = undefined; var af = try mod.root.atomicFile(mod.root_src_path, .{ .make_path = true }, &buf); defer af.deinit(); - try af.file.writeAll(file.source); + try af.file.writeAll(file.source.?); af.finish() catch |err| switch (err) { error.AccessDenied => switch (builtin.os.tag) { .windows => { @@ -326,7 +322,7 @@ fn writeFile(file: *File, mod: *Module) !void { }; file.stat = .{ - .size = file.source.len, + .size = file.source.?.len, .inode = 0, // dummy value .mtime = 0, // dummy value }; diff --git a/src/Compilation.zig b/src/Compilation.zig index dc1d7df320..14c216854e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2220,10 +2220,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count()); for (zcu.import_table.values()) |file_index| { if (zcu.fileByIndex(file_index).mod.isBuiltin()) continue; - const file = zcu.fileByIndex(file_index); - if (file.getMode() == .zig) { - comp.astgen_work_queue.writeItemAssumeCapacity(file_index); - } + comp.astgen_work_queue.writeItemAssumeCapacity(file_index); } if (comp.file_system_inputs) |fsi| { for (zcu.import_table.values()) |file_index| { @@ -2906,10 +2903,12 @@ pub fn makeBinFileWritable(comp: *Compilation) !void { const Header = extern struct { intern_pool: extern struct { thread_count: u32, - file_deps_len: u32, src_hash_deps_len: u32, nav_val_deps_len: u32, nav_ty_deps_len: u32, + interned_deps_len: u32, + zon_file_deps_len: u32, + embed_file_deps_len: u32, namespace_deps_len: u32, namespace_name_deps_len: u32, first_dependency_len: u32, @@ -2950,10 +2949,12 @@ pub fn saveState(comp: *Compilation) !void { const header: Header = .{ .intern_pool = .{ .thread_count = @intCast(ip.locals.len), - .file_deps_len = @intCast(ip.file_deps.count()), .src_hash_deps_len = @intCast(ip.src_hash_deps.count()), .nav_val_deps_len = @intCast(ip.nav_val_deps.count()), .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()), + .interned_deps_len = @intCast(ip.interned_deps.count()), + .zon_file_deps_len = @intCast(ip.zon_file_deps.count()), + .embed_file_deps_len = @intCast(ip.embed_file_deps.count()), .namespace_deps_len = @intCast(ip.namespace_deps.count()), .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()), .first_dependency_len = @intCast(ip.first_dependency.count()), @@ -2978,14 +2979,18 @@ pub fn saveState(comp: *Compilation) !void { addBuf(&bufs, mem.asBytes(&header)); addBuf(&bufs, mem.sliceAsBytes(pt_headers.items)); - addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.values())); addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys())); addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values())); addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys())); addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values())); addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys())); addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values())); + addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.keys())); + addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.values())); + addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.keys())); + addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.values())); + addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.keys())); + addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.values())); addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys())); addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values())); addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys())); @@ -3203,15 +3208,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { } if (comp.zcu) |zcu| { - const ip = &zcu.intern_pool; - for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| { if (error_msg) |msg| { try addModuleErrorMsg(zcu, &bundle, msg.*); } else { // Must be ZIR or Zoir errors. Note that this may include AST errors. _ = try file.getTree(gpa); // Tree must be loaded. - if (file.zir_loaded) { + if (file.zir != null) { try addZirErrorMessages(&bundle, file); } else if (file.zoir != null) { try addZoirErrorMessages(&bundle, file); @@ -3277,20 +3280,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { if (!refs.contains(anal_unit)) continue; } - report_ok: { - const file_index = switch (anal_unit.unwrap()) { - .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip), - .nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip), - .type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip), - .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip), - .memoized_state => break :report_ok, // always report std.builtin errors - }; - - // Skip errors for AnalUnits within files that had a parse failure. - // We'll try again once parsing succeeds. - if (!zcu.fileByIndex(file_index).okToReportErrors()) continue; - } - std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{ error_msg.msg, zcu.fmtAnalUnit(anal_unit), @@ -3318,12 +3307,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { } } } - for (zcu.failed_codegen.keys(), zcu.failed_codegen.values()) |nav, error_msg| { - if (!zcu.navFileScope(nav).okToReportErrors()) continue; + for (zcu.failed_codegen.values()) |error_msg| { try addModuleErrorMsg(zcu, &bundle, error_msg.*); } - for (zcu.failed_types.keys(), zcu.failed_types.values()) |ty_index, error_msg| { - if (!zcu.typeFileScope(ty_index).okToReportErrors()) continue; + for (zcu.failed_types.values()) |error_msg| { try addModuleErrorMsg(zcu, &bundle, error_msg.*); } for (zcu.failed_exports.values()) |value| { @@ -3623,22 +3610,17 @@ pub fn addModuleErrorMsg( } pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void { - assert(file.zir_loaded); - assert(file.tree_loaded); - assert(file.source_loaded); const gpa = eb.gpa; const src_path = try file.fullPath(gpa); defer gpa.free(src_path); - return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path); + return eb.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, src_path); } pub fn addZoirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void { - assert(file.source_loaded); - assert(file.tree_loaded); const gpa = eb.gpa; const src_path = try file.fullPath(gpa); defer gpa.free(src_path); - return eb.addZoirErrorMessages(file.zoir.?, file.tree, file.source, src_path); + return eb.addZoirErrorMessages(file.zoir.?, file.tree.?, file.source.?, src_path); } pub fn performAllTheWork( @@ -3802,7 +3784,7 @@ fn performAllTheWorkInner( // will be needed by the worker threads. const path_digest = zcu.filePathDigest(file_index); const file = zcu.fileByIndex(file_index); - comp.thread_pool.spawnWgId(&astgen_wait_group, workerAstGenFile, .{ + comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateFile, .{ comp, file, file_index, path_digest, zir_prog_node, &astgen_wait_group, .root, }); } @@ -3810,7 +3792,7 @@ fn performAllTheWorkInner( for (0.., zcu.embed_table.values()) |ef_index_usize, ef| { const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize); - comp.thread_pool.spawnWgId(&astgen_wait_group, workerCheckEmbedFile, .{ + comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateEmbedFile, .{ comp, ef_index, ef, }); } @@ -3832,12 +3814,64 @@ fn performAllTheWorkInner( if (comp.zcu) |zcu| { const pt: Zcu.PerThread = .activate(zcu, .main); defer pt.deactivate(); + + // If the cache mode is `whole`, then add every source file to the cache manifest. + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |man| { + const gpa = zcu.gpa; + for (zcu.import_table.values()) |file_index| { + const file = zcu.fileByIndex(file_index); + const source = file.getSource(gpa) catch |err| { + try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)}); + continue; + }; + const resolved_path = try std.fs.path.resolve(gpa, &.{ + file.mod.root.root_dir.path orelse ".", + file.mod.root.sub_path, + file.sub_file_path, + }); + errdefer gpa.free(resolved_path); + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => { + try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)}); + continue; + }, + }; + } + }, + .incremental => {}, + } + + try reportMultiModuleErrors(pt); + + const any_fatal_files = for (zcu.import_table.values()) |file_index| { + const file = zcu.fileByIndex(file_index); + switch (file.status) { + .never_loaded => unreachable, // everything is loaded by the workers + .retryable_failure, .astgen_failure => break true, + .success => {}, + } + } else false; + + if (any_fatal_files or comp.alloc_failure_occurred) { + // We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents + // us from invalidating lots of incremental dependencies due to files with e.g. parse errors. + // However, this means our analysis data is invalid, so we want to omit all analysis errors. + // To do that, let's just clear the analysis roots! + + assert(zcu.failed_files.count() > 0); // we will get an error + zcu.analysis_roots.clear(); // no analysis happened + return; + } + if (comp.incremental) { const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0); defer update_zir_refs_node.end(); try pt.updateZirRefs(); } - try reportMultiModuleErrors(pt); try zcu.flushRetryableFailures(); 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 }; } -fn workerAstGenFile( +fn workerUpdateFile( tid: usize, comp: *Compilation, file: *Zcu.File, @@ -4290,40 +4324,44 @@ fn workerAstGenFile( wg: *WaitGroup, src: Zcu.AstGenSrc, ) void { - assert(file.getMode() == .zig); const child_prog_node = prog_node.start(file.sub_file_path, 0); defer child_prog_node.end(); const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); defer pt.deactivate(); - pt.astGenFile(file, path_digest) catch |err| switch (err) { + pt.updateFile(file, path_digest) catch |err| switch (err) { error.AnalysisFail => return, else => { - file.status = .retryable_failure; pt.reportRetryableAstGenError(src, file_index, err) catch |oom| switch (oom) { - // Swallowing this error is OK because it's implied to be OOM when - // there is a missing `failed_files` error message. - error.OutOfMemory => {}, + error.OutOfMemory => { + comp.mutex.lock(); + defer comp.mutex.unlock(); + comp.setAllocFailure(); + }, }; return; }, }; + switch (file.getMode()) { + .zig => {}, // continue to logic below + .zon => return, // ZON can't import anything so we're done + } + // Pre-emptively look for `@import` paths and queue them up. // If we experience an error preemptively fetching the // file, just ignore it and let it happen again later during Sema. - assert(file.zir_loaded); - const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)]; + const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)]; if (imports_index != 0) { - const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); + const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index); var import_i: u32 = 0; var extra_index = extra.end; while (import_i < extra.data.imports_len) : (import_i += 1) { - const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index); + const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index); extra_index = item.end; - const import_path = file.zir.nullTerminatedString(item.data.name); + const import_path = file.zir.?.nullTerminatedString(item.data.name); // `@import("builtin")` is handled specially. if (mem.eql(u8, import_path, "builtin")) continue; @@ -4344,7 +4382,7 @@ fn workerAstGenFile( const imported_path_digest = pt.zcu.filePathDigest(res.file_index); break :blk .{ res, imported_path_digest }; }; - if (import_result.is_new and import_result.file.getMode() == .zig) { + if (import_result.is_new) { log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ file.sub_file_path, import_path, import_result.file.sub_file_path, }); @@ -4352,7 +4390,7 @@ fn workerAstGenFile( .importing_file = file_index, .import_tok = item.data.token, } }; - comp.thread_pool.spawnWgId(wg, workerAstGenFile, .{ + comp.thread_pool.spawnWgId(wg, workerUpdateFile, .{ comp, import_result.file, import_result.file_index, imported_path_digest, prog_node, wg, sub_src, }); } @@ -4375,7 +4413,7 @@ fn workerUpdateBuiltinZigFile( }; } -fn workerCheckEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void { +fn workerUpdateEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void { comp.detectEmbedFileUpdate(@enumFromInt(tid), ef_index, ef) catch |err| switch (err) { error.OutOfMemory => { comp.mutex.lock(); diff --git a/src/InternPool.zig b/src/InternPool.zig index 4a6b5a86d2..60d24223ce 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -17,13 +17,6 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32), /// Cached shift amount to put a `tid` in the top bits of a 32-bit value. tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32), -/// Dependencies on whether an entire file gets past AstGen. -/// These are triggered by `@import`, so that: -/// * if a file initially fails AstGen, triggering a transitive failure, when a future update -/// causes it to succeed AstGen, the `@import` is re-analyzed, allowing analysis to proceed -/// * if a file initially succeds AstGen, but a future update causes the file to fail it, -/// the `@import` is re-analyzed, registering a transitive failure -file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index), /// Dependencies on the source code hash associated with a ZIR instruction. /// * For a `declaration`, this is the entire declaration body. /// * 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), /// * a container type requiring resolution (invalidated when the type must be recreated at a new index) /// Value is index into `dep_entries` of the first dependency on this interned value. interned_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index), +/// Dependencies on a ZON file. Triggered by `@import` of ZON. +/// Value is index into `dep_entries` of the first dependency on this ZON file. +zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index), /// Dependencies on an embedded file. /// Introduced by `@embedFile`; invalidated when the file changes. /// Value is index into `dep_entries` of the first dependency on this `Zcu.EmbedFile`. @@ -89,11 +85,11 @@ pub const empty: InternPool = .{ .tid_shift_30 = if (single_threaded) 0 else 31, .tid_shift_31 = if (single_threaded) 0 else 31, .tid_shift_32 = if (single_threaded) 0 else 31, - .file_deps = .empty, .src_hash_deps = .empty, .nav_val_deps = .empty, .nav_ty_deps = .empty, .interned_deps = .empty, + .zon_file_deps = .empty, .embed_file_deps = .empty, .namespace_deps = .empty, .namespace_name_deps = .empty, @@ -824,11 +820,11 @@ pub const Nav = struct { }; pub const Dependee = union(enum) { - file: FileIndex, src_hash: TrackedInst.Index, nav_val: Nav.Index, nav_ty: Nav.Index, interned: Index, + zon_file: FileIndex, embed_file: Zcu.EmbedFile.Index, namespace: TrackedInst.Index, namespace_name: NamespaceNameKey, @@ -876,11 +872,11 @@ pub const DependencyIterator = struct { pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator { const first_entry = switch (dependee) { - .file => |x| ip.file_deps.get(x), .src_hash => |x| ip.src_hash_deps.get(x), .nav_val => |x| ip.nav_val_deps.get(x), .nav_ty => |x| ip.nav_ty_deps.get(x), .interned => |x| ip.interned_deps.get(x), + .zon_file => |x| ip.zon_file_deps.get(x), .embed_file => |x| ip.embed_file_deps.get(x), .namespace => |x| ip.namespace_deps.get(x), .namespace_name => |x| ip.namespace_name_deps.get(x), @@ -947,11 +943,11 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend }, inline else => |dependee_payload, tag| new_index: { const gop = try switch (tag) { - .file => ip.file_deps, .src_hash => ip.src_hash_deps, .nav_val => ip.nav_val_deps, .nav_ty => ip.nav_ty_deps, .interned => ip.interned_deps, + .zon_file => ip.zon_file_deps, .embed_file => ip.embed_file_deps, .namespace => ip.namespace_deps, .namespace_name => ip.namespace_name_deps, @@ -6688,11 +6684,11 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { pub fn deinit(ip: *InternPool, gpa: Allocator) void { if (debug_state.enable_checks) std.debug.assert(debug_state.intern_pool == null); - ip.file_deps.deinit(gpa); ip.src_hash_deps.deinit(gpa); ip.nav_val_deps.deinit(gpa); ip.nav_ty_deps.deinit(gpa); ip.interned_deps.deinit(gpa); + ip.zon_file_deps.deinit(gpa); ip.embed_file_deps.deinit(gpa); ip.namespace_deps.deinit(gpa); ip.namespace_name_deps.deinit(gpa); diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 5b3a487a49..0dec7bde76 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -482,15 +482,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { }; new_file.* = .{ .sub_file_path = "builtin.zig", - .source = generated_builtin_source, - .source_loaded = true, - .tree_loaded = false, - .zir_loaded = false, .stat = undefined, - .tree = undefined, - .zir = undefined, + .source = generated_builtin_source, + .tree = null, + .zir = null, + .zoir = null, .status = .never_loaded, - .prev_status = .never_loaded, .mod = new, }; break :b new; diff --git a/src/Sema.zig b/src/Sema.zig index 5f4463a9d5..ad144bf013 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6140,10 +6140,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); const path_digest = zcu.filePathDigest(result.file_index); - pt.astGenFile(result.file, path_digest) catch |err| + pt.updateFile(result.file, path_digest) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); - try sema.declareDependency(.{ .file = result.file_index }); try pt.ensureFileAnalyzed(result.file_index); const ty = zcu.fileRootType(result.file_index); try sema.declareDependency(.{ .interned = ty }); @@ -7649,9 +7648,8 @@ fn analyzeCall( const nav = ip.getNav(info.owner_nav); const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(resolved_func_inst.file); - assert(file.zir_loaded); - const zir_info = file.zir.getFnInfo(resolved_func_inst.inst); - break :b .{ nav, file.zir, info.zir_body_inst, resolved_func_inst.inst, zir_info }; + const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst); + break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info }; } else .{ undefined, undefined, undefined, undefined, undefined }; // 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. }; switch (result.file.getMode()) { .zig => { - try sema.declareDependency(.{ .file = result.file_index }); try pt.ensureFileAnalyzed(result.file_index); const ty = zcu.fileRootType(result.file_index); try sema.declareDependency(.{ .interned = ty }); @@ -13995,12 +13992,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return Air.internedToRef(ty); }, .zon => { - _ = result.file.getTree(zcu.gpa) catch |err| { - // TODO: these errors are file system errors; make sure an update() will - // retry this and not cache the file system error, which may be transient. - return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ result.file.sub_file_path, @errorName(err) }); - }; - if (extra.res_ty == .none) { return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{}); } @@ -14010,6 +14001,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{}); } + try sema.declareDependency(.{ .zon_file = result.file_index }); const interned = try LowerZon.run( sema, result.file, @@ -35328,7 +35320,7 @@ fn backingIntType( break :blk accumulator; }; - const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir; + const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?; const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; assert(extended.opcode == .struct_decl); @@ -35948,7 +35940,7 @@ fn structFields( const gpa = zcu.gpa; const ip = &zcu.intern_pool; const namespace_index = struct_type.namespace; - const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; + const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; const fields_len, _, var extra_index = structZirInfo(zir, zir_index); @@ -36149,7 +36141,7 @@ fn structFieldInits( assert(!struct_type.haveFieldInits(ip)); const namespace_index = struct_type.namespace; - const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; + const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; const fields_len, _, var extra_index = structZirInfo(zir, zir_index); @@ -36268,7 +36260,7 @@ fn unionFields( const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir; + const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?; const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail; const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; assert(extended.opcode == .union_decl); diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index 2b2d16b90a..f30879090b 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -39,8 +39,6 @@ pub fn run( ) CompileError!InternPool.Index { const pt = sema.pt; - _ = try file.getZoir(pt.zcu); - const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{ .file = file_index, .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file diff --git a/src/Type.zig b/src/Type.zig index 0fd6e184d8..690cc3fea0 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3587,8 +3587,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { }; const info = tracked.resolveFull(&zcu.intern_pool) orelse return null; const file = zcu.fileByIndex(info.file); - assert(file.zir_loaded); - const zir = file.zir; + const zir = file.zir.?; const inst = zir.instructions.get(@intFromEnum(info.inst)); return switch (inst.tag) { .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( var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa); defer comptime_err_ret_trace.deinit(); - const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir; + const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir.?; var sema: Sema = .{ .pt = pt, .gpa = gpa, @@ -3959,7 +3958,7 @@ fn resolveUnionInner( var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa); defer comptime_err_ret_trace.deinit(); - const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir; + const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir.?; var sema: Sema = .{ .pt = pt, .gpa = gpa, diff --git a/src/Zcu.zig b/src/Zcu.zig index 507452dda3..9b292ad783 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -658,24 +658,35 @@ pub const Namespace = struct { }; pub const File = struct { - status: Status, - prev_status: Status, - source_loaded: bool, - tree_loaded: bool, - zir_loaded: bool, /// Relative to the owning package's root source directory. /// Memory is stored in gpa, owned by File. sub_file_path: []const u8, - /// Whether this is populated depends on `source_loaded`. - source: [:0]const u8, + + status: enum { + /// We have not yet attempted to load this file. + /// `stat` is not populated and may be `undefined`. + never_loaded, + /// A filesystem access failed. It should be retried on the next update. + /// There is a `failed_files` entry containing a non-`null` message. + /// `stat` is not populated and may be `undefined`. + retryable_failure, + /// Parsing/AstGen/ZonGen of this file has failed. + /// There is an error in `zir` or `zoir`. + /// There is a `failed_files` entry (with a `null` message). + /// `stat` is populated. + astgen_failure, + /// Parsing and AstGen/ZonGen of this file has succeeded. + /// `stat` is populated. + success, + }, /// Whether this is populated depends on `status`. stat: Cache.File.Stat, - /// Whether this is populated or not depends on `tree_loaded`. - tree: Ast, - /// Whether this is populated or not depends on `zir_loaded`. - zir: Zir, - /// Cached Zoir, generated lazily. - zoir: ?Zoir = null, + + source: ?[:0]const u8, + tree: ?Ast, + zir: ?Zir, + zoir: ?Zoir, + /// Module that this file is a part of, managed externally. mod: *Package.Module, /// 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 { /// List of references to this file, used for multi-package errors. references: std.ArrayListUnmanaged(File.Reference) = .empty, - /// The most recent successful ZIR for this file, with no errors. - /// This is only populated when a previously successful ZIR - /// newly introduces compile errors during an update. When ZIR is - /// successful, this field is unloaded. + /// The ZIR for this file from the last update with no file failures. As such, this ZIR is never + /// failed (although it may have compile errors). + /// + /// Because updates with file failures do not perform ZIR mapping or semantic analysis, we keep + /// this around so we have the "old" ZIR to map when an update is ready to do so. Once such an + /// update occurs, this field is unloaded, since it is no longer necessary. + /// + /// In other words, if `TrackedInst`s are tied to ZIR other than what's in the `zir` field, this + /// field is populated with that old ZIR. prev_zir: ?*Zir = null, - pub const Status = enum { - never_loaded, - retryable_failure, - parse_failure, - astgen_failure, - success_zir, - }; + /// This field serves a similar purpose to `prev_zir`, but for ZOIR. However, since we do not + /// need to map old ZOIR to new ZOIR -- instead only invalidating dependencies if the ZOIR + /// changed -- this field is just a simple boolean. + /// + /// When `zoir` is updated, this field is set to `true`. In `updateZirRefs`, if this is `true`, + /// we invalidate the corresponding `zon_file` dependency, and reset it to `false`. + zoir_invalidated: bool = false, /// A single reference to a file. pub const Reference = union(enum) { @@ -727,23 +743,23 @@ pub const File = struct { } pub fn unloadTree(file: *File, gpa: Allocator) void { - if (file.tree_loaded) { - file.tree_loaded = false; - file.tree.deinit(gpa); + if (file.tree) |*tree| { + tree.deinit(gpa); + file.tree = null; } } pub fn unloadSource(file: *File, gpa: Allocator) void { - if (file.source_loaded) { - file.source_loaded = false; - gpa.free(file.source); + if (file.source) |source| { + gpa.free(source); + file.source = null; } } pub fn unloadZir(file: *File, gpa: Allocator) void { - if (file.zir_loaded) { - file.zir_loaded = false; - file.zir.deinit(gpa); + if (file.zir) |*zir| { + zir.deinit(gpa); + file.zir = null; } } @@ -753,8 +769,8 @@ pub const File = struct { }; pub fn getSource(file: *File, gpa: Allocator) !Source { - if (file.source_loaded) return Source{ - .bytes = file.source, + if (file.source) |source| return .{ + .bytes = source, .stat = file.stat, }; @@ -769,18 +785,20 @@ pub const File = struct { return error.FileTooBig; const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0); - defer if (!file.source_loaded) gpa.free(source); + errdefer gpa.free(source); + const amt = try f.readAll(source); if (amt != stat.size) return error.UnexpectedEndOfFile; // Here we do not modify stat fields because this function is the one // used for error reporting. We need to keep the stat fields stale so that - // astGenFile can know to regenerate ZIR. + // updateFile can know to regenerate ZIR. file.source = source; - file.source_loaded = true; - return Source{ + errdefer comptime unreachable; // don't error after populating `source` + + return .{ .bytes = source, .stat = .{ .size = stat.size, @@ -791,20 +809,20 @@ pub const File = struct { } pub fn getTree(file: *File, gpa: Allocator) !*const Ast { - if (file.tree_loaded) return &file.tree; + if (file.tree) |*tree| return tree; const source = try file.getSource(gpa); - file.tree = try Ast.parse(gpa, source.bytes, file.getMode()); - file.tree_loaded = true; - return &file.tree; + file.tree = try .parse(gpa, source.bytes, file.getMode()); + return &file.tree.?; } pub fn getZoir(file: *File, zcu: *Zcu) !*const Zoir { if (file.zoir) |*zoir| return zoir; - assert(file.tree_loaded); - assert(file.tree.mode == .zon); - file.zoir = try ZonGen.generate(zcu.gpa, file.tree, .{}); + const tree = file.tree.?; + assert(tree.mode == .zon); + + file.zoir = try ZonGen.generate(zcu.gpa, tree, .{}); if (file.zoir.?.hasCompileErrors()) { try zcu.failed_files.putNoClobber(zcu.gpa, file, null); return error.AnalysisFail; @@ -854,13 +872,6 @@ pub const File = struct { std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 }); } - pub fn okToReportErrors(file: File) bool { - return switch (file.status) { - .parse_failure, .astgen_failure => false, - else => true, - }; - } - /// Add a reference to this file during AstGen. pub fn addReference(file: *File, zcu: *Zcu, ref: File.Reference) !void { // 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 { // We can only mark children as failed if the ZIR is loaded, which may not // be the case if there were other astgen failures in this file - if (!file.zir_loaded) return; + if (file.zir == null) return; - const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)]; + const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)]; if (imports_index == 0) return; - const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); + const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index); var extra_index = extra.end; for (0..extra.data.imports_len) |_| { - const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index); + const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index); extra_index = item.end; - const import_path = file.zir.nullTerminatedString(item.data.name); + const import_path = file.zir.?.nullTerminatedString(item.data.name); if (mem.eql(u8, import_path, "builtin")) continue; const res = pt.importFile(file, import_path) catch continue; @@ -1012,7 +1023,7 @@ pub const SrcLoc = struct { lazy: LazySrcLoc.Offset, pub fn baseSrcToken(src_loc: SrcLoc) Ast.TokenIndex { - const tree = src_loc.file_scope.tree; + const tree = src_loc.file_scope.tree.?; return tree.firstToken(src_loc.base_node); } @@ -1057,7 +1068,6 @@ pub const SrcLoc = struct { const node_off = traced_off.x; const tree = try src_loc.file_scope.getTree(gpa); const node = src_loc.relativeToNodeIndex(node_off); - assert(src_loc.file_scope.tree_loaded); return tree.nodeToSpan(node); }, .node_offset_main_token => |node_off| { @@ -1069,7 +1079,6 @@ pub const SrcLoc = struct { .node_offset_bin_op => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); const node = src_loc.relativeToNodeIndex(node_off); - assert(src_loc.file_scope.tree_loaded); return tree.nodeToSpan(node); }, .node_offset_initializer => |node_off| { @@ -2408,9 +2417,8 @@ pub const LazySrcLoc = struct { if (zir_inst == .main_struct_inst) return .{ file, 0 }; // Otherwise, make sure ZIR is loaded. - assert(file.zir_loaded); + const zir = file.zir.?; - const zir = file.zir; const inst = zir.instructions.get(@intFromEnum(zir_inst)); const base_node: Ast.Node.Index = switch (inst.tag) { .declaration => inst.data.declaration.src_node, @@ -2643,6 +2651,189 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.F return zir; } +pub fn saveZirCache(gpa: Allocator, cache_file: std.fs.File, stat: std.fs.File.Stat, zir: Zir) (std.fs.File.WriteError || Allocator.Error)!void { + const safety_buffer = if (data_has_safety_tag) + try gpa.alloc([8]u8, zir.instructions.len) + else + undefined; + defer if (data_has_safety_tag) gpa.free(safety_buffer); + + const data_ptr: [*]const u8 = if (data_has_safety_tag) + if (zir.instructions.len == 0) + undefined + else + @ptrCast(safety_buffer.ptr) + else + @ptrCast(zir.instructions.items(.data).ptr); + + if (data_has_safety_tag) { + // The `Data` union has a safety tag but in the file format we store it without. + for (zir.instructions.items(.data), 0..) |*data, i| { + const as_struct: *const HackDataLayout = @ptrCast(data); + safety_buffer[i] = as_struct.data; + } + } + + const header: Zir.Header = .{ + .instructions_len = @intCast(zir.instructions.len), + .string_bytes_len = @intCast(zir.string_bytes.len), + .extra_len = @intCast(zir.extra.len), + + .stat_size = stat.size, + .stat_inode = stat.inode, + .stat_mtime = stat.mtime, + }; + var iovecs: [5]std.posix.iovec_const = .{ + .{ + .base = @ptrCast(&header), + .len = @sizeOf(Zir.Header), + }, + .{ + .base = @ptrCast(zir.instructions.items(.tag).ptr), + .len = zir.instructions.len, + }, + .{ + .base = data_ptr, + .len = zir.instructions.len * 8, + }, + .{ + .base = zir.string_bytes.ptr, + .len = zir.string_bytes.len, + }, + .{ + .base = @ptrCast(zir.extra.ptr), + .len = zir.extra.len * 4, + }, + }; + try cache_file.writevAll(&iovecs); +} + +pub fn saveZoirCache(cache_file: std.fs.File, stat: std.fs.File.Stat, zoir: Zoir) std.fs.File.WriteError!void { + const header: Zoir.Header = .{ + .nodes_len = @intCast(zoir.nodes.len), + .extra_len = @intCast(zoir.extra.len), + .limbs_len = @intCast(zoir.limbs.len), + .string_bytes_len = @intCast(zoir.string_bytes.len), + .compile_errors_len = @intCast(zoir.compile_errors.len), + .error_notes_len = @intCast(zoir.error_notes.len), + + .stat_size = stat.size, + .stat_inode = stat.inode, + .stat_mtime = stat.mtime, + }; + var iovecs: [9]std.posix.iovec_const = .{ + .{ + .base = @ptrCast(&header), + .len = @sizeOf(Zoir.Header), + }, + .{ + .base = @ptrCast(zoir.nodes.items(.tag)), + .len = zoir.nodes.len * @sizeOf(Zoir.Node.Repr.Tag), + }, + .{ + .base = @ptrCast(zoir.nodes.items(.data)), + .len = zoir.nodes.len * 4, + }, + .{ + .base = @ptrCast(zoir.nodes.items(.ast_node)), + .len = zoir.nodes.len * 4, + }, + .{ + .base = @ptrCast(zoir.extra), + .len = zoir.extra.len * 4, + }, + .{ + .base = @ptrCast(zoir.limbs), + .len = zoir.limbs.len * 4, + }, + .{ + .base = zoir.string_bytes.ptr, + .len = zoir.string_bytes.len, + }, + .{ + .base = @ptrCast(zoir.compile_errors), + .len = zoir.compile_errors.len * @sizeOf(Zoir.CompileError), + }, + .{ + .base = @ptrCast(zoir.error_notes), + .len = zoir.error_notes.len * @sizeOf(Zoir.CompileError.Note), + }, + }; + try cache_file.writevAll(&iovecs); +} + +pub fn loadZoirCacheBody(gpa: Allocator, header: Zoir.Header, cache_file: std.fs.File) !Zoir { + var zoir: Zoir = .{ + .nodes = .empty, + .extra = &.{}, + .limbs = &.{}, + .string_bytes = &.{}, + .compile_errors = &.{}, + .error_notes = &.{}, + }; + errdefer zoir.deinit(gpa); + + zoir.nodes = nodes: { + var nodes: std.MultiArrayList(Zoir.Node.Repr) = .empty; + defer nodes.deinit(gpa); + try nodes.setCapacity(gpa, header.nodes_len); + nodes.len = header.nodes_len; + break :nodes nodes.toOwnedSlice(); + }; + + zoir.extra = try gpa.alloc(u32, header.extra_len); + zoir.limbs = try gpa.alloc(std.math.big.Limb, header.limbs_len); + zoir.string_bytes = try gpa.alloc(u8, header.string_bytes_len); + + zoir.compile_errors = try gpa.alloc(Zoir.CompileError, header.compile_errors_len); + zoir.error_notes = try gpa.alloc(Zoir.CompileError.Note, header.error_notes_len); + + var iovecs: [8]std.posix.iovec = .{ + .{ + .base = @ptrCast(zoir.nodes.items(.tag)), + .len = header.nodes_len * @sizeOf(Zoir.Node.Repr.Tag), + }, + .{ + .base = @ptrCast(zoir.nodes.items(.data)), + .len = header.nodes_len * 4, + }, + .{ + .base = @ptrCast(zoir.nodes.items(.ast_node)), + .len = header.nodes_len * 4, + }, + .{ + .base = @ptrCast(zoir.extra), + .len = header.extra_len * 4, + }, + .{ + .base = @ptrCast(zoir.limbs), + .len = header.limbs_len * @sizeOf(std.math.big.Limb), + }, + .{ + .base = zoir.string_bytes.ptr, + .len = header.string_bytes_len, + }, + .{ + .base = @ptrCast(zoir.compile_errors), + .len = header.compile_errors_len * @sizeOf(Zoir.CompileError), + }, + .{ + .base = @ptrCast(zoir.error_notes), + .len = header.error_notes_len * @sizeOf(Zoir.CompileError.Note), + }, + }; + + const bytes_expected = expected: { + var n: usize = 0; + for (iovecs) |v| n += v.len; + break :expected n; + }; + + const bytes_read = try cache_file.readvAll(&iovecs); + if (bytes_read != bytes_expected) return error.UnexpectedFileSize; + return zoir; +} + pub fn markDependeeOutdated( zcu: *Zcu, /// 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 { return zcu.root_mod.optimize_mode; } -fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void { - switch (file.status) { - .success_zir, .retryable_failure => {}, - .never_loaded, .parse_failure, .astgen_failure => { - zcu.comp.mutex.lock(); - defer zcu.comp.mutex.unlock(); - if (zcu.failed_files.fetchSwapRemove(file)) |kv| { - if (kv.value) |msg| msg.destroy(zcu.gpa); // Delete previous error message. - } - }, - } -} - pub fn handleUpdateExports( zcu: *Zcu, export_indices: []const Export.Index, @@ -3670,9 +3848,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv // `test` declarations are analyzed depending on the test filter. const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue; const file = zcu.fileByIndex(inst_info.file); - // If the file failed AstGen, the TrackedInst refers to the old ZIR. - const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*; - const decl = zir.getDeclaration(inst_info.inst); + const decl = file.zir.?.getDeclaration(inst_info.inst); if (!comp.config.is_test or file.mod != zcu.main_mod) continue; @@ -3702,9 +3878,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv // These are named declarations. They are analyzed only if marked `export`. const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue; const file = zcu.fileByIndex(inst_info.file); - // If the file failed AstGen, the TrackedInst refers to the old ZIR. - const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*; - const decl = zir.getDeclaration(inst_info.inst); + const decl = file.zir.?.getDeclaration(inst_info.inst); if (decl.linkage == .@"export") { const unit: AnalUnit = .wrap(.{ .nav_val = nav }); if (!result.contains(unit)) { @@ -3720,9 +3894,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv // These are named declarations. They are analyzed only if marked `export`. const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue; const file = zcu.fileByIndex(inst_info.file); - // If the file failed AstGen, the TrackedInst refers to the old ZIR. - const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*; - const decl = zir.getDeclaration(inst_info.inst); + const decl = file.zir.?.getDeclaration(inst_info.inst); if (decl.linkage == .@"export") { const unit: AnalUnit = .wrap(.{ .nav_val = nav }); if (!result.contains(unit)) { @@ -3858,7 +4030,7 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { const ip = &zcu.intern_pool; const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?; const zir = zcu.fileByIndex(inst_info.file).zir; - return zir.getDeclaration(inst_info.inst).src_line; + return zir.?.getDeclaration(inst_info.inst).src_line; } pub 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 const zcu = data.zcu; const ip = &zcu.intern_pool; switch (data.dependee) { - .file => |file| { - const file_path = zcu.fileByIndex(file).sub_file_path; - return writer.print("file('{s}')", .{file_path}); - }, .src_hash => |ti| { const info = ti.resolveFull(ip) orelse { return writer.writeAll("inst(<lost>)"); @@ -3934,6 +4102,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}), else => unreachable, }, + .zon_file => |file| { + const file_path = zcu.fileByIndex(file).sub_file_path; + return writer.print("zon_file('{s}')", .{file_path}); + }, .embed_file => |ef_idx| { const ef = ef_idx.get(zcu); return writer.print("embed_file('{s}')", .{std.fs.path.fmtJoin(&.{ diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index d564ef8da5..32372bf597 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -26,6 +26,8 @@ const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); const Zcu = @import("../Zcu.zig"); const Zir = std.zig.Zir; +const Zoir = std.zig.Zoir; +const ZonGen = std.zig.ZonGen; zcu: *Zcu, @@ -73,7 +75,9 @@ pub fn destroyFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void { if (!is_builtin) gpa.destroy(file); } -pub fn astGenFile( +/// Ensures that `file` has up-to-date ZIR. If not, loads the ZIR cache or runs +/// AstGen as needed. Also updates `file.status`. +pub fn updateFile( pt: Zcu.PerThread, file: *Zcu.File, path_digest: Cache.BinDigest, @@ -109,7 +113,7 @@ pub fn astGenFile( break :lock .shared; }, - .parse_failure, .astgen_failure, .success_zir => lock: { + .astgen_failure, .success => lock: { const unchanged_metadata = stat.size == file.stat.size and stat.mtime == file.stat.mtime and @@ -126,6 +130,27 @@ pub fn astGenFile( }, }; + // The old compile error, if any, is no longer relevant. + pt.lockAndClearFileCompileError(file); + + // If `zir` is not null, and `prev_zir` is null, then `TrackedInst`s are associated with `zir`. + // We need to keep it around! + // As an optimization, also check `loweringFailed`; if true, but `prev_zir == null`, then this + // file has never passed AstGen, so we actually need not cache the old ZIR. + if (file.zir != null and file.prev_zir == null and !file.zir.?.loweringFailed()) { + assert(file.prev_zir == null); + const prev_zir_ptr = try gpa.create(Zir); + file.prev_zir = prev_zir_ptr; + prev_zir_ptr.* = file.zir.?; + file.zir = null; + } + + // If ZOIR is changing, then we need to invalidate dependencies on it + if (file.zoir != null) file.zoir_invalidated = true; + + // We're going to re-load everything, so unload source, AST, ZIR, ZOIR. + file.unload(gpa); + // We ask for a lock in order to coordinate with other zig processes. // If another process is already working on this file, we will get the cached // version. Likewise if we're working on AstGen and another process asks for @@ -180,190 +205,164 @@ pub fn astGenFile( }; defer cache_file.close(); - while (true) { - update: { - // First we read the header to determine the lengths of arrays. - const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) { - // This can happen if Zig bails out of this function between creating - // the cached file and writing it. - error.EndOfStream => break :update, - else => |e| return e, - }; - const unchanged_metadata = - stat.size == header.stat_size and - stat.mtime == header.stat_mtime and - stat.inode == header.stat_inode; - - if (!unchanged_metadata) { - log.debug("AstGen cache stale: {s}", .{file.sub_file_path}); - break :update; - } - log.debug("AstGen cache hit: {s} instructions_len={d}", .{ - file.sub_file_path, header.instructions_len, - }); - - file.zir = Zcu.loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) { - error.UnexpectedFileSize => { - log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}); - break :update; - }, - else => |e| return e, - }; - file.zir_loaded = true; - file.stat = .{ - .size = header.stat_size, - .inode = header.stat_inode, - .mtime = header.stat_mtime, - }; - file.prev_status = file.status; - file.status = .success_zir; - log.debug("AstGen cached success: {s}", .{file.sub_file_path}); - - if (file.zir.hasCompileErrors()) { - comp.mutex.lock(); - defer comp.mutex.unlock(); - try zcu.failed_files.putNoClobber(gpa, file, null); - } - if (file.zir.loweringFailed()) { - file.status = .astgen_failure; - return error.AnalysisFail; - } - return; + const need_update = while (true) { + const result = switch (file.getMode()) { + inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode), + }; + switch (result) { + .success => { + log.debug("AstGen cached success: {s}", .{file.sub_file_path}); + break false; + }, + .invalid => {}, + .truncated => log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}), + .stale => log.debug("AstGen cache stale: {s}", .{file.sub_file_path}), } // If we already have the exclusive lock then it is our job to update. - if (builtin.os.tag == .wasi or lock == .exclusive) break; + if (builtin.os.tag == .wasi or lock == .exclusive) break true; // Otherwise, unlock to give someone a chance to get the exclusive lock // and then upgrade to an exclusive lock. cache_file.unlock(); lock = .exclusive; try cache_file.lock(lock); - } + }; - // The cache is definitely stale so delete the contents to avoid an underwrite later. - cache_file.setEndPos(0) catch |err| switch (err) { - error.FileTooBig => unreachable, // 0 is not too big + if (need_update) { + // The cache is definitely stale so delete the contents to avoid an underwrite later. + cache_file.setEndPos(0) catch |err| switch (err) { + error.FileTooBig => unreachable, // 0 is not too big + else => |e| return e, + }; - else => |e| return e, - }; + if (stat.size > std.math.maxInt(u32)) + return error.FileTooBig; - pt.lockAndClearFileCompileError(file); + const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0); + defer if (file.source == null) gpa.free(source); + const amt = try source_file.readAll(source); + if (amt != stat.size) + return error.UnexpectedEndOfFile; - // Previous ZIR is kept for two reasons: - // - // 1. In case an update to the file causes a Parse or AstGen failure, we - // need to compare two successful ZIR files in order to proceed with an - // incremental update. This avoids needlessly tossing out semantic - // analysis work when an error is temporarily introduced. - // - // 2. In order to detect updates, we need to iterate over the intern pool - // values while comparing old ZIR to new ZIR. This is better done in a - // single-threaded context, so we need to keep both versions around - // until that point in the pipeline. Previous ZIR data is freed after - // that. - if (file.zir_loaded and !file.zir.loweringFailed()) { - assert(file.prev_zir == null); - const prev_zir_ptr = try gpa.create(Zir); - file.prev_zir = prev_zir_ptr; - prev_zir_ptr.* = file.zir; - file.zir = undefined; - file.zir_loaded = false; - } - file.unload(gpa); + file.source = source; + + // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen. + file.tree = try Ast.parse(gpa, source, file.getMode()); - if (stat.size > std.math.maxInt(u32)) - return error.FileTooBig; + switch (file.getMode()) { + .zig => { + file.zir = try AstGen.generate(gpa, file.tree.?); + Zcu.saveZirCache(gpa, cache_file, stat, file.zir.?) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{ + file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err), + }), + }; + }, + .zon => { + file.zoir = try ZonGen.generate(gpa, file.tree.?, .{}); + Zcu.saveZoirCache(cache_file, stat, file.zoir.?) catch |err| { + log.warn("unable to write cached ZOIR code for {}{s} to {}{s}: {s}", .{ + file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err), + }); + }; + }, + } - const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0); - defer if (!file.source_loaded) gpa.free(source); - const amt = try source_file.readAll(source); - if (amt != stat.size) - return error.UnexpectedEndOfFile; + log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); + } file.stat = .{ .size = stat.size, .inode = stat.inode, .mtime = stat.mtime, }; - file.source = source; - file.source_loaded = true; - file.tree = try Ast.parse(gpa, source, .zig); - file.tree_loaded = true; + // Now, `zir` or `zoir` is definitely populated and up-to-date. + // Mark file successes/failures as needed. - // Any potential AST errors are converted to ZIR errors here. - file.zir = try AstGen.generate(gpa, file.tree); - file.zir_loaded = true; - file.prev_status = file.status; - file.status = .success_zir; - log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); + switch (file.getMode()) { + .zig => { + if (file.zir.?.hasCompileErrors()) { + comp.mutex.lock(); + defer comp.mutex.unlock(); + try zcu.failed_files.putNoClobber(gpa, file, null); + } + if (file.zir.?.loweringFailed()) { + file.status = .astgen_failure; + } else { + file.status = .success; + } + }, + .zon => { + if (file.zoir.?.hasCompileErrors()) { + file.status = .astgen_failure; + comp.mutex.lock(); + defer comp.mutex.unlock(); + try zcu.failed_files.putNoClobber(gpa, file, null); + } else { + file.status = .success; + } + }, + } - const safety_buffer = if (Zcu.data_has_safety_tag) - try gpa.alloc([8]u8, file.zir.instructions.len) - else - undefined; - defer if (Zcu.data_has_safety_tag) gpa.free(safety_buffer); - const data_ptr = if (Zcu.data_has_safety_tag) - if (file.zir.instructions.len == 0) - @as([*]const u8, undefined) - else - @as([*]const u8, @ptrCast(safety_buffer.ptr)) - else - @as([*]const u8, @ptrCast(file.zir.instructions.items(.data).ptr)); - if (Zcu.data_has_safety_tag) { - // The `Data` union has a safety tag but in the file format we store it without. - for (file.zir.instructions.items(.data), 0..) |*data, i| { - const as_struct: *const Zcu.HackDataLayout = @ptrCast(data); - safety_buffer[i] = as_struct.data; - } + switch (file.status) { + .never_loaded => unreachable, + .retryable_failure => unreachable, + .astgen_failure => return error.AnalysisFail, + .success => return, } +} - const header: Zir.Header = .{ - .instructions_len = @as(u32, @intCast(file.zir.instructions.len)), - .string_bytes_len = @as(u32, @intCast(file.zir.string_bytes.len)), - .extra_len = @as(u32, @intCast(file.zir.extra.len)), +fn loadZirZoirCache( + zcu: *Zcu, + cache_file: std.fs.File, + stat: std.fs.File.Stat, + file: *Zcu.File, + comptime mode: Ast.Mode, +) !enum { success, invalid, truncated, stale } { + assert(file.getMode() == mode); - .stat_size = stat.size, - .stat_inode = stat.inode, - .stat_mtime = stat.mtime, - }; - var iovecs = [_]std.posix.iovec_const{ - .{ - .base = @as([*]const u8, @ptrCast(&header)), - .len = @sizeOf(Zir.Header), - }, - .{ - .base = @as([*]const u8, @ptrCast(file.zir.instructions.items(.tag).ptr)), - .len = file.zir.instructions.len, - }, - .{ - .base = data_ptr, - .len = file.zir.instructions.len * 8, - }, - .{ - .base = file.zir.string_bytes.ptr, - .len = file.zir.string_bytes.len, - }, - .{ - .base = @as([*]const u8, @ptrCast(file.zir.extra.ptr)), - .len = file.zir.extra.len * 4, - }, + const gpa = zcu.gpa; + + const Header = switch (mode) { + .zig => Zir.Header, + .zon => Zoir.Header, }; - cache_file.writevAll(&iovecs) catch |err| { - log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{ - file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err), - }); + + // First we read the header to determine the lengths of arrays. + const header = cache_file.reader().readStruct(Header) catch |err| switch (err) { + // This can happen if Zig bails out of this function between creating + // the cached file and writing it. + error.EndOfStream => return .invalid, + else => |e| return e, }; - if (file.zir.hasCompileErrors()) { - comp.mutex.lock(); - defer comp.mutex.unlock(); - try zcu.failed_files.putNoClobber(gpa, file, null); + const unchanged_metadata = + stat.size == header.stat_size and + stat.mtime == header.stat_mtime and + stat.inode == header.stat_inode; + + if (!unchanged_metadata) { + return .stale; } - if (file.zir.loweringFailed()) { - file.status = .astgen_failure; - return error.AnalysisFail; + + switch (mode) { + .zig => { + file.zir = Zcu.loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) { + error.UnexpectedFileSize => return .truncated, + else => |e| return e, + }; + }, + .zon => { + file.zoir = Zcu.loadZoirCacheBody(gpa, header, cache_file) catch |err| switch (err) { + error.UnexpectedFileSize => return .truncated, + else => |e| return e, + }; + }, } + + return .success; } const UpdatedFile = struct { @@ -384,24 +383,32 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { const gpa = zcu.gpa; // We need to visit every updated File for every TrackedInst in InternPool. + // This only includes Zig files; ZON files are omitted. var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .empty; defer cleanupUpdatedFiles(gpa, &updated_files); + for (zcu.import_table.values()) |file_index| { const file = zcu.fileByIndex(file_index); - if (file.prev_status != file.status and file.prev_status != .never_loaded) { - try zcu.markDependeeOutdated(.not_marked_po, .{ .file = file_index }); + assert(file.status == .success); + switch (file.getMode()) { + .zig => {}, // logic below + .zon => { + if (file.zoir_invalidated) { + try zcu.markDependeeOutdated(.not_marked_po, .{ .zon_file = file_index }); + file.zoir_invalidated = false; + } + continue; + }, } const old_zir = file.prev_zir orelse continue; - const new_zir = file.zir; + const new_zir = file.zir.?; const gop = try updated_files.getOrPut(gpa, file_index); assert(!gop.found_existing); gop.value_ptr.* = .{ .file = file, .inst_map = .{}, }; - if (!new_zir.loweringFailed()) { - try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map); - } + try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map); } if (updated_files.count() == 0) @@ -421,13 +428,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { .index = @intCast(tracked_inst_unwrapped_index), }).wrap(ip); const new_inst = updated_file.inst_map.get(old_inst) orelse { - // Tracking failed for this instruction. - // This may be due to changes in the ZIR, or AstGen might have failed due to a very broken file. - // Either way, invalidate associated `src_hash` deps. - log.debug("tracking failed for %{d}{s}", .{ - old_inst, - if (file.zir.loweringFailed()) " due to AstGen failure" else "", - }); + // Tracking failed for this instruction due to changes in the ZIR. + // Invalidate associated `src_hash` deps. + log.debug("tracking failed for %{d}", .{old_inst}); tracked_inst.inst = .lost; try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index }); continue; @@ -435,7 +438,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst); const old_zir = file.prev_zir.?.*; - const new_zir = file.zir; + const new_zir = file.zir.?; const old_tag = old_zir.instructions.items(.tag)[@intFromEnum(old_inst)]; const old_data = old_zir.instructions.items(.data)[@intFromEnum(old_inst)]; @@ -532,23 +535,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { for (updated_files.keys(), updated_files.values()) |file_index, updated_file| { const file = updated_file.file; - if (file.zir.loweringFailed()) { - // Keep `prev_zir` around: it's the last usable ZIR. - // Don't update the namespace, as we have no new data to update *to*. - } else { - const prev_zir = file.prev_zir.?; - file.prev_zir = null; - prev_zir.deinit(gpa); - gpa.destroy(prev_zir); - - // For every file which has changed, re-scan the namespace of the file's root struct type. - // These types are special-cased because they don't have an enclosing declaration which will - // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this - // now because this work is fast (no actual Sema work is happening, we're just updating the - // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace` - // will track some instructions. - try pt.updateFileNamespace(file_index); - } + + const prev_zir = file.prev_zir.?; + file.prev_zir = null; + prev_zir.deinit(gpa); + gpa.destroy(prev_zir); + + // For every file which has changed, re-scan the namespace of the file's root struct type. + // These types are special-cased because they don't have an enclosing declaration which will + // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this + // now because this work is fast (no actual Sema work is happening, we're just updating the + // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace` + // will track some instructions. + try pt.updateFileNamespace(file_index); } } @@ -750,6 +749,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU kv.value.destroy(gpa); } _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); + zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); } } else { // We can trust the current information about this unit. @@ -801,14 +801,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(inst_resolved.file); - // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is - // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends - // in `ensureComptimeUnitUpToDate`. - if (file.status != .success_zir) return error.AnalysisFail; - const zir = file.zir; - - // We are about to re-analyze this unit; drop its depenndencies. - zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); + const zir = file.zir.?; try zcu.analysis_in_progress.put(gpa, anal_unit, {}); 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 kv.value.destroy(gpa); } _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); + ip.removeDependenciesForDepender(gpa, anal_unit); } else { // We can trust the current information about this unit. if (prev_failed) return error.AnalysisFail; @@ -998,14 +992,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(inst_resolved.file); - // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is - // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends - // in `ensureComptimeUnitUpToDate`. - if (file.status != .success_zir) return error.AnalysisFail; - const zir = file.zir; - - // We are about to re-analyze this unit; drop its depenndencies. - zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); + const zir = file.zir.?; try zcu.analysis_in_progress.put(gpa, anal_unit, {}); errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); @@ -1306,6 +1293,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc kv.value.destroy(gpa); } _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); + ip.removeDependenciesForDepender(gpa, anal_unit); } else { // We can trust the current information about this unit. if (prev_failed) return error.AnalysisFail; @@ -1376,14 +1364,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(inst_resolved.file); - // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is - // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends - // in `ensureComptimeUnitUpToDate`. - if (file.status != .success_zir) return error.AnalysisFail; - const zir = file.zir; - - // We are about to re-analyze this unit; drop its depenndencies. - zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); + const zir = file.zir.?; try zcu.analysis_in_progress.put(gpa, anal_unit, {}); defer _ = zcu.analysis_in_progress.swapRemove(anal_unit); @@ -1758,7 +1739,7 @@ fn createFileRootStruct( const gpa = zcu.gpa; const ip = &zcu.intern_pool; const file = zcu.fileByIndex(file_index); - const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; + const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; assert(extended.opcode == .struct_decl); const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); assert(!small.has_captures_len); @@ -1766,16 +1747,16 @@ fn createFileRootStruct( assert(small.layout == .auto); var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; const fields_len = if (small.has_fields_len) blk: { - const fields_len = file.zir.extra[extra_index]; + const fields_len = file.zir.?.extra[extra_index]; extra_index += 1; break :blk fields_len; } else 0; const decls_len = if (small.has_decls_len) blk: { - const decls_len = file.zir.extra[extra_index]; + const decls_len = file.zir.?.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; - const decls = file.zir.bodySlice(extra_index, decls_len); + const decls = file.zir.?.bodySlice(extra_index, decls_len); extra_index += decls_len; const tracked_inst = try ip.trackZir(gpa, pt.tid, .{ @@ -1833,7 +1814,6 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator. const zcu = pt.zcu; const file = zcu.fileByIndex(file_index); - assert(file.status == .success_zir); const file_root_type = zcu.fileRootType(file_index); if (file_root_type == .none) return; @@ -1844,17 +1824,17 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator. const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu); const decls = decls: { - const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; + const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; extra_index += @intFromBool(small.has_fields_len); const decls_len = if (small.has_decls_len) blk: { - const decls_len = file.zir.extra[extra_index]; + const decls_len = file.zir.?.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; - break :decls file.zir.bodySlice(extra_index, decls_len); + break :decls file.zir.?.bodySlice(extra_index, decls_len); }; try pt.scanNamespace(namespace_index, decls); zcu.namespacePtr(namespace_index).generation = zcu.generation; @@ -1865,15 +1845,11 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { defer tracy.end(); const zcu = pt.zcu; - const gpa = zcu.gpa; const file = zcu.fileByIndex(file_index); assert(file.getMode() == .zig); assert(zcu.fileRootType(file_index) == .none); - if (file.status != .success_zir) { - return error.AnalysisFail; - } - assert(file.zir_loaded); + assert(file.zir != null); const new_namespace_index = try pt.createNamespace(.{ .parent = .none, @@ -1883,39 +1859,9 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { }); const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false); errdefer zcu.intern_pool.remove(pt.tid, struct_ty); - - switch (zcu.comp.cache_use) { - .whole => |whole| if (whole.cache_manifest) |man| { - const source = file.getSource(gpa) catch |err| { - try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)}); - return error.AnalysisFail; - }; - - const resolved_path = std.fs.path.resolve(gpa, &.{ - file.mod.root.root_dir.path orelse ".", - file.mod.root.sub_path, - file.sub_file_path, - }) catch |err| { - try pt.reportRetryableFileError(file_index, "unable to resolve path: {s}", .{@errorName(err)}); - return error.AnalysisFail; - }; - errdefer gpa.free(resolved_path); - - whole.cache_manifest_mutex.lock(); - defer whole.cache_manifest_mutex.unlock(); - man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => { - try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)}); - return error.AnalysisFail; - }, - }; - }, - .incremental => {}, - } } -pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult { +pub fn importPkg(pt: Zcu.PerThread, mod: *Module) Allocator.Error!Zcu.ImportFileResult { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -1983,15 +1929,12 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult { gop.value_ptr.* = new_file_index; new_file.* = .{ .sub_file_path = sub_file_path, - .source = undefined, - .source_loaded = false, - .tree_loaded = false, - .zir_loaded = false, .stat = undefined, - .tree = undefined, - .zir = undefined, + .source = null, + .tree = null, + .zir = null, + .zoir = null, .status = .never_loaded, - .prev_status = .never_loaded, .mod = mod, }; @@ -2004,13 +1947,19 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult { }; } -/// Called from a worker thread during AstGen. +/// Called from a worker thread during AstGen (with the Compilation mutex held). /// Also called from Sema during semantic analysis. +/// Does not attempt to load the file from disk; just returns a corresponding `*Zcu.File`. pub fn importFile( pt: Zcu.PerThread, cur_file: *Zcu.File, import_string: []const u8, -) !Zcu.ImportFileResult { +) error{ + OutOfMemory, + ModuleNotFound, + ImportOutsideModulePath, + CurrentWorkingDirectoryUnlinked, +}!Zcu.ImportFileResult { const zcu = pt.zcu; const mod = cur_file.mod; @@ -2068,7 +2017,10 @@ pub fn importFile( defer gpa.free(resolved_root_path); const sub_file_path = p: { - const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path); + const relative = std.fs.path.relative(gpa, resolved_root_path, resolved_path) catch |err| switch (err) { + error.Unexpected => unreachable, + else => |e| return e, + }; errdefer gpa.free(relative); if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) { @@ -2096,15 +2048,15 @@ pub fn importFile( gop.value_ptr.* = new_file_index; new_file.* = .{ .sub_file_path = sub_file_path, - .source = undefined, - .source_loaded = false, - .tree_loaded = false, - .zir_loaded = false, - .stat = undefined, - .tree = undefined, - .zir = undefined, + .status = .never_loaded, - .prev_status = .never_loaded, + .stat = undefined, + + .source = null, + .tree = null, + .zir = null, + .zoir = null, + .mod = mod, }; @@ -2441,7 +2393,7 @@ const ScanDeclIter = struct { const namespace = zcu.namespacePtr(namespace_index); const gpa = zcu.gpa; const file = namespace.fileScope(zcu); - const zir = file.zir; + const zir = file.zir.?; const ip = &zcu.intern_pool; const decl = zir.getDeclaration(decl_inst); @@ -2591,7 +2543,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE const func = zcu.funcInfo(func_index); const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(inst_info.file); - const zir = file.zir; + const zir = file.zir.?; try zcu.analysis_in_progress.put(gpa, anal_unit, {}); errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); @@ -2843,11 +2795,32 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err /// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed. /// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry. fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void { - if (!file.zir_loaded or !file.zir.hasCompileErrors()) return; + const maybe_has_error = switch (file.status) { + .never_loaded => false, + .retryable_failure => true, + .astgen_failure => true, + .success => switch (file.getMode()) { + .zig => has_error: { + const zir = file.zir orelse break :has_error false; + break :has_error zir.hasCompileErrors(); + }, + .zon => has_error: { + const zoir = file.zoir orelse break :has_error false; + break :has_error zoir.hasCompileErrors(); + }, + }, + }; + + // If runtime safety is on, let's quickly lock the mutex and check anyway. + if (!maybe_has_error and !std.debug.runtime_safety) { + return; + } + pt.zcu.comp.mutex.lock(); defer pt.zcu.comp.mutex.unlock(); if (pt.zcu.failed_files.fetchSwapRemove(file)) |kv| { - if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // Delete previous error message. + assert(maybe_has_error); // the runtime safety case above + if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // delete previous error message } } @@ -3203,6 +3176,7 @@ pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Inde } } +/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`. pub fn reportRetryableAstGenError( pt: Zcu.PerThread, src: Zcu.AstGenSrc, @@ -3238,13 +3212,18 @@ pub fn reportRetryableAstGenError( }); errdefer err_msg.destroy(gpa); - { - zcu.comp.mutex.lock(); - defer zcu.comp.mutex.unlock(); - try zcu.failed_files.putNoClobber(gpa, file, err_msg); + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + const gop = try zcu.failed_files.getOrPut(gpa, file); + if (gop.found_existing) { + if (gop.value_ptr.*) |old_err_msg| { + old_err_msg.destroy(gpa); + } } + gop.value_ptr.* = err_msg; } +/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`. pub fn reportRetryableFileError( pt: Zcu.PerThread, file_index: Zcu.File.Index, @@ -3778,8 +3757,7 @@ fn recreateStructType( const inst_info = key.zir_index.resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - assert(file.status == .success_zir); // otherwise inst tracking failed - const zir = file.zir; + const zir = file.zir.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; @@ -3851,8 +3829,7 @@ fn recreateUnionType( const inst_info = key.zir_index.resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - assert(file.status == .success_zir); // otherwise inst tracking failed - const zir = file.zir; + const zir = file.zir.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; @@ -3938,8 +3915,7 @@ fn recreateEnumType( const inst_info = key.zir_index.resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - assert(file.status == .success_zir); // otherwise inst tracking failed - const zir = file.zir; + const zir = file.zir.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); 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 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(inst_info.file); - if (file.status != .success_zir) return error.AnalysisFail; - const zir = file.zir; + const zir = file.zir.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; diff --git a/src/link.zig b/src/link.zig index e6ee788095..d2e841ffb6 100644 --- a/src/link.zig +++ b/src/link.zig @@ -750,8 +750,7 @@ pub const File = struct { { const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; const file = pt.zcu.fileByIndex(ti.file); - assert(file.zir_loaded); - const inst = file.zir.instructions.get(@intFromEnum(ti.inst)); + const inst = file.zir.?.instructions.get(@intFromEnum(ti.inst)); assert(inst.tag == .declaration); } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 392d9dd181..6f596b6161 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -2358,8 +2358,7 @@ fn initWipNavInner( const nav = ip.getNav(nav_index); const inst_info = nav.srcInst(ip).resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - assert(file.zir_loaded); - const decl = file.zir.getDeclaration(inst_info.inst); + const decl = file.zir.?.getDeclaration(inst_info.inst); log.debug("initWipNav({s}:{d}:{d} %{d} = {})", .{ file.sub_file_path, decl.src_line + 1, @@ -2373,7 +2372,7 @@ fn initWipNavInner( switch (nav_key) { // Ignore @extern .@"extern" => |@"extern"| if (decl.linkage != .@"extern" or - !@"extern".name.eqlSlice(file.zir.nullTerminatedString(decl.name), ip)) return null, + !@"extern".name.eqlSlice(file.zir.?.nullTerminatedString(decl.name), ip)) return null, else => {}, } @@ -2696,8 +2695,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const nav = ip.getNav(nav_index); const inst_info = nav.srcInst(ip).resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - assert(file.zir_loaded); - const decl = file.zir.getDeclaration(inst_info.inst); + const decl = file.zir.?.getDeclaration(inst_info.inst); log.debug("updateComptimeNav({s}:{d}:{d} %{d} = {})", .{ file.sub_file_path, decl.src_line + 1, @@ -4097,7 +4095,7 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP // if a newly-tracked instruction can be a type's owner `zir_index`. comptime assert(Zir.inst_tracking_version == 0); - const decl_inst = file.zir.instructions.get(@intFromEnum(inst_info.inst)); + const decl_inst = file.zir.?.instructions.get(@intFromEnum(inst_info.inst)); const name_strat: Zir.Inst.NameStrategy = switch (decl_inst.tag) { .struct_init, .struct_init_ref, .struct_init_anon => .anon, .extended => switch (decl_inst.data.extended.opcode) { @@ -4301,14 +4299,13 @@ pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedI const inst_info = zir_index.resolveFull(ip).?; assert(inst_info.inst != .main_struct_inst); const file = zcu.fileByIndex(inst_info.file); - assert(file.zir_loaded); - const decl = file.zir.getDeclaration(inst_info.inst); + const decl = file.zir.?.getDeclaration(inst_info.inst); log.debug("updateLineNumber({s}:{d}:{d} %{d} = {s})", .{ file.sub_file_path, decl.src_line + 1, decl.src_column + 1, @intFromEnum(inst_info.inst), - file.zir.nullTerminatedString(decl.name), + file.zir.?.nullTerminatedString(decl.name), }); var line_buf: [4]u8 = undefined; @@ -4661,7 +4658,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { .target_unit = StringSection.unit, .target_entry = (try dwarf.debug_line_str.addString( dwarf, - if (file.mod.builtin_file == file) file.source else "", + if (file.mod.builtin_file == file) file.source.? else "", )).toOptional(), }); header.appendNTimesAssumeCapacity(0, dwarf.sectionOffsetBytes()); diff --git a/src/main.zig b/src/main.zig index ba5ebf8efd..daf6010dcc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3636,7 +3636,7 @@ fn buildOutputType( if (show_builtin) { const builtin_mod = comp.root_mod.getBuiltinDependency(); - const source = builtin_mod.builtin_file.?.source; + const source = builtin_mod.builtin_file.?.source.?; return std.io.getStdOut().writeAll(source); } switch (listen) { @@ -6134,15 +6134,12 @@ fn cmdAstCheck( var file: Zcu.File = .{ .status = .never_loaded, - .prev_status = .never_loaded, - .source_loaded = false, - .tree_loaded = false, - .zir_loaded = false, .sub_file_path = undefined, - .source = undefined, .stat = undefined, - .tree = undefined, - .zir = undefined, + .source = null, + .tree = null, + .zir = null, + .zoir = null, .mod = undefined, }; if (zig_source_file) |file_name| { @@ -6163,7 +6160,6 @@ fn cmdAstCheck( file.sub_file_path = file_name; file.source = source; - file.source_loaded = true; file.stat = .{ .size = stat.size, .inode = stat.inode, @@ -6176,7 +6172,6 @@ fn cmdAstCheck( }; file.sub_file_path = "<stdin>"; file.source = source; - file.source_loaded = true; file.stat.size = source.len; } @@ -6196,17 +6191,15 @@ fn cmdAstCheck( .fully_qualified_name = "root", }); - file.tree = try Ast.parse(gpa, file.source, mode); - file.tree_loaded = true; - defer file.tree.deinit(gpa); + file.tree = try Ast.parse(gpa, file.source.?, mode); + defer file.tree.?.deinit(gpa); switch (mode) { .zig => { - file.zir = try AstGen.generate(gpa, file.tree); - file.zir_loaded = true; - defer file.zir.deinit(gpa); + file.zir = try AstGen.generate(gpa, file.tree.?); + defer file.zir.?.deinit(gpa); - if (file.zir.hasCompileErrors()) { + if (file.zir.?.hasCompileErrors()) { var wip_errors: std.zig.ErrorBundle.Wip = undefined; try wip_errors.init(gpa); defer wip_errors.deinit(); @@ -6215,13 +6208,13 @@ fn cmdAstCheck( defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(color.renderOptions()); - if (file.zir.loweringFailed()) { + if (file.zir.?.loweringFailed()) { process.exit(1); } } if (!want_output_text) { - if (file.zir.hasCompileErrors()) { + if (file.zir.?.hasCompileErrors()) { process.exit(1); } else { return cleanExit(); @@ -6233,18 +6226,18 @@ fn cmdAstCheck( { const token_bytes = @sizeOf(Ast.TokenList) + - file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset)); - const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len * + file.tree.?.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset)); + const tree_bytes = @sizeOf(Ast) + file.tree.?.nodes.len * (@sizeOf(Ast.Node.Tag) + @sizeOf(Ast.Node.Data) + @sizeOf(Ast.TokenIndex)); - const instruction_bytes = file.zir.instructions.len * + const instruction_bytes = file.zir.?.instructions.len * // Here we don't use @sizeOf(Zir.Inst.Data) because it would include // the debug safety tag but we want to measure release size. (@sizeOf(Zir.Inst.Tag) + 8); - const extra_bytes = file.zir.extra.len * @sizeOf(u32); + const extra_bytes = file.zir.?.extra.len * @sizeOf(u32); const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes + - file.zir.string_bytes.len * @sizeOf(u8); + file.zir.?.string_bytes.len * @sizeOf(u8); const stdout = io.getStdOut(); const fmtIntSizeBin = std.fmt.fmtIntSizeBin; // zig fmt: off @@ -6258,27 +6251,27 @@ fn cmdAstCheck( \\# Extra Data Items: {d} ({}) \\ , .{ - fmtIntSizeBin(file.source.len), - file.tree.tokens.len, fmtIntSizeBin(token_bytes), - file.tree.nodes.len, fmtIntSizeBin(tree_bytes), + fmtIntSizeBin(file.source.?.len), + file.tree.?.tokens.len, fmtIntSizeBin(token_bytes), + file.tree.?.nodes.len, fmtIntSizeBin(tree_bytes), fmtIntSizeBin(total_bytes), - file.zir.instructions.len, fmtIntSizeBin(instruction_bytes), - fmtIntSizeBin(file.zir.string_bytes.len), - file.zir.extra.len, fmtIntSizeBin(extra_bytes), + file.zir.?.instructions.len, fmtIntSizeBin(instruction_bytes), + fmtIntSizeBin(file.zir.?.string_bytes.len), + file.zir.?.extra.len, fmtIntSizeBin(extra_bytes), }); // zig fmt: on } try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut()); - if (file.zir.hasCompileErrors()) { + if (file.zir.?.hasCompileErrors()) { process.exit(1); } else { return cleanExit(); } }, .zon => { - const zoir = try ZonGen.generate(gpa, file.tree, .{}); + const zoir = try ZonGen.generate(gpa, file.tree.?, .{}); defer zoir.deinit(gpa); if (zoir.hasCompileErrors()) { @@ -6289,7 +6282,7 @@ fn cmdAstCheck( { const src_path = try file.fullPath(gpa); defer gpa.free(src_path); - try wip_errors.addZoirErrorMessages(zoir, file.tree, file.source, src_path); + try wip_errors.addZoirErrorMessages(zoir, file.tree.?, file.source.?, src_path); } var error_bundle = try wip_errors.toOwnedBundle(""); @@ -6518,27 +6511,24 @@ fn cmdDumpZir( var file: Zcu.File = .{ .status = .never_loaded, - .prev_status = .never_loaded, - .source_loaded = false, - .tree_loaded = false, - .zir_loaded = true, .sub_file_path = undefined, - .source = undefined, .stat = undefined, - .tree = undefined, + .source = null, + .tree = null, .zir = try Zcu.loadZirCache(gpa, f), + .zoir = null, .mod = undefined, }; - defer file.zir.deinit(gpa); + defer file.zir.?.deinit(gpa); { - const instruction_bytes = file.zir.instructions.len * + const instruction_bytes = file.zir.?.instructions.len * // Here we don't use @sizeOf(Zir.Inst.Data) because it would include // the debug safety tag but we want to measure release size. (@sizeOf(Zir.Inst.Tag) + 8); - const extra_bytes = file.zir.extra.len * @sizeOf(u32); + const extra_bytes = file.zir.?.extra.len * @sizeOf(u32); const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes + - file.zir.string_bytes.len * @sizeOf(u8); + file.zir.?.string_bytes.len * @sizeOf(u8); const stdout = io.getStdOut(); const fmtIntSizeBin = std.fmt.fmtIntSizeBin; // zig fmt: off @@ -6550,9 +6540,9 @@ fn cmdDumpZir( \\ , .{ fmtIntSizeBin(total_bytes), - file.zir.instructions.len, fmtIntSizeBin(instruction_bytes), - fmtIntSizeBin(file.zir.string_bytes.len), - file.zir.extra.len, fmtIntSizeBin(extra_bytes), + file.zir.?.instructions.len, fmtIntSizeBin(instruction_bytes), + fmtIntSizeBin(file.zir.?.string_bytes.len), + file.zir.?.extra.len, fmtIntSizeBin(extra_bytes), }); // zig fmt: on } @@ -6586,19 +6576,16 @@ fn cmdChangelist( var file: Zcu.File = .{ .status = .never_loaded, - .prev_status = .never_loaded, - .source_loaded = false, - .tree_loaded = false, - .zir_loaded = false, .sub_file_path = old_source_file, - .source = undefined, .stat = .{ .size = stat.size, .inode = stat.inode, .mtime = stat.mtime, }, - .tree = undefined, - .zir = undefined, + .source = null, + .tree = null, + .zir = null, + .zoir = null, .mod = undefined, }; @@ -6613,17 +6600,14 @@ fn cmdChangelist( if (amt != stat.size) return error.UnexpectedEndOfFile; file.source = source; - file.source_loaded = true; - file.tree = try Ast.parse(gpa, file.source, .zig); - file.tree_loaded = true; - defer file.tree.deinit(gpa); + file.tree = try Ast.parse(gpa, file.source.?, .zig); + defer file.tree.?.deinit(gpa); - file.zir = try AstGen.generate(gpa, file.tree); - file.zir_loaded = true; - defer file.zir.deinit(gpa); + file.zir = try AstGen.generate(gpa, file.tree.?); + defer file.zir.?.deinit(gpa); - if (file.zir.loweringFailed()) { + if (file.zir.?.loweringFailed()) { var wip_errors: std.zig.ErrorBundle.Wip = undefined; try wip_errors.init(gpa); defer wip_errors.deinit(); @@ -6652,13 +6636,12 @@ fn cmdChangelist( var new_tree = try Ast.parse(gpa, new_source, .zig); defer new_tree.deinit(gpa); - var old_zir = file.zir; + var old_zir = file.zir.?; defer old_zir.deinit(gpa); - file.zir_loaded = false; + file.zir = null; file.zir = try AstGen.generate(gpa, new_tree); - file.zir_loaded = true; - if (file.zir.loweringFailed()) { + if (file.zir.?.loweringFailed()) { var wip_errors: std.zig.ErrorBundle.Wip = undefined; try wip_errors.init(gpa); defer wip_errors.deinit(); @@ -6672,7 +6655,7 @@ fn cmdChangelist( var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty; defer inst_map.deinit(gpa); - try Zcu.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map); + try Zcu.mapOldZirToNew(gpa, old_zir, file.zir.?, &inst_map); var bw = io.bufferedWriter(io.getStdOut().writer()); const stdout = bw.writer(); diff --git a/src/print_zir.zig b/src/print_zir.zig index 033ea82de9..0757ca83de 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -22,7 +22,7 @@ pub fn renderAsTextToFile( .gpa = gpa, .arena = arena.allocator(), .file = scope_file, - .code = scope_file.zir, + .code = scope_file.zir.?, .indent = 0, .parent_decl_node = 0, .recurse_decls = true, @@ -36,18 +36,18 @@ pub fn renderAsTextToFile( try stream.print("%{d} ", .{@intFromEnum(main_struct_inst)}); try writer.writeInstToStream(stream, main_struct_inst); try stream.writeAll("\n"); - const imports_index = scope_file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)]; + const imports_index = scope_file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)]; if (imports_index != 0) { try stream.writeAll("Imports:\n"); - const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index); + const extra = scope_file.zir.?.extraData(Zir.Inst.Imports, imports_index); var extra_index = extra.end; for (0..extra.data.imports_len) |_| { - const item = scope_file.zir.extraData(Zir.Inst.Imports.Item, extra_index); + const item = scope_file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index); extra_index = item.end; - const import_path = scope_file.zir.nullTerminatedString(item.data.name); + const import_path = scope_file.zir.?.nullTerminatedString(item.data.name); try stream.print(" @import(\"{}\") ", .{ std.zig.fmtEscapes(import_path), }); @@ -75,7 +75,7 @@ pub fn renderInstructionContext( .gpa = gpa, .arena = arena.allocator(), .file = scope_file, - .code = scope_file.zir, + .code = scope_file.zir.?, .indent = if (indent < 2) 2 else indent, .parent_decl_node = parent_decl_node, .recurse_decls = false, @@ -107,7 +107,7 @@ pub fn renderSingleInstruction( .gpa = gpa, .arena = arena.allocator(), .file = scope_file, - .code = scope_file.zir, + .code = scope_file.zir.?, .indent = indent, .parent_decl_node = parent_decl_node, .recurse_decls = false, @@ -2759,8 +2759,7 @@ const Writer = struct { } fn writeSrcNode(self: *Writer, stream: anytype, src_node: i32) !void { - if (!self.file.tree_loaded) return; - const tree = self.file.tree; + const tree = self.file.tree orelse return; const abs_node = self.relativeToNodeIndex(src_node); const src_span = tree.nodeToSpan(abs_node); const start = self.line_col_cursor.find(tree.source, src_span.start); @@ -2772,8 +2771,7 @@ const Writer = struct { } fn writeSrcTok(self: *Writer, stream: anytype, src_tok: u32) !void { - if (!self.file.tree_loaded) return; - const tree = self.file.tree; + const tree = self.file.tree orelse return; const abs_tok = tree.firstToken(self.parent_decl_node) + src_tok; const span_start = tree.tokens.items(.start)[abs_tok]; const span_end = span_start + @as(u32, @intCast(tree.tokenSlice(abs_tok).len)); @@ -2786,8 +2784,7 @@ const Writer = struct { } fn writeSrcTokAbs(self: *Writer, stream: anytype, src_tok: u32) !void { - if (!self.file.tree_loaded) return; - const tree = self.file.tree; + const tree = self.file.tree orelse return; const span_start = tree.tokens.items(.start)[src_tok]; const span_end = span_start + @as(u32, @intCast(tree.tokenSlice(src_tok).len)); const start = self.line_col_cursor.find(tree.source, span_start); |
