mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 00:08:56 +00:00
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.
This commit is contained in:
parent
4d7818a76a
commit
7f3211a101
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 });
|
||||
|
||||
log.debug("ensureFuncBodyAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
|
||||
|
||||
const func = zcu.funcInfo(maybe_coerced_func_index);
|
||||
|
||||
log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(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.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
19
src/main.zig
19
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(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user