authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-04 11:55:54+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-04 16:20:29+00:00
loga8e53801d0bfe2132831b8286c4a237788aea8fa
tree7e56e97b81d3bd11dc9c7dd4cb9bcf23acae905b
parent5e20a47469f5d6feac7fb0785b2437e417b068ef
signaturelock-open Commit is signed but in an unrecognized format.

compiler: don't perform semantic analysis if there are files without ZIR


6 files changed, 122 insertions(+), 163 deletions(-)

src/Builtin.zig+1-1
...@@ -299,7 +299,7 @@ pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void {...@@ -299,7 +299,7 @@ pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void {
299299
300 file.zir = try AstGen.generate(comp.gpa, file.tree.?);300 file.zir = try AstGen.generate(comp.gpa, file.tree.?);
301 assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors301 assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors
302 file.status = .success_zir;302 file.status = .success;
303 // Note that whilst we set `zir` here, we populated `path_digest`303 // Note that whilst we set `zir` here, we populated `path_digest`
304 // all the way back in `Package.Module.create`.304 // all the way back in `Package.Module.create`.
305}305}
src/Compilation.zig+31-25
...@@ -3203,8 +3203,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3203,8 +3203,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3203 }3203 }
32043204
3205 if (comp.zcu) |zcu| {3205 if (comp.zcu) |zcu| {
3206 const ip = &zcu.intern_pool;
3207
3208 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {3206 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
3209 if (error_msg) |msg| {3207 if (error_msg) |msg| {
3210 try addModuleErrorMsg(zcu, &bundle, msg.*);3208 try addModuleErrorMsg(zcu, &bundle, msg.*);
...@@ -3277,20 +3275,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3277,20 +3275,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3277 if (!refs.contains(anal_unit)) continue;3275 if (!refs.contains(anal_unit)) continue;
3278 }3276 }
32793277
3280 report_ok: {
3281 const file_index = switch (anal_unit.unwrap()) {
3282 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
3283 .nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
3284 .type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
3285 .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
3286 .memoized_state => break :report_ok, // always report std.builtin errors
3287 };
3288
3289 // Skip errors for AnalUnits within files that had a parse failure.
3290 // We'll try again once parsing succeeds.
3291 if (!zcu.fileByIndex(file_index).okToReportErrors()) continue;
3292 }
3293
3294 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{3278 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{
3295 error_msg.msg,3279 error_msg.msg,
3296 zcu.fmtAnalUnit(anal_unit),3280 zcu.fmtAnalUnit(anal_unit),
...@@ -3318,12 +3302,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3318,12 +3302,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3318 }3302 }
3319 }3303 }
3320 }3304 }
3321 for (zcu.failed_codegen.keys(), zcu.failed_codegen.values()) |nav, error_msg| {3305 for (zcu.failed_codegen.values()) |error_msg| {
3322 if (!zcu.navFileScope(nav).okToReportErrors()) continue;
3323 try addModuleErrorMsg(zcu, &bundle, error_msg.*);3306 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
3324 }3307 }
3325 for (zcu.failed_types.keys(), zcu.failed_types.values()) |ty_index, error_msg| {3308 for (zcu.failed_types.values()) |error_msg| {
3326 if (!zcu.typeFileScope(ty_index).okToReportErrors()) continue;
3327 try addModuleErrorMsg(zcu, &bundle, error_msg.*);3309 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
3328 }3310 }
3329 for (zcu.failed_exports.values()) |value| {3311 for (zcu.failed_exports.values()) |value| {
...@@ -3827,12 +3809,35 @@ fn performAllTheWorkInner(...@@ -3827,12 +3809,35 @@ fn performAllTheWorkInner(
3827 if (comp.zcu) |zcu| {3809 if (comp.zcu) |zcu| {
3828 const pt: Zcu.PerThread = .activate(zcu, .main);3810 const pt: Zcu.PerThread = .activate(zcu, .main);
3829 defer pt.deactivate();3811 defer pt.deactivate();
3812
3813 try reportMultiModuleErrors(pt);
3814
3815 const any_fatal_files = for (zcu.import_table.values()) |file_index| {
3816 const file = zcu.fileByIndex(file_index);
3817 if (file.getMode() == .zon) continue;
3818 switch (file.status) {
3819 .never_loaded => unreachable, // everything is loaded by the workers
3820 .retryable_failure, .astgen_failure => break true,
3821 .success => {},
3822 }
3823 } else false;
3824
3825 if (any_fatal_files) {
3826 // We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents
3827 // us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
3828 // However, this means our analysis data is invalid, so we want to omit all analysis errors.
3829 // To do that, let's just clear the analysis roots!
3830
3831 assert(zcu.failed_files.count() > 0); // we will get an error
3832 zcu.analysis_roots.clear(); // no analysis happened
3833 return;
3834 }
3835
3830 if (comp.incremental) {3836 if (comp.incremental) {
3831 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);3837 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
3832 defer update_zir_refs_node.end();3838 defer update_zir_refs_node.end();
3833 try pt.updateZirRefs();3839 try pt.updateZirRefs();
3834 }3840 }
3835 try reportMultiModuleErrors(pt);
3836 try zcu.flushRetryableFailures();3841 try zcu.flushRetryableFailures();
38373842
3838 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);3843 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
...@@ -4294,11 +4299,12 @@ fn workerAstGenFile(...@@ -4294,11 +4299,12 @@ fn workerAstGenFile(
4294 pt.astGenFile(file, path_digest) catch |err| switch (err) {4299 pt.astGenFile(file, path_digest) catch |err| switch (err) {
4295 error.AnalysisFail => return,4300 error.AnalysisFail => return,
4296 else => {4301 else => {
4297 file.status = .retryable_failure;
4298 pt.reportRetryableAstGenError(src, file_index, err) catch |oom| switch (oom) {4302 pt.reportRetryableAstGenError(src, file_index, err) catch |oom| switch (oom) {
4299 // Swallowing this error is OK because it's implied to be OOM when4303 error.OutOfMemory => {
4300 // there is a missing `failed_files` error message.4304 comp.mutex.lock();
4301 error.OutOfMemory => {},4305 defer comp.mutex.unlock();
4306 comp.setAllocFailure();
4307 },
4302 };4308 };
4303 return;4309 return;
4304 },4310 },
src/Package/Module.zig-1
...@@ -488,7 +488,6 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -488,7 +488,6 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
488 .zir = null,488 .zir = null,
489 .zoir = null,489 .zoir = null,
490 .status = .never_loaded,490 .status = .never_loaded,
491 .prev_status = .never_loaded,
492 .mod = new,491 .mod = new,
493 };492 };
494 break :b new;493 break :b new;
src/Zcu.zig+33-44
...@@ -658,11 +658,27 @@ pub const Namespace = struct {...@@ -658,11 +658,27 @@ pub const Namespace = struct {
658};658};
659659
660pub const File = struct {660pub const File = struct {
661 status: Status,
662 prev_status: Status,
663 /// Relative to the owning package's root source directory.661 /// Relative to the owning package's root source directory.
664 /// Memory is stored in gpa, owned by File.662 /// Memory is stored in gpa, owned by File.
665 sub_file_path: []const u8,663 sub_file_path: []const u8,
664
665 status: enum {
666 /// We have not yet attempted to load this file.
667 /// `stat` is not populated and may be `undefined`.
668 never_loaded,
669 /// A filesystem access failed. It should be retried on the next update.
670 /// There is a `failed_files` entry containing a non-`null` message.
671 /// `stat` is not populated and may be `undefined`.
672 retryable_failure,
673 /// Parsing/AstGen/ZonGen of this file has failed.
674 /// There is an error in `zir` or `zoir`.
675 /// There is a `failed_files` entry (with a `null` message).
676 /// `stat` is populated.
677 astgen_failure,
678 /// Parsing and AstGen/ZonGen of this file has succeeded.
679 /// `stat` is populated.
680 success,
681 },
666 /// Whether this is populated depends on `status`.682 /// Whether this is populated depends on `status`.
667 stat: Cache.File.Stat,683 stat: Cache.File.Stat,
668684
...@@ -678,19 +694,17 @@ pub const File = struct {...@@ -678,19 +694,17 @@ pub const File = struct {
678 /// List of references to this file, used for multi-package errors.694 /// List of references to this file, used for multi-package errors.
679 references: std.ArrayListUnmanaged(File.Reference) = .empty,695 references: std.ArrayListUnmanaged(File.Reference) = .empty,
680696
681 /// The most recent successful ZIR for this file, with no errors.697 /// The ZIR for this file from the last update with no file failures. As such, this ZIR is never
682 /// This is only populated when a previously successful ZIR698 /// failed (although it may have compile errors).
683 /// newly introduces compile errors during an update. When ZIR is699 ///
684 /// successful, this field is unloaded.700 /// Because updates with file failures do not perform ZIR mapping or semantic analysis, we keep
701 /// this around so we have the "old" ZIR to map when an update is ready to do so. Once such an
702 /// update occurs, this field is unloaded, since it is no longer necessary.
703 ///
704 /// In other words, if `TrackedInst`s are tied to ZIR other than what's in the `zir` field, this
705 /// field is populated with that old ZIR.
685 prev_zir: ?*Zir = null,706 prev_zir: ?*Zir = null,
686707
687 pub const Status = enum {
688 never_loaded,
689 retryable_failure,
690 astgen_failure,
691 success_zir,
692 };
693
694 /// A single reference to a file.708 /// A single reference to a file.
695 pub const Reference = union(enum) {709 pub const Reference = union(enum) {
696 /// The file is imported directly (i.e. not as a package) with @import.710 /// The file is imported directly (i.e. not as a package) with @import.
...@@ -763,7 +777,7 @@ pub const File = struct {...@@ -763,7 +777,7 @@ pub const File = struct {
763 return error.FileTooBig;777 return error.FileTooBig;
764778
765 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);779 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
766 defer gpa.free(source);780 errdefer gpa.free(source);
767781
768 const amt = try f.readAll(source);782 const amt = try f.readAll(source);
769 if (amt != stat.size)783 if (amt != stat.size)
...@@ -773,8 +787,9 @@ pub const File = struct {...@@ -773,8 +787,9 @@ pub const File = struct {
773 // used for error reporting. We need to keep the stat fields stale so that787 // used for error reporting. We need to keep the stat fields stale so that
774 // astGenFile can know to regenerate ZIR.788 // astGenFile can know to regenerate ZIR.
775789
776 errdefer comptime unreachable; // don't error after populating `source`
777 file.source = source;790 file.source = source;
791 errdefer comptime unreachable; // don't error after populating `source`
792
778 return .{793 return .{
779 .bytes = source,794 .bytes = source,
780 .stat = .{795 .stat = .{
...@@ -849,13 +864,6 @@ pub const File = struct {...@@ -849,13 +864,6 @@ pub const File = struct {
849 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });864 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
850 }865 }
851866
852 pub fn okToReportErrors(file: File) bool {
853 return switch (file.status) {
854 .astgen_failure => false,
855 else => true,
856 };
857 }
858
859 /// Add a reference to this file during AstGen.867 /// Add a reference to this file during AstGen.
860 pub fn addReference(file: *File, zcu: *Zcu, ref: File.Reference) !void {868 pub fn addReference(file: *File, zcu: *Zcu, ref: File.Reference) !void {
861 // Don't add the same module root twice. Note that since we always add module roots at the869 // 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 {...@@ -3295,19 +3303,6 @@ pub fn optimizeMode(zcu: *const Zcu) std.builtin.OptimizeMode {
3295 return zcu.root_mod.optimize_mode;3303 return zcu.root_mod.optimize_mode;
3296}3304}
32973305
3298fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void {
3299 switch (file.status) {
3300 .success_zir, .retryable_failure => {},
3301 .never_loaded, .astgen_failure => {
3302 zcu.comp.mutex.lock();
3303 defer zcu.comp.mutex.unlock();
3304 if (zcu.failed_files.fetchSwapRemove(file)) |kv| {
3305 if (kv.value) |msg| msg.destroy(zcu.gpa); // Delete previous error message.
3306 }
3307 },
3308 }
3309}
3310
3311pub fn handleUpdateExports(3306pub fn handleUpdateExports(
3312 zcu: *Zcu,3307 zcu: *Zcu,
3313 export_indices: []const Export.Index,3308 export_indices: []const Export.Index,
...@@ -3662,9 +3657,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv...@@ -3662,9 +3657,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
3662 // `test` declarations are analyzed depending on the test filter.3657 // `test` declarations are analyzed depending on the test filter.
3663 const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;3658 const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;
3664 const file = zcu.fileByIndex(inst_info.file);3659 const file = zcu.fileByIndex(inst_info.file);
3665 // If the file failed AstGen, the TrackedInst refers to the old ZIR.3660 const decl = file.zir.?.getDeclaration(inst_info.inst);
3666 const zir = if (file.status == .success_zir) file.zir.? else file.prev_zir.?.*;
3667 const decl = zir.getDeclaration(inst_info.inst);
36683661
3669 if (!comp.config.is_test or file.mod != zcu.main_mod) continue;3662 if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
36703663
...@@ -3694,9 +3687,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv...@@ -3694,9 +3687,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
3694 // These are named declarations. They are analyzed only if marked `export`.3687 // These are named declarations. They are analyzed only if marked `export`.
3695 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;3688 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
3696 const file = zcu.fileByIndex(inst_info.file);3689 const file = zcu.fileByIndex(inst_info.file);
3697 // If the file failed AstGen, the TrackedInst refers to the old ZIR.3690 const decl = file.zir.?.getDeclaration(inst_info.inst);
3698 const zir = if (file.status == .success_zir) file.zir.? else file.prev_zir.?.*;
3699 const decl = zir.getDeclaration(inst_info.inst);
3700 if (decl.linkage == .@"export") {3691 if (decl.linkage == .@"export") {
3701 const unit: AnalUnit = .wrap(.{ .nav_val = nav });3692 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
3702 if (!result.contains(unit)) {3693 if (!result.contains(unit)) {
...@@ -3712,9 +3703,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv...@@ -3712,9 +3703,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
3712 // These are named declarations. They are analyzed only if marked `export`.3703 // These are named declarations. They are analyzed only if marked `export`.
3713 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;3704 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
3714 const file = zcu.fileByIndex(inst_info.file);3705 const file = zcu.fileByIndex(inst_info.file);
3715 // If the file failed AstGen, the TrackedInst refers to the old ZIR.3706 const decl = file.zir.?.getDeclaration(inst_info.inst);
3716 const zir = if (file.status == .success_zir) file.zir.? else file.prev_zir.?.*;
3717 const decl = zir.getDeclaration(inst_info.inst);
3718 if (decl.linkage == .@"export") {3707 if (decl.linkage == .@"export") {
3719 const unit: AnalUnit = .wrap(.{ .nav_val = nav });3708 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
3720 if (!result.contains(unit)) {3709 if (!result.contains(unit)) {
src/Zcu/PerThread.zig+57-89
...@@ -109,7 +109,7 @@ pub fn astGenFile(...@@ -109,7 +109,7 @@ pub fn astGenFile(
109109
110 break :lock .shared;110 break :lock .shared;
111 },111 },
112 .astgen_failure, .success_zir => lock: {112 .astgen_failure, .success => lock: {
113 const unchanged_metadata =113 const unchanged_metadata =
114 stat.size == file.stat.size and114 stat.size == file.stat.size and
115 stat.mtime == file.stat.mtime and115 stat.mtime == file.stat.mtime and
...@@ -214,8 +214,7 @@ pub fn astGenFile(...@@ -214,8 +214,7 @@ pub fn astGenFile(
214 .inode = header.stat_inode,214 .inode = header.stat_inode,
215 .mtime = header.stat_mtime,215 .mtime = header.stat_mtime,
216 };216 };
217 file.prev_status = file.status;217 file.status = .success;
218 file.status = .success_zir;
219 log.debug("AstGen cached success: {s}", .{file.sub_file_path});218 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
220219
221 if (file.zir.?.hasCompileErrors()) {220 if (file.zir.?.hasCompileErrors()) {
...@@ -248,19 +247,11 @@ pub fn astGenFile(...@@ -248,19 +247,11 @@ pub fn astGenFile(
248247
249 pt.lockAndClearFileCompileError(file);248 pt.lockAndClearFileCompileError(file);
250249
251 // Previous ZIR is kept for two reasons:250 // If `zir` is not null, and `prev_zir` is null, then `TrackedInst`s are associated with `zir`.
252 //251 // We need to keep it around!
253 // 1. In case an update to the file causes a Parse or AstGen failure, we252 // As an optimization, also check `loweringFailed`; if true, but `prev_zir == null`, then this
254 // need to compare two successful ZIR files in order to proceed with an253 // file has never passed AstGen, so we actually need not cache the old ZIR.
255 // incremental update. This avoids needlessly tossing out semantic254 if (file.zir != null and file.prev_zir == null and !file.zir.?.loweringFailed()) {
256 // analysis work when an error is temporarily introduced.
257 //
258 // 2. In order to detect updates, we need to iterate over the intern pool
259 // values while comparing old ZIR to new ZIR. This is better done in a
260 // single-threaded context, so we need to keep both versions around
261 // until that point in the pipeline. Previous ZIR data is freed after
262 // that.
263 if (file.zir != null and !file.zir.?.loweringFailed()) {
264 assert(file.prev_zir == null);255 assert(file.prev_zir == null);
265 const prev_zir_ptr = try gpa.create(Zir);256 const prev_zir_ptr = try gpa.create(Zir);
266 file.prev_zir = prev_zir_ptr;257 file.prev_zir = prev_zir_ptr;
...@@ -289,8 +280,7 @@ pub fn astGenFile(...@@ -289,8 +280,7 @@ pub fn astGenFile(
289280
290 // Any potential AST errors are converted to ZIR errors here.281 // Any potential AST errors are converted to ZIR errors here.
291 file.zir = try AstGen.generate(gpa, file.tree.?);282 file.zir = try AstGen.generate(gpa, file.tree.?);
292 file.prev_status = file.status;283 file.status = .success;
293 file.status = .success_zir;
294 log.debug("AstGen fresh success: {s}", .{file.sub_file_path});284 log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
295285
296 const safety_buffer = if (Zcu.data_has_safety_tag)286 const safety_buffer = if (Zcu.data_has_safety_tag)
...@@ -383,9 +373,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -383,9 +373,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
383 defer cleanupUpdatedFiles(gpa, &updated_files);373 defer cleanupUpdatedFiles(gpa, &updated_files);
384 for (zcu.import_table.values()) |file_index| {374 for (zcu.import_table.values()) |file_index| {
385 const file = zcu.fileByIndex(file_index);375 const file = zcu.fileByIndex(file_index);
386 if (file.prev_status != file.status and file.prev_status != .never_loaded) {376 assert(file.status == .success);
387 try zcu.markDependeeOutdated(.not_marked_po, .{ .file = file_index });
388 }
389 const old_zir = file.prev_zir orelse continue;377 const old_zir = file.prev_zir orelse continue;
390 const new_zir = file.zir.?;378 const new_zir = file.zir.?;
391 const gop = try updated_files.getOrPut(gpa, file_index);379 const gop = try updated_files.getOrPut(gpa, file_index);
...@@ -394,9 +382,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -394,9 +382,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
394 .file = file,382 .file = file,
395 .inst_map = .{},383 .inst_map = .{},
396 };384 };
397 if (!new_zir.loweringFailed()) {385 try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map);
398 try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map);
399 }
400 }386 }
401387
402 if (updated_files.count() == 0)388 if (updated_files.count() == 0)
...@@ -416,13 +402,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -416,13 +402,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
416 .index = @intCast(tracked_inst_unwrapped_index),402 .index = @intCast(tracked_inst_unwrapped_index),
417 }).wrap(ip);403 }).wrap(ip);
418 const new_inst = updated_file.inst_map.get(old_inst) orelse {404 const new_inst = updated_file.inst_map.get(old_inst) orelse {
419 // Tracking failed for this instruction.405 // Tracking failed for this instruction due to changes in the ZIR.
420 // This may be due to changes in the ZIR, or AstGen might have failed due to a very broken file.406 // Invalidate associated `src_hash` deps.
421 // Either way, invalidate associated `src_hash` deps.407 log.debug("tracking failed for %{d}", .{old_inst});
422 log.debug("tracking failed for %{d}{s}", .{
423 old_inst,
424 if (file.zir.?.loweringFailed()) " due to AstGen failure" else "",
425 });
426 tracked_inst.inst = .lost;408 tracked_inst.inst = .lost;
427 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });409 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
428 continue;410 continue;
...@@ -527,23 +509,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -527,23 +509,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
527509
528 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {510 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
529 const file = updated_file.file;511 const file = updated_file.file;
530 if (file.zir.?.loweringFailed()) {512
531 // Keep `prev_zir` around: it's the last usable ZIR.513 const prev_zir = file.prev_zir.?;
532 // Don't update the namespace, as we have no new data to update *to*.514 file.prev_zir = null;
533 } else {515 prev_zir.deinit(gpa);
534 const prev_zir = file.prev_zir.?;516 gpa.destroy(prev_zir);
535 file.prev_zir = null;517
536 prev_zir.deinit(gpa);518 // For every file which has changed, re-scan the namespace of the file's root struct type.
537 gpa.destroy(prev_zir);519 // These types are special-cased because they don't have an enclosing declaration which will
538520 // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
539 // For every file which has changed, re-scan the namespace of the file's root struct type.521 // now because this work is fast (no actual Sema work is happening, we're just updating the
540 // These types are special-cased because they don't have an enclosing declaration which will522 // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
541 // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this523 // will track some instructions.
542 // now because this work is fast (no actual Sema work is happening, we're just updating the524 try pt.updateFileNamespace(file_index);
543 // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
544 // will track some instructions.
545 try pt.updateFileNamespace(file_index);
546 }
547 }525 }
548}526}
549527
...@@ -745,6 +723,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU...@@ -745,6 +723,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
745 kv.value.destroy(gpa);723 kv.value.destroy(gpa);
746 }724 }
747 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);725 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
726 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
748 }727 }
749 } else {728 } else {
750 // We can trust the current information about this unit.729 // We can trust the current information about this unit.
...@@ -796,15 +775,8 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu...@@ -796,15 +775,8 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu
796775
797 const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail;776 const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
798 const file = zcu.fileByIndex(inst_resolved.file);777 const file = zcu.fileByIndex(inst_resolved.file);
799 // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
800 // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
801 // in `ensureComptimeUnitUpToDate`.
802 if (file.status != .success_zir) return error.AnalysisFail;
803 const zir = file.zir.?;778 const zir = file.zir.?;
804779
805 // We are about to re-analyze this unit; drop its depenndencies.
806 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
807
808 try zcu.analysis_in_progress.put(gpa, anal_unit, {});780 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
809 defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));781 defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
810782
...@@ -923,6 +895,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu...@@ -923,6 +895,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
923 kv.value.destroy(gpa);895 kv.value.destroy(gpa);
924 }896 }
925 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);897 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
898 ip.removeDependenciesForDepender(gpa, anal_unit);
926 } else {899 } else {
927 // We can trust the current information about this unit.900 // We can trust the current information about this unit.
928 if (prev_failed) return error.AnalysisFail;901 if (prev_failed) return error.AnalysisFail;
...@@ -993,15 +966,8 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr...@@ -993,15 +966,8 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
993966
994 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;967 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
995 const file = zcu.fileByIndex(inst_resolved.file);968 const file = zcu.fileByIndex(inst_resolved.file);
996 // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
997 // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
998 // in `ensureComptimeUnitUpToDate`.
999 if (file.status != .success_zir) return error.AnalysisFail;
1000 const zir = file.zir.?;969 const zir = file.zir.?;
1001970
1002 // We are about to re-analyze this unit; drop its depenndencies.
1003 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
1004
1005 try zcu.analysis_in_progress.put(gpa, anal_unit, {});971 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
1006 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);972 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
1007973
...@@ -1301,6 +1267,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc...@@ -1301,6 +1267,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
1301 kv.value.destroy(gpa);1267 kv.value.destroy(gpa);
1302 }1268 }
1303 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);1269 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
1270 ip.removeDependenciesForDepender(gpa, anal_unit);
1304 } else {1271 } else {
1305 // We can trust the current information about this unit.1272 // We can trust the current information about this unit.
1306 if (prev_failed) return error.AnalysisFail;1273 if (prev_failed) return error.AnalysisFail;
...@@ -1371,15 +1338,8 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr...@@ -1371,15 +1338,8 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
13711338
1372 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;1339 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
1373 const file = zcu.fileByIndex(inst_resolved.file);1340 const file = zcu.fileByIndex(inst_resolved.file);
1374 // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
1375 // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
1376 // in `ensureComptimeUnitUpToDate`.
1377 if (file.status != .success_zir) return error.AnalysisFail;
1378 const zir = file.zir.?;1341 const zir = file.zir.?;
13791342
1380 // We are about to re-analyze this unit; drop its depenndencies.
1381 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
1382
1383 try zcu.analysis_in_progress.put(gpa, anal_unit, {});1343 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
1384 defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);1344 defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
13851345
...@@ -1828,7 +1788,6 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator....@@ -1828,7 +1788,6 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.
1828 const zcu = pt.zcu;1788 const zcu = pt.zcu;
18291789
1830 const file = zcu.fileByIndex(file_index);1790 const file = zcu.fileByIndex(file_index);
1831 assert(file.status == .success_zir);
1832 const file_root_type = zcu.fileRootType(file_index);1791 const file_root_type = zcu.fileRootType(file_index);
1833 if (file_root_type == .none) return;1792 if (file_root_type == .none) return;
18341793
...@@ -1865,9 +1824,6 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {...@@ -1865,9 +1824,6 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
1865 assert(file.getMode() == .zig);1824 assert(file.getMode() == .zig);
1866 assert(zcu.fileRootType(file_index) == .none);1825 assert(zcu.fileRootType(file_index) == .none);
18671826
1868 if (file.status != .success_zir) {
1869 return error.AnalysisFail;
1870 }
1871 assert(file.zir != null);1827 assert(file.zir != null);
18721828
1873 const new_namespace_index = try pt.createNamespace(.{1829 const new_namespace_index = try pt.createNamespace(.{
...@@ -1910,7 +1866,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {...@@ -1910,7 +1866,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
1910 }1866 }
1911}1867}
19121868
1913pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {1869pub fn importPkg(pt: Zcu.PerThread, mod: *Module) Allocator.Error!Zcu.ImportFileResult {
1914 const zcu = pt.zcu;1870 const zcu = pt.zcu;
1915 const gpa = zcu.gpa;1871 const gpa = zcu.gpa;
19161872
...@@ -1984,7 +1940,6 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {...@@ -1984,7 +1940,6 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
1984 .zir = null,1940 .zir = null,
1985 .zoir = null,1941 .zoir = null,
1986 .status = .never_loaded,1942 .status = .never_loaded,
1987 .prev_status = .never_loaded,
1988 .mod = mod,1943 .mod = mod,
1989 };1944 };
19901945
...@@ -1997,13 +1952,19 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {...@@ -1997,13 +1952,19 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
1997 };1952 };
1998}1953}
19991954
2000/// Called from a worker thread during AstGen.1955/// Called from a worker thread during AstGen (with the Compilation mutex held).
2001/// Also called from Sema during semantic analysis.1956/// Also called from Sema during semantic analysis.
1957/// Does not attempt to load the file from disk; just returns a corresponding `*Zcu.File`.
2002pub fn importFile(1958pub fn importFile(
2003 pt: Zcu.PerThread,1959 pt: Zcu.PerThread,
2004 cur_file: *Zcu.File,1960 cur_file: *Zcu.File,
2005 import_string: []const u8,1961 import_string: []const u8,
2006) !Zcu.ImportFileResult {1962) error{
1963 OutOfMemory,
1964 ModuleNotFound,
1965 ImportOutsideModulePath,
1966 CurrentWorkingDirectoryUnlinked,
1967}!Zcu.ImportFileResult {
2007 const zcu = pt.zcu;1968 const zcu = pt.zcu;
2008 const mod = cur_file.mod;1969 const mod = cur_file.mod;
20091970
...@@ -2061,7 +2022,10 @@ pub fn importFile(...@@ -2061,7 +2022,10 @@ pub fn importFile(
2061 defer gpa.free(resolved_root_path);2022 defer gpa.free(resolved_root_path);
20622023
2063 const sub_file_path = p: {2024 const sub_file_path = p: {
2064 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);2025 const relative = std.fs.path.relative(gpa, resolved_root_path, resolved_path) catch |err| switch (err) {
2026 error.Unexpected => unreachable,
2027 else => |e| return e,
2028 };
2065 errdefer gpa.free(relative);2029 errdefer gpa.free(relative);
20662030
2067 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {2031 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
...@@ -2089,13 +2053,15 @@ pub fn importFile(...@@ -2089,13 +2053,15 @@ pub fn importFile(
2089 gop.value_ptr.* = new_file_index;2053 gop.value_ptr.* = new_file_index;
2090 new_file.* = .{2054 new_file.* = .{
2091 .sub_file_path = sub_file_path,2055 .sub_file_path = sub_file_path,
2056
2057 .status = .never_loaded,
2092 .stat = undefined,2058 .stat = undefined,
2059
2093 .source = null,2060 .source = null,
2094 .tree = null,2061 .tree = null,
2095 .zir = null,2062 .zir = null,
2096 .zoir = null,2063 .zoir = null,
2097 .status = .never_loaded,2064
2098 .prev_status = .never_loaded,
2099 .mod = mod,2065 .mod = mod,
2100 };2066 };
21012067
...@@ -2835,7 +2801,7 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err...@@ -2835,7 +2801,7 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err
2835/// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry.2801/// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry.
2836fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void {2802fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void {
2837 const zir = file.zir orelse return;2803 const zir = file.zir orelse return;
2838 if (zir.hasCompileErrors()) return;2804 if (!zir.hasCompileErrors()) return;
28392805
2840 pt.zcu.comp.mutex.lock();2806 pt.zcu.comp.mutex.lock();
2841 defer pt.zcu.comp.mutex.unlock();2807 defer pt.zcu.comp.mutex.unlock();
...@@ -3196,6 +3162,7 @@ pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Inde...@@ -3196,6 +3162,7 @@ pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Inde
3196 }3162 }
3197}3163}
31983164
3165/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`.
3199pub fn reportRetryableAstGenError(3166pub fn reportRetryableAstGenError(
3200 pt: Zcu.PerThread,3167 pt: Zcu.PerThread,
3201 src: Zcu.AstGenSrc,3168 src: Zcu.AstGenSrc,
...@@ -3231,13 +3198,18 @@ pub fn reportRetryableAstGenError(...@@ -3231,13 +3198,18 @@ pub fn reportRetryableAstGenError(
3231 });3198 });
3232 errdefer err_msg.destroy(gpa);3199 errdefer err_msg.destroy(gpa);
32333200
3234 {3201 zcu.comp.mutex.lock();
3235 zcu.comp.mutex.lock();3202 defer zcu.comp.mutex.unlock();
3236 defer zcu.comp.mutex.unlock();3203 const gop = try zcu.failed_files.getOrPut(gpa, file);
3237 try zcu.failed_files.putNoClobber(gpa, file, err_msg);3204 if (gop.found_existing) {
3205 if (gop.value_ptr.*) |old_err_msg| {
3206 old_err_msg.destroy(gpa);
3207 }
3238 }3208 }
3209 gop.value_ptr.* = err_msg;
3239}3210}
32403211
3212/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`.
3241pub fn reportRetryableFileError(3213pub fn reportRetryableFileError(
3242 pt: Zcu.PerThread,3214 pt: Zcu.PerThread,
3243 file_index: Zcu.File.Index,3215 file_index: Zcu.File.Index,
...@@ -3771,7 +3743,6 @@ fn recreateStructType(...@@ -3771,7 +3743,6 @@ fn recreateStructType(
37713743
3772 const inst_info = key.zir_index.resolveFull(ip).?;3744 const inst_info = key.zir_index.resolveFull(ip).?;
3773 const file = zcu.fileByIndex(inst_info.file);3745 const file = zcu.fileByIndex(inst_info.file);
3774 assert(file.status == .success_zir); // otherwise inst tracking failed
3775 const zir = file.zir.?;3746 const zir = file.zir.?;
37763747
3777 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);3748 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
...@@ -3844,7 +3815,6 @@ fn recreateUnionType(...@@ -3844,7 +3815,6 @@ fn recreateUnionType(
38443815
3845 const inst_info = key.zir_index.resolveFull(ip).?;3816 const inst_info = key.zir_index.resolveFull(ip).?;
3846 const file = zcu.fileByIndex(inst_info.file);3817 const file = zcu.fileByIndex(inst_info.file);
3847 assert(file.status == .success_zir); // otherwise inst tracking failed
3848 const zir = file.zir.?;3818 const zir = file.zir.?;
38493819
3850 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);3820 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
...@@ -3931,7 +3901,6 @@ fn recreateEnumType(...@@ -3931,7 +3901,6 @@ fn recreateEnumType(
39313901
3932 const inst_info = key.zir_index.resolveFull(ip).?;3902 const inst_info = key.zir_index.resolveFull(ip).?;
3933 const file = zcu.fileByIndex(inst_info.file);3903 const file = zcu.fileByIndex(inst_info.file);
3934 assert(file.status == .success_zir); // otherwise inst tracking failed
3935 const zir = file.zir.?;3904 const zir = file.zir.?;
39363905
3937 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);3906 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
...@@ -4075,7 +4044,6 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace...@@ -4075,7 +4044,6 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
40754044
4076 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;4045 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
4077 const file = zcu.fileByIndex(inst_info.file);4046 const file = zcu.fileByIndex(inst_info.file);
4078 if (file.status != .success_zir) return error.AnalysisFail;
4079 const zir = file.zir.?;4047 const zir = file.zir.?;
40804048
4081 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);4049 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
src/main.zig-3
...@@ -6134,7 +6134,6 @@ fn cmdAstCheck(...@@ -6134,7 +6134,6 @@ fn cmdAstCheck(
61346134
6135 var file: Zcu.File = .{6135 var file: Zcu.File = .{
6136 .status = .never_loaded,6136 .status = .never_loaded,
6137 .prev_status = .never_loaded,
6138 .sub_file_path = undefined,6137 .sub_file_path = undefined,
6139 .stat = undefined,6138 .stat = undefined,
6140 .source = null,6139 .source = null,
...@@ -6512,7 +6511,6 @@ fn cmdDumpZir(...@@ -6512,7 +6511,6 @@ fn cmdDumpZir(
65126511
6513 var file: Zcu.File = .{6512 var file: Zcu.File = .{
6514 .status = .never_loaded,6513 .status = .never_loaded,
6515 .prev_status = .never_loaded,
6516 .sub_file_path = undefined,6514 .sub_file_path = undefined,
6517 .stat = undefined,6515 .stat = undefined,
6518 .source = null,6516 .source = null,
...@@ -6578,7 +6576,6 @@ fn cmdChangelist(...@@ -6578,7 +6576,6 @@ fn cmdChangelist(
65786576
6579 var file: Zcu.File = .{6577 var file: Zcu.File = .{
6580 .status = .never_loaded,6578 .status = .never_loaded,
6581 .prev_status = .never_loaded,
6582 .sub_file_path = old_source_file,6579 .sub_file_path = old_source_file,
6583 .stat = .{6580 .stat = .{
6584 .size = stat.size,6581 .size = stat.size,