Commit 7f3211a101
Changed files (4)
src/Zcu/PerThread.zig
@@ -538,7 +538,7 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
const cau = ip.getCau(cau_index);
- log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});
+ log.debug("ensureCauAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
assert(!zcu.analysis_in_progress.contains(anal_unit));
@@ -576,13 +576,19 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
}
const sema_result: SemaCauResult, const analysis_fail = if (pt.ensureCauAnalyzedInner(cau_index, cau_outdated)) |result|
- .{ result, false }
+ // This `Cau` has gone from failed to success, so even if the value of the owner `Nav` didn't actually
+ // change, we need to invalidate the dependencies anyway.
+ .{ .{
+ .invalidate_decl_val = result.invalidate_decl_val or prev_failed,
+ .invalidate_decl_ref = result.invalidate_decl_ref or prev_failed,
+ }, false }
else |err| switch (err) {
error.AnalysisFail => res: {
if (!zcu.failed_analysis.contains(anal_unit)) {
// If this `Cau` caused the error, it would have an entry in `failed_analysis`.
// Since it does not, this must be a transitive failure.
try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
}
// We consider this `Cau` to be outdated if:
// * 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
// We only care about the uncoerced function.
const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
+ const anal_unit = AnalUnit.wrap(.{ .func = func_index });
- const func = zcu.funcInfo(maybe_coerced_func_index);
+ log.debug("ensureFuncBodyAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
- log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});
+ const func = zcu.funcInfo(maybe_coerced_func_index);
- const anal_unit = AnalUnit.wrap(.{ .func = func_index });
const func_outdated = zcu.outdated.swapRemove(anal_unit) or
zcu.potentially_outdated.swapRemove(anal_unit);
@@ -740,6 +746,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
// If this function caused the error, it would have an entry in `failed_analysis`.
// Since it does not, this must be a transitive failure.
try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
}
// We consider the IES to be outdated if the function previously succeeded analysis; in this case,
// 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
if (func_outdated) {
if (ies_outdated) {
- log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
} else {
- log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
}
}
@@ -779,6 +784,7 @@ fn ensureFuncBodyAnalyzedInner(
// results in the worst case.
if (func.generic_owner == .none) {
+ // Among another things, this ensures that the function's `zir_body_inst` is correct.
try pt.ensureCauAnalyzed(ip.getNav(func.owner_nav).analysis_owner.unwrap().?);
if (ip.getNav(func.owner_nav).status.resolved.val != func_index) {
// This function is no longer referenced! There's no point in re-analyzing it.
@@ -787,6 +793,7 @@ fn ensureFuncBodyAnalyzedInner(
}
} else {
const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
+ // Among another things, this ensures that the function's `zir_body_inst` is correct.
try pt.ensureCauAnalyzed(ip.getNav(go_nav).analysis_owner.unwrap().?);
if (ip.getNav(go_nav).status.resolved.val != func.generic_owner) {
// The generic owner is no longer referenced, so this function is also unreferenced.
@@ -824,8 +831,8 @@ fn ensureFuncBodyAnalyzedInner(
}
}
- log.debug("analyze and generate fn body '{d}'; reason='{s}'", .{
- @intFromEnum(func_index),
+ log.debug("analyze and generate fn body {}; reason='{s}'", .{
+ zcu.fmtAnalUnit(anal_unit),
if (func_outdated) "outdated" else "never analyzed",
});
@@ -1164,7 +1171,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
.none, .type => false,
};
- log.debug("semaCau '{d}'", .{@intFromEnum(cau_index)});
+ log.debug("semaCau {}", .{zcu.fmtAnalUnit(anal_unit)});
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
@@ -2307,16 +2314,14 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err
return pt.getErrorValue(try pt.zcu.intern_pool.getOrPutString(pt.zcu.gpa, name));
}
+/// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed.
+/// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry.
fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void {
- switch (file.status) {
- .success_zir, .retryable_failure => {},
- .never_loaded, .parse_failure, .astgen_failure => {
- pt.zcu.comp.mutex.lock();
- defer pt.zcu.comp.mutex.unlock();
- if (pt.zcu.failed_files.fetchSwapRemove(file)) |kv| {
- if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // Delete previous error message.
- }
- },
+ if (!file.zir_loaded or !file.zir.hasCompileErrors()) return;
+ pt.zcu.comp.mutex.lock();
+ defer pt.zcu.comp.mutex.unlock();
+ if (pt.zcu.failed_files.fetchSwapRemove(file)) |kv| {
+ if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // Delete previous error message.
}
}
src/Compilation.zig
@@ -3223,17 +3223,29 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
}
}
- if (comp.zcu) |zcu| {
- if (comp.incremental and bundle.root_list.items.len == 0) {
- const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
- const refs = try zcu.resolveReferences();
- if (refs.contains(failed_unit)) break true;
- } else false;
- if (should_have_error) {
- @panic("referenced transitive analysis errors, but none actually emitted");
+ // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
+ // very common way for incremental compilation bugs to manifest, so let's always check it.
+ if (comp.zcu) |zcu| if (comp.incremental and bundle.root_list.items.len == 0) {
+ for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
+ const refs = try zcu.resolveReferences();
+ var ref = refs.get(failed_unit) orelse continue;
+ // This AU is referenced and has a transitive compile error, meaning it referenced something with a compile error.
+ // However, we haven't reported any such error.
+ // This is a compiler bug.
+ const stderr = std.io.getStdErr().writer();
+ try stderr.writeAll("referenced transitive analysis errors, but none actually emitted\n");
+ try stderr.print("{} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)});
+ while (ref) |r| {
+ try stderr.print("referenced by: {}{s}\n", .{
+ zcu.fmtAnalUnit(r.referencer),
+ if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "",
+ });
+ ref = refs.get(r.referencer).?;
}
+
+ @panic("referenced transitive analysis errors, but none actually emitted");
}
- }
+ };
const compile_log_text = if (comp.zcu) |m| m.compile_log_text.items else "";
return bundle.toOwnedBundle(compile_log_text);
src/InternPool.zig
@@ -8614,6 +8614,16 @@ pub fn getFuncDecl(
defer gop.deinit();
if (gop == .existing) {
extra.mutate.len = prev_extra_len;
+
+ const zir_body_inst_ptr = ip.funcDeclInfo(gop.existing).zirBodyInstPtr(ip);
+ if (zir_body_inst_ptr.* != key.zir_body_inst) {
+ // Since this function's `owner_nav` matches `key`, this *is* the function we're talking
+ // about. The only way it could have a different ZIR `func` instruction is if the old
+ // instruction has been lost and replaced with a new `TrackedInst.Index`.
+ assert(zir_body_inst_ptr.resolve(ip) == null);
+ zir_body_inst_ptr.* = key.zir_body_inst;
+ }
+
return gop.existing;
}
@@ -8760,6 +8770,16 @@ pub fn getFuncDeclIes(
// An existing function type was found; undo the additions to our two arrays.
items.mutate.len -= 4;
extra.mutate.len = prev_extra_len;
+
+ const zir_body_inst_ptr = ip.funcDeclInfo(func_gop.existing).zirBodyInstPtr(ip);
+ if (zir_body_inst_ptr.* != key.zir_body_inst) {
+ // Since this function's `owner_nav` matches `key`, this *is* the function we're talking
+ // about. The only way it could have a different ZIR `func` instruction is if the old
+ // instruction has been lost and replaced with a new `TrackedInst.Index`.
+ assert(zir_body_inst_ptr.resolve(ip) == null);
+ zir_body_inst_ptr.* = key.zir_body_inst;
+ }
+
return func_gop.existing;
}
func_gop.putTentative(func_index);
src/main.zig
@@ -6096,11 +6096,18 @@ fn cmdAstCheck(
var error_bundle = try wip_errors.toOwnedBundle("");
defer error_bundle.deinit(gpa);
error_bundle.renderToStdErr(color.renderOptions());
- process.exit(1);
+
+ if (file.zir.loweringFailed()) {
+ process.exit(1);
+ }
}
if (!want_output_text) {
- return cleanExit();
+ if (file.zir.hasCompileErrors()) {
+ process.exit(1);
+ } else {
+ return cleanExit();
+ }
}
if (!build_options.enable_debug_extensions) {
fatal("-t option only available in builds of zig with debug extensions", .{});
@@ -6144,7 +6151,13 @@ fn cmdAstCheck(
// zig fmt: on
}
- return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
+ try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
+
+ if (file.zir.hasCompileErrors()) {
+ process.exit(1);
+ } else {
+ return cleanExit();
+ }
}
fn cmdDetectCpu(