From a8e53801d0bfe2132831b8286c4a237788aea8fa Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 4 Feb 2025 11:55:54 +0000 Subject: [PATCH] compiler: don't perform semantic analysis if there are files without ZIR --- src/Builtin.zig | 2 +- src/Compilation.zig | 56 +++++++++------- src/Package/Module.zig | 1 - src/Zcu.zig | 77 ++++++++++------------ src/Zcu/PerThread.zig | 144 ++++++++++++++++------------------------- src/main.zig | 3 - 6 files changed, 121 insertions(+), 162 deletions(-) diff --git a/src/Builtin.zig b/src/Builtin.zig index 95704bbe2f..ac23cafb3c 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -299,7 +299,7 @@ pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void { file.zir = try AstGen.generate(comp.gpa, file.tree.?); assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors - file.status = .success_zir; + file.status = .success; // Note that whilst we set `zir` here, we populated `path_digest` // all the way back in `Package.Module.create`. } diff --git a/src/Compilation.zig b/src/Compilation.zig index 400372740e..28017eeed9 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3203,8 +3203,6 @@ 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.*); @@ -3277,20 +3275,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 +3302,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| { @@ -3827,12 +3809,35 @@ fn performAllTheWorkInner( if (comp.zcu) |zcu| { const pt: Zcu.PerThread = .activate(zcu, .main); defer pt.deactivate(); + + try reportMultiModuleErrors(pt); + + const any_fatal_files = for (zcu.import_table.values()) |file_index| { + const file = zcu.fileByIndex(file_index); + if (file.getMode() == .zon) continue; + 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) { + // 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); @@ -4294,11 +4299,12 @@ fn workerAstGenFile( pt.astGenFile(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; }, diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 01a97df258..0dec7bde76 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -488,7 +488,6 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { .zir = null, .zoir = null, .status = .never_loaded, - .prev_status = .never_loaded, .mod = new, }; break :b new; diff --git a/src/Zcu.zig b/src/Zcu.zig index bb8c648366..0ce43ce78d 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -658,11 +658,27 @@ pub const Namespace = struct { }; pub const File = struct { - status: Status, - prev_status: Status, /// Relative to the owning package's root source directory. /// Memory is stored in gpa, owned by File. sub_file_path: []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, @@ -678,19 +694,17 @@ 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, - astgen_failure, - success_zir, - }; - /// A single reference to a file. pub const Reference = union(enum) { /// The file is imported directly (i.e. not as a package) with @import. @@ -763,7 +777,7 @@ pub const File = struct { return error.FileTooBig; const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0); - defer gpa.free(source); + errdefer gpa.free(source); const amt = try f.readAll(source); if (amt != stat.size) @@ -773,8 +787,9 @@ pub const File = struct { // used for error reporting. We need to keep the stat fields stale so that // astGenFile can know to regenerate ZIR. - errdefer comptime unreachable; // don't error after populating `source` file.source = source; + errdefer comptime unreachable; // don't error after populating `source` + return .{ .bytes = source, .stat = .{ @@ -849,13 +864,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) { - .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 @@ -3295,19 +3303,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, .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, @@ -3662,9 +3657,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; @@ -3694,9 +3687,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)) { @@ -3712,9 +3703,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)) { diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index c18145180d..cc323c4ac0 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -109,7 +109,7 @@ pub fn astGenFile( break :lock .shared; }, - .astgen_failure, .success_zir => lock: { + .astgen_failure, .success => lock: { const unchanged_metadata = stat.size == file.stat.size and stat.mtime == file.stat.mtime and @@ -214,8 +214,7 @@ pub fn astGenFile( .inode = header.stat_inode, .mtime = header.stat_mtime, }; - file.prev_status = file.status; - file.status = .success_zir; + file.status = .success; log.debug("AstGen cached success: {s}", .{file.sub_file_path}); if (file.zir.?.hasCompileErrors()) { @@ -248,19 +247,11 @@ pub fn astGenFile( pt.lockAndClearFileCompileError(file); - // 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 != null and !file.zir.?.loweringFailed()) { + // 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; @@ -289,8 +280,7 @@ pub fn astGenFile( // Any potential AST errors are converted to ZIR errors here. file.zir = try AstGen.generate(gpa, file.tree.?); - file.prev_status = file.status; - file.status = .success_zir; + file.status = .success; log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); const safety_buffer = if (Zcu.data_has_safety_tag) @@ -383,9 +373,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { 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); const old_zir = file.prev_zir orelse continue; const new_zir = file.zir.?; const gop = try updated_files.getOrPut(gpa, file_index); @@ -394,9 +382,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { .file = file, .inst_map = .{}, }; - if (!new_zir.loweringFailed()) { - try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map); - } + try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map); } if (updated_files.count() == 0) @@ -416,13 +402,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; @@ -527,23 +509,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); } } @@ -745,6 +723,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. @@ -796,15 +775,8 @@ 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); - try zcu.analysis_in_progress.put(gpa, anal_unit, {}); defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); @@ -923,6 +895,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; @@ -993,15 +966,8 @@ 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); - try zcu.analysis_in_progress.put(gpa, anal_unit, {}); errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); @@ -1301,6 +1267,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; @@ -1371,15 +1338,8 @@ 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); - try zcu.analysis_in_progress.put(gpa, anal_unit, {}); defer _ = zcu.analysis_in_progress.swapRemove(anal_unit); @@ -1828,7 +1788,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; @@ -1865,9 +1824,6 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { assert(file.getMode() == .zig); assert(zcu.fileRootType(file_index) == .none); - if (file.status != .success_zir) { - return error.AnalysisFail; - } assert(file.zir != null); const new_namespace_index = try pt.createNamespace(.{ @@ -1910,7 +1866,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { } } -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; @@ -1984,7 +1940,6 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult { .zir = null, .zoir = null, .status = .never_loaded, - .prev_status = .never_loaded, .mod = mod, }; @@ -1997,13 +1952,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; @@ -2061,7 +2022,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)) { @@ -2089,13 +2053,15 @@ pub fn importFile( gop.value_ptr.* = new_file_index; new_file.* = .{ .sub_file_path = sub_file_path, + + .status = .never_loaded, .stat = undefined, + .source = null, .tree = null, .zir = null, .zoir = null, - .status = .never_loaded, - .prev_status = .never_loaded, + .mod = mod, }; @@ -2835,7 +2801,7 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err /// `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 { const zir = file.zir orelse return; - if (zir.hasCompileErrors()) return; + if (!zir.hasCompileErrors()) return; pt.zcu.comp.mutex.lock(); defer pt.zcu.comp.mutex.unlock(); @@ -3196,6 +3162,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, @@ -3231,13 +3198,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, @@ -3771,7 +3743,6 @@ 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.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); @@ -3844,7 +3815,6 @@ 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.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); @@ -3931,7 +3901,6 @@ 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.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); @@ -4075,7 +4044,6 @@ 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.?; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); diff --git a/src/main.zig b/src/main.zig index 8d30bef237..daf6010dcc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6134,7 +6134,6 @@ fn cmdAstCheck( var file: Zcu.File = .{ .status = .never_loaded, - .prev_status = .never_loaded, .sub_file_path = undefined, .stat = undefined, .source = null, @@ -6512,7 +6511,6 @@ fn cmdDumpZir( var file: Zcu.File = .{ .status = .never_loaded, - .prev_status = .never_loaded, .sub_file_path = undefined, .stat = undefined, .source = null, @@ -6578,7 +6576,6 @@ fn cmdChangelist( var file: Zcu.File = .{ .status = .never_loaded, - .prev_status = .never_loaded, .sub_file_path = old_source_file, .stat = .{ .size = stat.size,