authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-05 16:59:50+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-05 19:58:42+00:00
log7f3211a101d8763ec5f0009b219f6819dba2cd35
treee9bb31b31381f6fe07bf9d2253c4d7d5b56e882f
parent4d7818a76ad951f0a16c3831b31841430b1368f7
signature Commit is signed but in an unrecognized format.

compiler: incremental compilation fixes

The previous commit exposed some bugs in incremental compilation. This commit fixes those, and adds a little more logging for debugging incremental compilation. Also, allow `ast-check -t` to dump ZIR when there are non-fatal AstGen errors.

4 files changed, 81 insertions(+), 31 deletions(-)

src/Compilation.zig+21-9
......@@ -3223,17 +3223,29 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32233223 }
32243224 }
32253225
3226 if (comp.zcu) |zcu| {
3227 if (comp.incremental and bundle.root_list.items.len == 0) {
3228 const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
3229 const refs = try zcu.resolveReferences();
3230 if (refs.contains(failed_unit)) break true;
3231 } else false;
3232 if (should_have_error) {
3233 @panic("referenced transitive analysis errors, but none actually emitted");
3226 // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
3227 // very common way for incremental compilation bugs to manifest, so let's always check it.
3228 if (comp.zcu) |zcu| if (comp.incremental and bundle.root_list.items.len == 0) {
3229 for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
3230 const refs = try zcu.resolveReferences();
3231 var ref = refs.get(failed_unit) orelse continue;
3232 // This AU is referenced and has a transitive compile error, meaning it referenced something with a compile error.
3233 // However, we haven't reported any such error.
3234 // This is a compiler bug.
3235 const stderr = std.io.getStdErr().writer();
3236 try stderr.writeAll("referenced transitive analysis errors, but none actually emitted\n");
3237 try stderr.print("{} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)});
3238 while (ref) |r| {
3239 try stderr.print("referenced by: {}{s}\n", .{
3240 zcu.fmtAnalUnit(r.referencer),
3241 if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "",
3242 });
3243 ref = refs.get(r.referencer).?;
32343244 }
3245
3246 @panic("referenced transitive analysis errors, but none actually emitted");
32353247 }
3236 }
3248 };
32373249
32383250 const compile_log_text = if (comp.zcu) |m| m.compile_log_text.items else "";
32393251 return bundle.toOwnedBundle(compile_log_text);
src/InternPool.zig+20
......@@ -8614,6 +8614,16 @@ pub fn getFuncDecl(
86148614 defer gop.deinit();
86158615 if (gop == .existing) {
86168616 extra.mutate.len = prev_extra_len;
8617
8618 const zir_body_inst_ptr = ip.funcDeclInfo(gop.existing).zirBodyInstPtr(ip);
8619 if (zir_body_inst_ptr.* != key.zir_body_inst) {
8620 // Since this function's `owner_nav` matches `key`, this *is* the function we're talking
8621 // about. The only way it could have a different ZIR `func` instruction is if the old
8622 // instruction has been lost and replaced with a new `TrackedInst.Index`.
8623 assert(zir_body_inst_ptr.resolve(ip) == null);
8624 zir_body_inst_ptr.* = key.zir_body_inst;
8625 }
8626
86178627 return gop.existing;
86188628 }
86198629
......@@ -8760,6 +8770,16 @@ pub fn getFuncDeclIes(
87608770 // An existing function type was found; undo the additions to our two arrays.
87618771 items.mutate.len -= 4;
87628772 extra.mutate.len = prev_extra_len;
8773
8774 const zir_body_inst_ptr = ip.funcDeclInfo(func_gop.existing).zirBodyInstPtr(ip);
8775 if (zir_body_inst_ptr.* != key.zir_body_inst) {
8776 // Since this function's `owner_nav` matches `key`, this *is* the function we're talking
8777 // about. The only way it could have a different ZIR `func` instruction is if the old
8778 // instruction has been lost and replaced with a new `TrackedInst.Index`.
8779 assert(zir_body_inst_ptr.resolve(ip) == null);
8780 zir_body_inst_ptr.* = key.zir_body_inst;
8781 }
8782
87638783 return func_gop.existing;
87648784 }
87658785 func_gop.putTentative(func_index);
src/Zcu/PerThread.zig+24-19
......@@ -538,7 +538,7 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
538538 const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
539539 const cau = ip.getCau(cau_index);
540540
541 log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});
541 log.debug("ensureCauAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
542542
543543 assert(!zcu.analysis_in_progress.contains(anal_unit));
544544
......@@ -576,13 +576,19 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
576576 }
577577
578578 const sema_result: SemaCauResult, const analysis_fail = if (pt.ensureCauAnalyzedInner(cau_index, cau_outdated)) |result|
579 .{ result, false }
579 // This `Cau` has gone from failed to success, so even if the value of the owner `Nav` didn't actually
580 // change, we need to invalidate the dependencies anyway.
581 .{ .{
582 .invalidate_decl_val = result.invalidate_decl_val or prev_failed,
583 .invalidate_decl_ref = result.invalidate_decl_ref or prev_failed,
584 }, false }
580585 else |err| switch (err) {
581586 error.AnalysisFail => res: {
582587 if (!zcu.failed_analysis.contains(anal_unit)) {
583588 // If this `Cau` caused the error, it would have an entry in `failed_analysis`.
584589 // Since it does not, this must be a transitive failure.
585590 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
591 log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
586592 }
587593 // We consider this `Cau` to be outdated if:
588594 // * Previous analysis succeeded; in this case, we need to re-analyze dependants to ensure
......@@ -707,12 +713,12 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
707713
708714 // We only care about the uncoerced function.
709715 const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
716 const anal_unit = AnalUnit.wrap(.{ .func = func_index });
710717
711 const func = zcu.funcInfo(maybe_coerced_func_index);
718 log.debug("ensureFuncBodyAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
712719
713 log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});
720 const func = zcu.funcInfo(maybe_coerced_func_index);
714721
715 const anal_unit = AnalUnit.wrap(.{ .func = func_index });
716722 const func_outdated = zcu.outdated.swapRemove(anal_unit) or
717723 zcu.potentially_outdated.swapRemove(anal_unit);
718724
......@@ -740,6 +746,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
740746 // If this function caused the error, it would have an entry in `failed_analysis`.
741747 // Since it does not, this must be a transitive failure.
742748 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
749 log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
743750 }
744751 // We consider the IES to be outdated if the function previously succeeded analysis; in this case,
745752 // we need to re-analyze dependants to ensure they hit a transitive error here, rather than reporting
......@@ -751,10 +758,8 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
751758
752759 if (func_outdated) {
753760 if (ies_outdated) {
754 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
755761 try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
756762 } else {
757 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
758763 try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
759764 }
760765 }
......@@ -779,6 +784,7 @@ fn ensureFuncBodyAnalyzedInner(
779784 // results in the worst case.
780785
781786 if (func.generic_owner == .none) {
787 // Among another things, this ensures that the function's `zir_body_inst` is correct.
782788 try pt.ensureCauAnalyzed(ip.getNav(func.owner_nav).analysis_owner.unwrap().?);
783789 if (ip.getNav(func.owner_nav).status.resolved.val != func_index) {
784790 // This function is no longer referenced! There's no point in re-analyzing it.
......@@ -787,6 +793,7 @@ fn ensureFuncBodyAnalyzedInner(
787793 }
788794 } else {
789795 const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
796 // Among another things, this ensures that the function's `zir_body_inst` is correct.
790797 try pt.ensureCauAnalyzed(ip.getNav(go_nav).analysis_owner.unwrap().?);
791798 if (ip.getNav(go_nav).status.resolved.val != func.generic_owner) {
792799 // The generic owner is no longer referenced, so this function is also unreferenced.
......@@ -824,8 +831,8 @@ fn ensureFuncBodyAnalyzedInner(
824831 }
825832 }
826833
827 log.debug("analyze and generate fn body '{d}'; reason='{s}'", .{
828 @intFromEnum(func_index),
834 log.debug("analyze and generate fn body {}; reason='{s}'", .{
835 zcu.fmtAnalUnit(anal_unit),
829836 if (func_outdated) "outdated" else "never analyzed",
830837 });
831838
......@@ -1164,7 +1171,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
11641171 .none, .type => false,
11651172 };
11661173
1167 log.debug("semaCau '{d}'", .{@intFromEnum(cau_index)});
1174 log.debug("semaCau {}", .{zcu.fmtAnalUnit(anal_unit)});
11681175
11691176 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
11701177 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
......@@ -2307,16 +2314,14 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err
23072314 return pt.getErrorValue(try pt.zcu.intern_pool.getOrPutString(pt.zcu.gpa, name));
23082315}
23092316
2317/// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed.
2318/// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry.
23102319fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void {
2311 switch (file.status) {
2312 .success_zir, .retryable_failure => {},
2313 .never_loaded, .parse_failure, .astgen_failure => {
2314 pt.zcu.comp.mutex.lock();
2315 defer pt.zcu.comp.mutex.unlock();
2316 if (pt.zcu.failed_files.fetchSwapRemove(file)) |kv| {
2317 if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // Delete previous error message.
2318 }
2319 },
2320 if (!file.zir_loaded or !file.zir.hasCompileErrors()) return;
2321 pt.zcu.comp.mutex.lock();
2322 defer pt.zcu.comp.mutex.unlock();
2323 if (pt.zcu.failed_files.fetchSwapRemove(file)) |kv| {
2324 if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // Delete previous error message.
23202325 }
23212326}
23222327
src/main.zig+16-3
......@@ -6096,11 +6096,18 @@ fn cmdAstCheck(
60966096 var error_bundle = try wip_errors.toOwnedBundle("");
60976097 defer error_bundle.deinit(gpa);
60986098 error_bundle.renderToStdErr(color.renderOptions());
6099 process.exit(1);
6099
6100 if (file.zir.loweringFailed()) {
6101 process.exit(1);
6102 }
61006103 }
61016104
61026105 if (!want_output_text) {
6103 return cleanExit();
6106 if (file.zir.hasCompileErrors()) {
6107 process.exit(1);
6108 } else {
6109 return cleanExit();
6110 }
61046111 }
61056112 if (!build_options.enable_debug_extensions) {
61066113 fatal("-t option only available in builds of zig with debug extensions", .{});
......@@ -6144,7 +6151,13 @@ fn cmdAstCheck(
61446151 // zig fmt: on
61456152 }
61466153
6147 return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
6154 try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
6155
6156 if (file.zir.hasCompileErrors()) {
6157 process.exit(1);
6158 } else {
6159 return cleanExit();
6160 }
61486161}
61496162
61506163fn cmdDetectCpu(