authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-10-16 20:29:55+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-10-16 20:29:55+01:00
log6201031e0581c54688fdf6071a250b80f892525b
treeb8fac5782647b26920756d6a805cd5d799f533ff
parentbdd3bc056ee998770ea48a93b4ec99521f069aed
parent3ba4f86198267fee0f6a8d50496908794e743cbd
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21722 from mlugg/incremental

incremental compilation progress

10 files changed, 181 insertions(+), 62 deletions(-)

src/Compilation.zig+29-35
......@@ -2901,6 +2901,7 @@ pub fn makeBinFileWritable(comp: *Compilation) !void {
29012901const Header = extern struct {
29022902 intern_pool: extern struct {
29032903 thread_count: u32,
2904 file_deps_len: u32,
29042905 src_hash_deps_len: u32,
29052906 nav_val_deps_len: u32,
29062907 namespace_deps_len: u32,
......@@ -2943,6 +2944,7 @@ pub fn saveState(comp: *Compilation) !void {
29432944 const header: Header = .{
29442945 .intern_pool = .{
29452946 .thread_count = @intCast(ip.locals.len),
2947 .file_deps_len = @intCast(ip.file_deps.count()),
29462948 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
29472949 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
29482950 .namespace_deps_len = @intCast(ip.namespace_deps.count()),
......@@ -2969,6 +2971,8 @@ pub fn saveState(comp: *Compilation) !void {
29692971 addBuf(&bufs, mem.asBytes(&header));
29702972 addBuf(&bufs, mem.sliceAsBytes(pt_headers.items));
29712973
2974 addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.keys()));
2975 addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.values()));
29722976 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys()));
29732977 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
29742978 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
......@@ -3076,15 +3080,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
30763080 });
30773081 }
30783082
3079 var all_references: ?std.AutoHashMapUnmanaged(InternPool.AnalUnit, ?Zcu.ResolvedReference) = null;
3080 defer if (all_references) |*a| a.deinit(gpa);
3081
30823083 if (comp.zcu) |zcu| {
30833084 const ip = &zcu.intern_pool;
30843085
30853086 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
30863087 if (error_msg) |msg| {
3087 try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references);
3088 try addModuleErrorMsg(zcu, &bundle, msg.*);
30883089 } else {
30893090 // Must be ZIR errors. Note that this may include AST errors.
30903091 // addZirErrorMessages asserts that the tree is loaded.
......@@ -3093,7 +3094,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
30933094 }
30943095 }
30953096 for (zcu.failed_embed_files.values()) |error_msg| {
3096 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
3097 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
30973098 }
30983099 {
30993100 const SortOrder = struct {
......@@ -3136,10 +3137,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
31363137 }
31373138 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {
31383139 if (comp.incremental) {
3139 if (all_references == null) {
3140 all_references = try zcu.resolveReferences();
3141 }
3142 if (!all_references.?.contains(anal_unit)) continue;
3140 const refs = try zcu.resolveReferences();
3141 if (!refs.contains(anal_unit)) continue;
31433142 }
31443143
31453144 const file_index = switch (anal_unit.unwrap()) {
......@@ -3151,7 +3150,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
31513150 // We'll try again once parsing succeeds.
31523151 if (!zcu.fileByIndex(file_index).okToReportErrors()) continue;
31533152
3154 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
3153 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
31553154 if (zcu.cimport_errors.get(anal_unit)) |errors| {
31563155 for (errors.getMessages()) |err_msg_index| {
31573156 const err_msg = errors.getErrorMessage(err_msg_index);
......@@ -3175,10 +3174,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
31753174 }
31763175 for (zcu.failed_codegen.keys(), zcu.failed_codegen.values()) |nav, error_msg| {
31773176 if (!zcu.navFileScope(nav).okToReportErrors()) continue;
3178 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
3177 try addModuleErrorMsg(zcu, &bundle, error_msg.*);
31793178 }
31803179 for (zcu.failed_exports.values()) |value| {
3181 try addModuleErrorMsg(zcu, &bundle, value.*, &all_references);
3180 try addModuleErrorMsg(zcu, &bundle, value.*);
31823181 }
31833182
31843183 const actual_error_count = zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
......@@ -3252,17 +3251,15 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32523251 };
32533252 }
32543253
3255 try addModuleErrorMsg(zcu, &bundle, err_msg, &all_references);
3254 try addModuleErrorMsg(zcu, &bundle, err_msg);
32563255 }
32573256 }
32583257
32593258 if (comp.zcu) |zcu| {
32603259 if (comp.incremental and bundle.root_list.items.len == 0) {
32613260 const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
3262 if (all_references == null) {
3263 all_references = try zcu.resolveReferences();
3264 }
3265 if (all_references.?.contains(failed_unit)) break true;
3261 const refs = try zcu.resolveReferences();
3262 if (refs.contains(failed_unit)) break true;
32663263 } else false;
32673264 if (should_have_error) {
32683265 @panic("referenced transitive analysis errors, but none actually emitted");
......@@ -3331,14 +3328,13 @@ pub const ErrorNoteHashContext = struct {
33313328};
33323329
33333330pub fn addModuleErrorMsg(
3334 mod: *Zcu,
3331 zcu: *Zcu,
33353332 eb: *ErrorBundle.Wip,
33363333 module_err_msg: Zcu.ErrorMsg,
3337 all_references: *?std.AutoHashMapUnmanaged(InternPool.AnalUnit, ?Zcu.ResolvedReference),
33383334) !void {
33393335 const gpa = eb.gpa;
3340 const ip = &mod.intern_pool;
3341 const err_src_loc = module_err_msg.src_loc.upgrade(mod);
3336 const ip = &zcu.intern_pool;
3337 const err_src_loc = module_err_msg.src_loc.upgrade(zcu);
33423338 const err_source = err_src_loc.file_scope.getSource(gpa) catch |err| {
33433339 const file_path = try err_src_loc.file_scope.fullPath(gpa);
33443340 defer gpa.free(file_path);
......@@ -3358,22 +3354,20 @@ pub fn addModuleErrorMsg(
33583354 defer ref_traces.deinit(gpa);
33593355
33603356 if (module_err_msg.reference_trace_root.unwrap()) |rt_root| {
3361 if (all_references.* == null) {
3362 all_references.* = try mod.resolveReferences();
3363 }
3357 const all_references = try zcu.resolveReferences();
33643358
33653359 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty;
33663360 defer seen.deinit(gpa);
33673361
3368 const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len;
3362 const max_references = zcu.comp.reference_trace orelse Sema.default_reference_trace_len;
33693363
33703364 var referenced_by = rt_root;
3371 while (all_references.*.?.get(referenced_by)) |maybe_ref| {
3365 while (all_references.get(referenced_by)) |maybe_ref| {
33723366 const ref = maybe_ref orelse break;
33733367 const gop = try seen.getOrPut(gpa, ref.referencer);
33743368 if (gop.found_existing) break;
33753369 if (ref_traces.items.len < max_references) {
3376 const src = ref.src.upgrade(mod);
3370 const src = ref.src.upgrade(zcu);
33773371 const source = try src.file_scope.getSource(gpa);
33783372 const span = try src.span(gpa);
33793373 const loc = std.zig.findLineColumn(source.bytes, span.main);
......@@ -3385,7 +3379,7 @@ pub fn addModuleErrorMsg(
33853379 .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
33863380 .none => "comptime",
33873381 },
3388 .func => |f| ip.getNav(mod.funcInfo(f).owner_nav).name.toSlice(ip),
3382 .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
33893383 };
33903384 try ref_traces.append(gpa, .{
33913385 .decl_name = try eb.addString(name),
......@@ -3435,7 +3429,7 @@ pub fn addModuleErrorMsg(
34353429 defer notes.deinit(gpa);
34363430
34373431 for (module_err_msg.notes) |module_note| {
3438 const note_src_loc = module_note.src_loc.upgrade(mod);
3432 const note_src_loc = module_note.src_loc.upgrade(zcu);
34393433 const source = try note_src_loc.file_scope.getSource(gpa);
34403434 const span = try note_src_loc.span(gpa);
34413435 const loc = std.zig.findLineColumn(source.bytes, span.main);
......@@ -3488,13 +3482,13 @@ pub fn performAllTheWork(
34883482 comp: *Compilation,
34893483 main_progress_node: std.Progress.Node,
34903484) JobError!void {
3491 defer if (comp.zcu) |mod| {
3492 mod.sema_prog_node.end();
3493 mod.sema_prog_node = std.Progress.Node.none;
3494 mod.codegen_prog_node.end();
3495 mod.codegen_prog_node = std.Progress.Node.none;
3485 defer if (comp.zcu) |zcu| {
3486 zcu.sema_prog_node.end();
3487 zcu.sema_prog_node = std.Progress.Node.none;
3488 zcu.codegen_prog_node.end();
3489 zcu.codegen_prog_node = std.Progress.Node.none;
34963490
3497 mod.generation += 1;
3491 zcu.generation += 1;
34983492 };
34993493 try comp.performAllTheWorkInner(main_progress_node);
35003494 if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
src/InternPool.zig+12
......@@ -17,6 +17,13 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32),
1717/// Cached shift amount to put a `tid` in the top bits of a 32-bit value.
1818tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32),
1919
20/// Dependencies on whether an entire file gets past AstGen.
21/// These are triggered by `@import`, so that:
22/// * if a file initially fails AstGen, triggering a transitive failure, when a future update
23/// causes it to succeed AstGen, the `@import` is re-analyzed, allowing analysis to proceed
24/// * if a file initially succeds AstGen, but a future update causes the file to fail it,
25/// the `@import` is re-analyzed, registering a transitive failure
26file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
2027/// Dependencies on the source code hash associated with a ZIR instruction.
2128/// * For a `declaration`, this is the entire declaration body.
2229/// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations).
......@@ -70,6 +77,7 @@ pub const empty: InternPool = .{
7077 .tid_shift_30 = if (single_threaded) 0 else 31,
7178 .tid_shift_31 = if (single_threaded) 0 else 31,
7279 .tid_shift_32 = if (single_threaded) 0 else 31,
80 .file_deps = .empty,
7381 .src_hash_deps = .empty,
7482 .nav_val_deps = .empty,
7583 .interned_deps = .empty,
......@@ -656,6 +664,7 @@ pub const Nav = struct {
656664};
657665
658666pub const Dependee = union(enum) {
667 file: FileIndex,
659668 src_hash: TrackedInst.Index,
660669 nav_val: Nav.Index,
661670 interned: Index,
......@@ -704,6 +713,7 @@ pub const DependencyIterator = struct {
704713
705714pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator {
706715 const first_entry = switch (dependee) {
716 .file => |x| ip.file_deps.get(x),
707717 .src_hash => |x| ip.src_hash_deps.get(x),
708718 .nav_val => |x| ip.nav_val_deps.get(x),
709719 .interned => |x| ip.interned_deps.get(x),
......@@ -740,6 +750,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
740750 const new_index: DepEntry.Index = switch (dependee) {
741751 inline else => |dependee_payload, tag| new_index: {
742752 const gop = try switch (tag) {
753 .file => ip.file_deps,
743754 .src_hash => ip.src_hash_deps,
744755 .nav_val => ip.nav_val_deps,
745756 .interned => ip.interned_deps,
......@@ -6268,6 +6279,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
62686279}
62696280
62706281pub fn deinit(ip: *InternPool, gpa: Allocator) void {
6282 ip.file_deps.deinit(gpa);
62716283 ip.src_hash_deps.deinit(gpa);
62726284 ip.nav_val_deps.deinit(gpa);
62736285 ip.interned_deps.deinit(gpa);
src/Package/Module.zig+1
......@@ -454,6 +454,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
454454 .tree = undefined,
455455 .zir = undefined,
456456 .status = .never_loaded,
457 .prev_status = .never_loaded,
457458 .mod = new,
458459 };
459460 break :b new;
src/Sema.zig+3-8
......@@ -2559,10 +2559,9 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg
25592559 const zcu = sema.pt.zcu;
25602560
25612561 if (build_options.enable_debug_extensions and zcu.comp.debug_compile_errors) {
2562 var all_references: ?std.AutoHashMapUnmanaged(AnalUnit, ?Zcu.ResolvedReference) = null;
25632562 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
25642563 wip_errors.init(gpa) catch @panic("out of memory");
2565 Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, &all_references) catch @panic("out of memory");
2564 Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*) catch @panic("out of memory");
25662565 std.debug.print("compile error during Sema:\n", .{});
25672566 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");
25682567 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
......@@ -6025,9 +6024,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
60256024 pt.astGenFile(result.file, path_digest) catch |err|
60266025 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
60276026
6028 // TODO: register some kind of dependency on the file.
6029 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to
6030 // trigger re-analysis later.
6027 try sema.declareDependency(.{ .file = result.file_index });
60316028 try pt.ensureFileAnalyzed(result.file_index);
60326029 const ty = zcu.fileRootType(result.file_index);
60336030 try sema.declareDependency(.{ .interned = ty });
......@@ -14348,9 +14345,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1434814345 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
1434914346 },
1435014347 };
14351 // TODO: register some kind of dependency on the file.
14352 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to
14353 // trigger re-analysis later.
14348 try sema.declareDependency(.{ .file = result.file_index });
1435414349 try pt.ensureFileAnalyzed(result.file_index);
1435514350 const ty = zcu.fileRootType(result.file_index);
1435614351 try sema.declareDependency(.{ .interned = ty });
src/Zcu.zig+40-8
......@@ -173,6 +173,10 @@ retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .empty,
173173/// These are the modules which we initially queue for analysis in `Compilation.update`.
174174/// `resolveReferences` will use these as the root of its reachability traversal.
175175analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
176/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and
177/// reset to `null` when any semantic analysis occurs (since this invalidates the data).
178/// Allocated into `gpa`.
179resolved_references: ?std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = null,
176180
177181stage1_flags: packed struct {
178182 have_winmain: bool = false,
......@@ -420,13 +424,8 @@ pub const Namespace = struct {
420424};
421425
422426pub const File = struct {
423 status: enum {
424 never_loaded,
425 retryable_failure,
426 parse_failure,
427 astgen_failure,
428 success_zir,
429 },
427 status: Status,
428 prev_status: Status,
430429 source_loaded: bool,
431430 tree_loaded: bool,
432431 zir_loaded: bool,
......@@ -454,6 +453,14 @@ pub const File = struct {
454453 /// successful, this field is unloaded.
455454 prev_zir: ?*Zir = null,
456455
456 pub const Status = enum {
457 never_loaded,
458 retryable_failure,
459 parse_failure,
460 astgen_failure,
461 success_zir,
462 };
463
457464 /// A single reference to a file.
458465 pub const Reference = union(enum) {
459466 /// The file is imported directly (i.e. not as a package) with @import.
......@@ -2192,6 +2199,8 @@ pub fn deinit(zcu: *Zcu) void {
21922199 zcu.all_type_references.deinit(gpa);
21932200 zcu.free_type_references.deinit(gpa);
21942201
2202 if (zcu.resolved_references) |*r| r.deinit(gpa);
2203
21952204 zcu.intern_pool.deinit(gpa);
21962205}
21972206
......@@ -2760,6 +2769,8 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
27602769pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
27612770 const gpa = zcu.gpa;
27622771
2772 zcu.clearCachedResolvedReferences();
2773
27632774 unit_refs: {
27642775 const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse break :unit_refs;
27652776 var idx = kv.value;
......@@ -2792,6 +2803,8 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
27922803pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit, ref_src: LazySrcLoc) Allocator.Error!void {
27932804 const gpa = zcu.gpa;
27942805
2806 zcu.clearCachedResolvedReferences();
2807
27952808 try zcu.reference_table.ensureUnusedCapacity(gpa, 1);
27962809
27972810 const ref_idx = zcu.free_references.popOrNull() orelse idx: {
......@@ -2815,6 +2828,8 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit
28152828pub fn addTypeReference(zcu: *Zcu, src_unit: AnalUnit, referenced_type: InternPool.Index, ref_src: LazySrcLoc) Allocator.Error!void {
28162829 const gpa = zcu.gpa;
28172830
2831 zcu.clearCachedResolvedReferences();
2832
28182833 try zcu.type_reference_table.ensureUnusedCapacity(gpa, 1);
28192834
28202835 const ref_idx = zcu.free_type_references.popOrNull() orelse idx: {
......@@ -2835,6 +2850,11 @@ pub fn addTypeReference(zcu: *Zcu, src_unit: AnalUnit, referenced_type: InternPo
28352850 gop.value_ptr.* = @intCast(ref_idx);
28362851}
28372852
2853fn clearCachedResolvedReferences(zcu: *Zcu) void {
2854 if (zcu.resolved_references) |*r| r.deinit(zcu.gpa);
2855 zcu.resolved_references = null;
2856}
2857
28382858pub fn errorSetBits(zcu: *const Zcu) u16 {
28392859 if (zcu.error_limit == 0) return 0;
28402860 return @as(u16, std.math.log2_int(ErrorInt, zcu.error_limit)) + 1;
......@@ -3138,7 +3158,15 @@ pub const ResolvedReference = struct {
31383158/// Returns a mapping from an `AnalUnit` to where it is referenced.
31393159/// If the value is `null`, the `AnalUnit` is a root of analysis.
31403160/// If an `AnalUnit` is not in the returned map, it is unreferenced.
3141pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
3161/// The returned hashmap is owned by the `Zcu`, so should not be freed by the caller.
3162/// This hashmap is cached, so repeated calls to this function are cheap.
3163pub fn resolveReferences(zcu: *Zcu) !*const std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
3164 if (zcu.resolved_references == null) {
3165 zcu.resolved_references = try zcu.resolveReferencesInner();
3166 }
3167 return &zcu.resolved_references.?;
3168}
3169fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
31423170 const gpa = zcu.gpa;
31433171 const comp = zcu.comp;
31443172 const ip = &zcu.intern_pool;
......@@ -3449,6 +3477,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
34493477 const zcu = data.zcu;
34503478 const ip = &zcu.intern_pool;
34513479 switch (data.dependee) {
3480 .file => |file| {
3481 const file_path = zcu.fileByIndex(file).sub_file_path;
3482 return writer.print("file('{s}')", .{file_path});
3483 },
34523484 .src_hash => |ti| {
34533485 const info = ti.resolveFull(ip) orelse {
34543486 return writer.writeAll("inst(<lost>)");
src/Zcu/PerThread.zig+26-10
......@@ -179,10 +179,10 @@ pub fn astGenFile(
179179 .inode = header.stat_inode,
180180 .mtime = header.stat_mtime,
181181 };
182 file.prev_status = file.status;
182183 file.status = .success_zir;
183184 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
184185
185 // TODO don't report compile errors until Sema @importFile
186186 if (file.zir.hasCompileErrors()) {
187187 {
188188 comp.mutex.lock();
......@@ -258,6 +258,7 @@ pub fn astGenFile(
258258 // Any potential AST errors are converted to ZIR errors here.
259259 file.zir = try AstGen.generate(gpa, file.tree);
260260 file.zir_loaded = true;
261 file.prev_status = file.status;
261262 file.status = .success_zir;
262263 log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
263264
......@@ -350,6 +351,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
350351 defer cleanupUpdatedFiles(gpa, &updated_files);
351352 for (zcu.import_table.values()) |file_index| {
352353 const file = zcu.fileByIndex(file_index);
354 if (file.prev_status != file.status and file.prev_status != .never_loaded) {
355 try zcu.markDependeeOutdated(.not_marked_po, .{ .file = file_index });
356 }
353357 const old_zir = file.prev_zir orelse continue;
354358 const new_zir = file.zir;
355359 const gop = try updated_files.getOrPut(gpa, file_index);
......@@ -551,11 +555,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
551555 const cau_outdated = zcu.outdated.swapRemove(anal_unit) or
552556 zcu.potentially_outdated.swapRemove(anal_unit);
553557
558 const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
559
554560 if (cau_outdated) {
555561 _ = zcu.outdated_ready.swapRemove(anal_unit);
556562 } else {
557563 // We can trust the current information about this `Cau`.
558 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
564 if (prev_failed) {
559565 return error.AnalysisFail;
560566 }
561567 // If it wasn't failed and wasn't marked outdated, then either...
......@@ -578,9 +584,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
578584 // Since it does not, this must be a transitive failure.
579585 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
580586 }
581 // We treat errors as up-to-date, since those uses would just trigger a transitive error.
582 // The exception is types, since type declarations may require re-analysis if the type, e.g. its captures, changed.
583 const outdated = cau.owner.unwrap() == .type;
587 // We consider this `Cau` to be outdated if:
588 // * Previous analysis succeeded; in this case, we need to re-analyze dependants to ensure
589 // they hit a transitive error here, rather than reporting a different error later (which
590 // may now be invalid).
591 // * The `Cau` is a type; in this case, the declaration site may require re-analysis to
592 // construct a valid type.
593 const outdated = !prev_failed or cau.owner.unwrap() == .type;
584594 break :res .{ .{
585595 .invalidate_decl_val = outdated,
586596 .invalidate_decl_ref = outdated,
......@@ -597,10 +607,9 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
597607 );
598608 zcu.retryable_failures.appendAssumeCapacity(anal_unit);
599609 zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, msg);
600 // We treat errors as up-to-date, since those uses would just trigger a transitive error
601610 break :res .{ .{
602 .invalidate_decl_val = false,
603 .invalidate_decl_ref = false,
611 .invalidate_decl_val = true,
612 .invalidate_decl_ref = true,
604613 }, true };
605614 },
606615 };
......@@ -707,11 +716,13 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
707716 const func_outdated = zcu.outdated.swapRemove(anal_unit) or
708717 zcu.potentially_outdated.swapRemove(anal_unit);
709718
719 const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
720
710721 if (func_outdated) {
711722 _ = zcu.outdated_ready.swapRemove(anal_unit);
712723 } else {
713724 // We can trust the current information about this function.
714 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
725 if (prev_failed) {
715726 return error.AnalysisFail;
716727 }
717728 switch (func.analysisUnordered(ip).state) {
......@@ -730,7 +741,10 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
730741 // Since it does not, this must be a transitive failure.
731742 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
732743 }
733 break :res .{ false, true }; // we treat errors as up-to-date IES, since those uses would just trigger a transitive error
744 // We consider the IES to be outdated if the function previously succeeded analysis; in this case,
745 // we need to re-analyze dependants to ensure they hit a transitive error here, rather than reporting
746 // a different error later (which may now be invalid).
747 break :res .{ !prev_failed, true };
734748 },
735749 error.OutOfMemory => return error.OutOfMemory, // TODO: graceful handling like `ensureCauAnalyzed`
736750 };
......@@ -1445,6 +1459,7 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
14451459 .tree = undefined,
14461460 .zir = undefined,
14471461 .status = .never_loaded,
1462 .prev_status = .never_loaded,
14481463 .mod = mod,
14491464 };
14501465
......@@ -1555,6 +1570,7 @@ pub fn importFile(
15551570 .tree = undefined,
15561571 .zir = undefined,
15571572 .status = .never_loaded,
1573 .prev_status = .never_loaded,
15581574 .mod = mod,
15591575 };
15601576
src/main.zig+3
......@@ -6118,6 +6118,7 @@ fn cmdAstCheck(
61186118
61196119 var file: Zcu.File = .{
61206120 .status = .never_loaded,
6121 .prev_status = .never_loaded,
61216122 .source_loaded = false,
61226123 .tree_loaded = false,
61236124 .zir_loaded = false,
......@@ -6441,6 +6442,7 @@ fn cmdDumpZir(
64416442
64426443 var file: Zcu.File = .{
64436444 .status = .never_loaded,
6445 .prev_status = .never_loaded,
64446446 .source_loaded = false,
64456447 .tree_loaded = false,
64466448 .zir_loaded = true,
......@@ -6508,6 +6510,7 @@ fn cmdChangelist(
65086510
65096511 var file: Zcu.File = .{
65106512 .status = .never_loaded,
6513 .prev_status = .never_loaded,
65116514 .source_loaded = false,
65126515 .tree_loaded = false,
65136516 .zir_loaded = false,
test/incremental/add_decl+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux-selfhosted
1// Disabled on self-hosted due to linker crash
2// #target=x86_64-linux-selfhosted
23#target=x86_64-linux-cbe
34#target=x86_64-windows-cbe
45#update=initial version
test/incremental/fix_astgen_failure created+35
......@@ -0,0 +1,35 @@
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe
4#update=initial version with error
5#file=main.zig
6pub fn main() !void {
7 try @import("foo.zig").hello();
8}
9#file=foo.zig
10pub fn hello() !void {
11 try std.io.getStdOut().writeAll("Hello, World!\n");
12}
13#expect_error=ignored
14#update=fix the error
15#file=foo.zig
16const std = @import("std");
17pub fn hello() !void {
18 try std.io.getStdOut().writeAll("Hello, World!\n");
19}
20#expect_stdout="Hello, World!\n"
21#update=add new error
22#file=foo.zig
23const std = @import("std");
24pub fn hello() !void {
25 try std.io.getStdOut().writeAll(hello_str);
26}
27#expect_error=ignored
28#update=fix the new error
29#file=foo.zig
30const std = @import("std");
31const hello_str = "Hello, World! Again!\n";
32pub fn hello() !void {
33 try std.io.getStdOut().writeAll(hello_str);
34}
35#expect_stdout="Hello, World! Again!\n"
test/incremental/remove_invalid_union_backing_enum created+30
......@@ -0,0 +1,30 @@
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe
4#update=initial version
5#file=main.zig
6const E = enum { a, b, c };
7const U = union(E) {
8 a: i32,
9 b: f64,
10 c: f64,
11 d: f64,
12};
13pub fn main() void {
14 const u: U = .{ .a = 123 };
15 _ = u;
16}
17#expect_error=ignored
18#update=remove invalid backing enum
19#file=main.zig
20const U = union {
21 a: i32,
22 b: f64,
23 c: f64,
24 d: f64,
25};
26pub fn main() void {
27 const u: U = .{ .a = 123 };
28 _ = u;
29}
30#expect_stdout=""