authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-13 03:05:19+01:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-08-17 18:50:10-04:00
logaa6c1c40ec29d581844ebb5db09a33453c76d4ba
tree70206cdc7f9ce4adf14b4c98af6f9a7ce6a9add0
parent6faa4cc7e60c2ecd26759878a6f9e277d69a4968

frontend: yet more incremental work


4 files changed, 418 insertions(+), 192 deletions(-)

src/Compilation.zig+10-9
...@@ -2300,7 +2300,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2300,7 +2300,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2300 zcu.intern_pool.dumpGenericInstances(gpa);2300 zcu.intern_pool.dumpGenericInstances(gpa);
2301 }2301 }
23022302
2303 if (comp.config.is_test and comp.totalErrorCount() == 0) {2303 if (comp.config.is_test and try comp.totalErrorCount() == 0) {
2304 // The `test_functions` decl has been intentionally postponed until now,2304 // The `test_functions` decl has been intentionally postponed until now,
2305 // at which point we must populate it with the list of test functions that2305 // at which point we must populate it with the list of test functions that
2306 // have been discovered and not filtered out.2306 // have been discovered and not filtered out.
...@@ -2310,7 +2310,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2310,7 +2310,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2310 try pt.processExports();2310 try pt.processExports();
2311 }2311 }
23122312
2313 if (comp.totalErrorCount() != 0) {2313 if (try comp.totalErrorCount() != 0) {
2314 // Skip flushing and keep source files loaded for error reporting.2314 // Skip flushing and keep source files loaded for error reporting.
2315 comp.link_error_flags = .{};2315 comp.link_error_flags = .{};
2316 return;2316 return;
...@@ -2394,7 +2394,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2394,7 +2394,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2394 }2394 }
23952395
2396 try flush(comp, arena, .main, main_progress_node);2396 try flush(comp, arena, .main, main_progress_node);
2397 if (comp.totalErrorCount() != 0) return;2397 if (try comp.totalErrorCount() != 0) return;
23982398
2399 // Failure here only means an unnecessary cache miss.2399 // Failure here only means an unnecessary cache miss.
2400 man.writeManifest() catch |err| {2400 man.writeManifest() catch |err| {
...@@ -2411,7 +2411,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2411,7 +2411,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2411 },2411 },
2412 .incremental => {2412 .incremental => {
2413 try flush(comp, arena, .main, main_progress_node);2413 try flush(comp, arena, .main, main_progress_node);
2414 if (comp.totalErrorCount() != 0) return;2414 if (try comp.totalErrorCount() != 0) return;
2415 },2415 },
2416 }2416 }
2417}2417}
...@@ -3048,7 +3048,7 @@ fn addBuf(list: *std.ArrayList(std.posix.iovec_const), buf: []const u8) void {...@@ -3048,7 +3048,7 @@ fn addBuf(list: *std.ArrayList(std.posix.iovec_const), buf: []const u8) void {
3048}3048}
30493049
3050/// This function is temporally single-threaded.3050/// This function is temporally single-threaded.
3051pub fn totalErrorCount(comp: *Compilation) u32 {3051pub fn totalErrorCount(comp: *Compilation) Allocator.Error!u32 {
3052 var total: usize =3052 var total: usize =
3053 comp.misc_failures.count() +3053 comp.misc_failures.count() +
3054 @intFromBool(comp.alloc_failure_occurred) +3054 @intFromBool(comp.alloc_failure_occurred) +
...@@ -3088,7 +3088,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 {...@@ -3088,7 +3088,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
3088 // the previous parse success, including compile errors, but we cannot3088 // the previous parse success, including compile errors, but we cannot
3089 // emit them until the file succeeds parsing.3089 // emit them until the file succeeds parsing.
3090 for (zcu.failed_analysis.keys()) |anal_unit| {3090 for (zcu.failed_analysis.keys()) |anal_unit| {
3091 if (!all_references.contains(anal_unit)) continue;3091 if (comp.incremental and !all_references.contains(anal_unit)) continue;
3092 const file_index = switch (anal_unit.unwrap()) {3092 const file_index = switch (anal_unit.unwrap()) {
3093 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,3093 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
3094 .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,3094 .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,
...@@ -3225,7 +3225,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3225,7 +3225,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3225 if (err) |e| return e;3225 if (err) |e| return e;
3226 }3226 }
3227 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {3227 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {
3228 if (!all_references.contains(anal_unit)) continue;3228 if (comp.incremental and !all_references.contains(anal_unit)) continue;
32293229
3230 const file_index = switch (anal_unit.unwrap()) {3230 const file_index = switch (anal_unit.unwrap()) {
3231 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,3231 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
...@@ -3341,10 +3341,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3341,10 +3341,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3341 }3341 }
3342 }3342 }
33433343
3344 assert(comp.totalErrorCount() == bundle.root_list.items.len);3344 assert(try comp.totalErrorCount() == bundle.root_list.items.len);
33453345
3346 if (comp.module) |zcu| {3346 if (comp.module) |zcu| {
3347 if (bundle.root_list.items.len == 0) {3347 if (comp.incremental and bundle.root_list.items.len == 0) {
3348 const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| {3348 const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
3349 if (all_references.contains(failed_unit)) break true;3349 if (all_references.contains(failed_unit)) break true;
3350 } else false;3350 } else false;
...@@ -3448,6 +3448,7 @@ pub fn addModuleErrorMsg(...@@ -3448,6 +3448,7 @@ pub fn addModuleErrorMsg(
3448 const span = try src.span(gpa);3448 const span = try src.span(gpa);
3449 const loc = std.zig.findLineColumn(source.bytes, span.main);3449 const loc = std.zig.findLineColumn(source.bytes, span.main);
3450 const rt_file_path = try src.file_scope.fullPath(gpa);3450 const rt_file_path = try src.file_scope.fullPath(gpa);
3451 defer gpa.free(rt_file_path);
3451 const name = switch (ref.referencer.unwrap()) {3452 const name = switch (ref.referencer.unwrap()) {
3452 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {3453 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
3453 .nav => |nav| ip.getNav(nav).name.toSlice(ip),3454 .nav => |nav| ip.getNav(nav).name.toSlice(ip),
src/Sema.zig+25-25
...@@ -112,6 +112,11 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{},...@@ -112,6 +112,11 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{},
112references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},112references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},
113type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},113type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
114114
115/// All dependencies registered so far by this `Sema`. This is a temporary duplicate
116/// of the main dependency data. It exists to avoid adding dependencies to a given
117/// `AnalUnit` multiple times.
118dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .{},
119
115const MaybeComptimeAlloc = struct {120const MaybeComptimeAlloc = struct {
116 /// The runtime index of the `alloc` instruction.121 /// The runtime index of the `alloc` instruction.
117 runtime_index: Value.RuntimeIndex,122 runtime_index: Value.RuntimeIndex,
...@@ -879,6 +884,7 @@ pub fn deinit(sema: *Sema) void {...@@ -879,6 +884,7 @@ pub fn deinit(sema: *Sema) void {
879 sema.exports.deinit(gpa);884 sema.exports.deinit(gpa);
880 sema.references.deinit(gpa);885 sema.references.deinit(gpa);
881 sema.type_references.deinit(gpa);886 sema.type_references.deinit(gpa);
887 sema.dependencies.deinit(gpa);
882 sema.* = undefined;888 sema.* = undefined;
883}889}
884890
...@@ -2740,7 +2746,7 @@ fn maybeRemoveOutdatedType(sema: *Sema, ty: InternPool.Index) !bool {...@@ -2740,7 +2746,7 @@ fn maybeRemoveOutdatedType(sema: *Sema, ty: InternPool.Index) !bool {
2740 _ = zcu.outdated_ready.swapRemove(cau_unit);2746 _ = zcu.outdated_ready.swapRemove(cau_unit);
2741 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, cau_unit);2747 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, cau_unit);
2742 zcu.intern_pool.remove(pt.tid, ty);2748 zcu.intern_pool.remove(pt.tid, ty);
2743 try zcu.markDependeeOutdated(.{ .interned = ty });2749 try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
2744 return true;2750 return true;
2745}2751}
27462752
...@@ -6066,7 +6072,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -6066,7 +6072,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
6066 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to6072 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to
6067 // trigger re-analysis later.6073 // trigger re-analysis later.
6068 try pt.ensureFileAnalyzed(result.file_index);6074 try pt.ensureFileAnalyzed(result.file_index);
6069 return Air.internedToRef(zcu.fileRootType(result.file_index));6075 const ty = zcu.fileRootType(result.file_index);
6076 try sema.addTypeReferenceEntry(src, ty);
6077 return Air.internedToRef(ty);
6070}6078}
60716079
6072fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6080fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -6820,6 +6828,13 @@ fn lookupInNamespace(...@@ -6820,6 +6828,13 @@ fn lookupInNamespace(
68206828
6821 const src_file = zcu.namespacePtr(block.namespace).file_scope;6829 const src_file = zcu.namespacePtr(block.namespace).file_scope;
68226830
6831 if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| {
6832 try sema.declareDependency(.{ .namespace_name = .{
6833 .namespace = type_decl_inst,
6834 .name = ident_name,
6835 } });
6836 }
6837
6823 if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) {6838 if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) {
6824 const gpa = sema.gpa;6839 const gpa = sema.gpa;
6825 var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .{};6840 var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .{};
...@@ -13981,12 +13996,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13981,12 +13996,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13981 });13996 });
1398213997
13983 try sema.checkNamespaceType(block, lhs_src, container_type);13998 try sema.checkNamespaceType(block, lhs_src, container_type);
13984 if (container_type.typeDeclInst(mod)) |type_decl_inst| {
13985 try sema.declareDependency(.{ .namespace_name = .{
13986 .namespace = type_decl_inst,
13987 .name = decl_name,
13988 } });
13989 }
1399013999
13991 const namespace = container_type.getNamespace(mod).unwrap() orelse return .bool_false;14000 const namespace = container_type.getNamespace(mod).unwrap() orelse return .bool_false;
13992 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| {14001 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| {
...@@ -14026,7 +14035,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -14026,7 +14035,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
14026 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to14035 // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to
14027 // trigger re-analysis later.14036 // trigger re-analysis later.
14028 try pt.ensureFileAnalyzed(result.file_index);14037 try pt.ensureFileAnalyzed(result.file_index);
14029 return Air.internedToRef(zcu.fileRootType(result.file_index));14038 const ty = zcu.fileRootType(result.file_index);
14039 try sema.addTypeReferenceEntry(operand_src, ty);
14040 return Air.internedToRef(ty);
14030}14041}
1403114042
14032fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {14043fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -27696,13 +27707,6 @@ fn fieldVal(...@@ -27696,13 +27707,6 @@ fn fieldVal(
27696 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;27707 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
27697 const child_type = val.toType();27708 const child_type = val.toType();
2769827709
27699 if (child_type.typeDeclInst(mod)) |type_decl_inst| {
27700 try sema.declareDependency(.{ .namespace_name = .{
27701 .namespace = type_decl_inst,
27702 .name = field_name,
27703 } });
27704 }
27705
27706 switch (try child_type.zigTypeTagOrPoison(mod)) {27710 switch (try child_type.zigTypeTagOrPoison(mod)) {
27707 .ErrorSet => {27711 .ErrorSet => {
27708 switch (ip.indexToKey(child_type.toIntern())) {27712 switch (ip.indexToKey(child_type.toIntern())) {
...@@ -27934,13 +27938,6 @@ fn fieldPtr(...@@ -27934,13 +27938,6 @@ fn fieldPtr(
27934 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;27938 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
27935 const child_type = val.toType();27939 const child_type = val.toType();
2793627940
27937 if (child_type.typeDeclInst(mod)) |type_decl_inst| {
27938 try sema.declareDependency(.{ .namespace_name = .{
27939 .namespace = type_decl_inst,
27940 .name = field_name,
27941 } });
27942 }
27943
27944 switch (child_type.zigTypeTag(mod)) {27941 switch (child_type.zigTypeTag(mod)) {
27945 .ErrorSet => {27942 .ErrorSet => {
27946 switch (ip.indexToKey(child_type.toIntern())) {27943 switch (ip.indexToKey(child_type.toIntern())) {
...@@ -32260,7 +32257,7 @@ fn addReferenceEntry(...@@ -32260,7 +32257,7 @@ fn addReferenceEntry(
32260 referenced_unit: AnalUnit,32257 referenced_unit: AnalUnit,
32261) !void {32258) !void {
32262 const zcu = sema.pt.zcu;32259 const zcu = sema.pt.zcu;
32263 if (zcu.comp.reference_trace == 0) return;32260 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
32264 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);32261 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
32265 if (gop.found_existing) return;32262 if (gop.found_existing) return;
32266 // TODO: we need to figure out how to model inline calls here.32263 // TODO: we need to figure out how to model inline calls here.
...@@ -32275,7 +32272,7 @@ fn addTypeReferenceEntry(...@@ -32275,7 +32272,7 @@ fn addTypeReferenceEntry(
32275 referenced_type: InternPool.Index,32272 referenced_type: InternPool.Index,
32276) !void {32273) !void {
32277 const zcu = sema.pt.zcu;32274 const zcu = sema.pt.zcu;
32278 if (zcu.comp.reference_trace == 0) return;32275 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
32279 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);32276 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
32280 if (gop.found_existing) return;32277 if (gop.found_existing) return;
32281 try zcu.addTypeReference(sema.owner, referenced_type, src);32278 try zcu.addTypeReference(sema.owner, referenced_type, src);
...@@ -38272,6 +38269,9 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {...@@ -38272,6 +38269,9 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
38272 const zcu = sema.pt.zcu;38269 const zcu = sema.pt.zcu;
38273 if (!zcu.comp.incremental) return;38270 if (!zcu.comp.incremental) return;
3827438271
38272 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
38273 if (gop.found_existing) return;
38274
38275 // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields38275 // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields
38276 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would38276 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
38277 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve38277 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
src/Zcu.zig+198-43
...@@ -10,7 +10,7 @@ const builtin = @import("builtin");...@@ -10,7 +10,7 @@ const builtin = @import("builtin");
10const mem = std.mem;10const mem = std.mem;
11const Allocator = std.mem.Allocator;11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;12const assert = std.debug.assert;
13const log = std.log.scoped(.module);13const log = std.log.scoped(.zcu);
14const BigIntConst = std.math.big.int.Const;14const BigIntConst = std.math.big.int.Const;
15const BigIntMutable = std.math.big.int.Mutable;15const BigIntMutable = std.math.big.int.Mutable;
16const Target = std.Target;16const Target = std.Target;
...@@ -153,9 +153,11 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = ....@@ -153,9 +153,11 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .
153/// Maximum amount of distinct error values, set by --error-limit153/// Maximum amount of distinct error values, set by --error-limit
154error_limit: ErrorInt,154error_limit: ErrorInt,
155155
156/// Value is the number of PO or outdated Decls which this AnalUnit depends on.156/// Value is the number of PO dependencies of this AnalUnit.
157/// This value will decrease as we perform semantic analysis to learn what is outdated.
158/// If any of these PO deps is outdated, this value will be moved to `outdated`.
157potentially_outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},159potentially_outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
158/// Value is the number of PO or outdated Decls which this AnalUnit depends on.160/// Value is the number of PO dependencies of this AnalUnit.
159/// Once this value drops to 0, the AnalUnit is a candidate for re-analysis.161/// Once this value drops to 0, the AnalUnit is a candidate for re-analysis.
160outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},162outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
161/// This contains all `AnalUnit`s in `outdated` whose PO dependency count is 0.163/// This contains all `AnalUnit`s in `outdated` whose PO dependency count is 0.
...@@ -2276,55 +2278,90 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.F...@@ -2276,55 +2278,90 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.F
2276 return zir;2278 return zir;
2277}2279}
22782280
2279pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {2281pub fn markDependeeOutdated(
2280 log.debug("outdated dependee: {}", .{dependee});2282 zcu: *Zcu,
2283 /// When we are diffing ZIR and marking things as outdated, we won't yet have marked the dependencies as PO.
2284 /// However, when we discover during analysis that something was outdated, the `Dependee` was already
2285 /// marked as PO, so we need to decrement the PO dep count for each depender.
2286 marked_po: enum { not_marked_po, marked_po },
2287 dependee: InternPool.Dependee,
2288) !void {
2289 log.debug("outdated dependee: {}", .{fmtDependee(dependee, zcu)});
2281 var it = zcu.intern_pool.dependencyIterator(dependee);2290 var it = zcu.intern_pool.dependencyIterator(dependee);
2282 while (it.next()) |depender| {2291 while (it.next()) |depender| {
2283 if (zcu.outdated.contains(depender)) {2292 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
2284 // We do not need to increment the PO dep count, as if the outdated2293 switch (marked_po) {
2285 // dependee is a Decl, we had already marked this as PO.2294 .not_marked_po => {},
2295 .marked_po => {
2296 po_dep_count.* -= 1;
2297 log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), po_dep_count.* });
2298 if (po_dep_count.* == 0) {
2299 log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
2300 try zcu.outdated_ready.put(zcu.gpa, depender, {});
2301 }
2302 },
2303 }
2286 continue;2304 continue;
2287 }2305 }
2288 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender);2306 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender);
2307 const new_po_dep_count = switch (marked_po) {
2308 .not_marked_po => if (opt_po_entry) |e| e.value else 0,
2309 .marked_po => if (opt_po_entry) |e| e.value - 1 else {
2310 // This dependency has been registered during in-progress analysis, but the unit is
2311 // not in `potentially_outdated` because analysis is in-progress. Nothing to do.
2312 continue;
2313 },
2314 };
2315 log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), new_po_dep_count });
2289 try zcu.outdated.putNoClobber(2316 try zcu.outdated.putNoClobber(
2290 zcu.gpa,2317 zcu.gpa,
2291 depender,2318 depender,
2292 // We do not need to increment this count for the same reason as above.2319 new_po_dep_count,
2293 if (opt_po_entry) |e| e.value else 0,
2294 );2320 );
2295 log.debug("outdated: {}", .{depender});2321 log.debug("outdated: {}", .{fmtAnalUnit(depender, zcu)});
2296 if (opt_po_entry == null) {2322 if (new_po_dep_count == 0) {
2297 // This is a new entry with no PO dependencies.2323 log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
2298 try zcu.outdated_ready.put(zcu.gpa, depender, {});2324 try zcu.outdated_ready.put(zcu.gpa, depender, {});
2299 }2325 }
2300 // If this is a Decl and was not previously PO, we must recursively2326 // If this is a Decl and was not previously PO, we must recursively
2301 // mark dependencies on its tyval as PO.2327 // mark dependencies on its tyval as PO.
2302 if (opt_po_entry == null) {2328 if (opt_po_entry == null) {
2329 assert(marked_po == .not_marked_po);
2303 try zcu.markTransitiveDependersPotentiallyOutdated(depender);2330 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
2304 }2331 }
2305 }2332 }
2306}2333}
23072334
2308pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {2335pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
2336 log.debug("up-to-date dependee: {}", .{fmtDependee(dependee, zcu)});
2309 var it = zcu.intern_pool.dependencyIterator(dependee);2337 var it = zcu.intern_pool.dependencyIterator(dependee);
2310 while (it.next()) |depender| {2338 while (it.next()) |depender| {
2311 if (zcu.outdated.getPtr(depender)) |po_dep_count| {2339 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
2312 // This depender is already outdated, but it now has one2340 // This depender is already outdated, but it now has one
2313 // less PO dependency!2341 // less PO dependency!
2314 po_dep_count.* -= 1;2342 po_dep_count.* -= 1;
2343 log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), po_dep_count.* });
2315 if (po_dep_count.* == 0) {2344 if (po_dep_count.* == 0) {
2345 log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
2316 try zcu.outdated_ready.put(zcu.gpa, depender, {});2346 try zcu.outdated_ready.put(zcu.gpa, depender, {});
2317 }2347 }
2318 continue;2348 continue;
2319 }2349 }
2320 // This depender is definitely at least PO, because this Decl was just analyzed2350 // This depender is definitely at least PO, because this Decl was just analyzed
2321 // due to being outdated.2351 // due to being outdated.
2322 const ptr = zcu.potentially_outdated.getPtr(depender).?;2352 const ptr = zcu.potentially_outdated.getPtr(depender) orelse {
2353 // This dependency has been registered during in-progress analysis, but the unit is
2354 // not in `potentially_outdated` because analysis is in-progress. Nothing to do.
2355 continue;
2356 };
2323 if (ptr.* > 1) {2357 if (ptr.* > 1) {
2324 ptr.* -= 1;2358 ptr.* -= 1;
2359 log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), ptr.* });
2325 continue;2360 continue;
2326 }2361 }
23272362
2363 log.debug("up-to-date (po deps = 0): {}", .{fmtAnalUnit(depender, zcu)});
2364
2328 // This dependency is no longer PO, i.e. is known to be up-to-date.2365 // This dependency is no longer PO, i.e. is known to be up-to-date.
2329 assert(zcu.potentially_outdated.swapRemove(depender));2366 assert(zcu.potentially_outdated.swapRemove(depender));
2330 // If this is a Decl, we must recursively mark dependencies on its tyval2367 // If this is a Decl, we must recursively mark dependencies on its tyval
...@@ -2344,14 +2381,16 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {...@@ -2344,14 +2381,16 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
2344/// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES.2381/// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES.
2345fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {2382fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {
2346 const ip = &zcu.intern_pool;2383 const ip = &zcu.intern_pool;
2347 var it = ip.dependencyIterator(switch (maybe_outdated.unwrap()) {2384 const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
2348 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {2385 .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
2349 .nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced2386 .nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
2350 .none, .type => return, // analysis of this `Cau` can't outdate any dependencies2387 .type => |ty| .{ .interned = ty },
2388 .none => return, // analysis of this `Cau` can't outdate any dependencies
2351 },2389 },
2352 .func => |func_index| .{ .interned = func_index }, // IES2390 .func => |func_index| .{ .interned = func_index }, // IES
2353 });2391 };
23542392 log.debug("marking dependee po: {}", .{fmtDependee(dependee, zcu)});
2393 var it = ip.dependencyIterator(dependee);
2355 while (it.next()) |po| {2394 while (it.next()) |po| {
2356 if (zcu.outdated.getPtr(po)) |po_dep_count| {2395 if (zcu.outdated.getPtr(po)) |po_dep_count| {
2357 // This dependency is already outdated, but it now has one more PO2396 // This dependency is already outdated, but it now has one more PO
...@@ -2360,14 +2399,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni...@@ -2360,14 +2399,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
2360 _ = zcu.outdated_ready.swapRemove(po);2399 _ = zcu.outdated_ready.swapRemove(po);
2361 }2400 }
2362 po_dep_count.* += 1;2401 po_dep_count.* += 1;
2402 log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), po_dep_count.* });
2363 continue;2403 continue;
2364 }2404 }
2365 if (zcu.potentially_outdated.getPtr(po)) |n| {2405 if (zcu.potentially_outdated.getPtr(po)) |n| {
2366 // There is now one more PO dependency.2406 // There is now one more PO dependency.
2367 n.* += 1;2407 n.* += 1;
2408 log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), n.* });
2368 continue;2409 continue;
2369 }2410 }
2370 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);2411 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
2412 log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), 1 });
2371 // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.2413 // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.
2372 try zcu.markTransitiveDependersPotentiallyOutdated(po);2414 try zcu.markTransitiveDependersPotentiallyOutdated(po);
2373 }2415 }
...@@ -2391,13 +2433,9 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {...@@ -2391,13 +2433,9 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
2391 // In this case, we must defer to more complex logic below.2433 // In this case, we must defer to more complex logic below.
23922434
2393 if (zcu.outdated_ready.count() > 0) {2435 if (zcu.outdated_ready.count() > 0) {
2394 log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{2436 const unit = zcu.outdated_ready.keys()[0];
2395 @tagName(zcu.outdated_ready.keys()[0].unwrap()),2437 log.debug("findOutdatedToAnalyze: trivial {}", .{fmtAnalUnit(unit, zcu)});
2396 switch (zcu.outdated_ready.keys()[0].unwrap()) {2438 return unit;
2397 inline else => |x| @intFromEnum(x),
2398 },
2399 });
2400 return zcu.outdated_ready.keys()[0];
2401 }2439 }
24022440
2403 // There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some2441 // There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some
...@@ -2445,8 +2483,16 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {...@@ -2445,8 +2483,16 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
2445 }2483 }
2446 }2484 }
24472485
2448 log.debug("findOutdatedToAnalyze: heuristic returned Cau {d} ({d} dependers)", .{2486 if (chosen_cau == null) {
2449 @intFromEnum(chosen_cau.?),2487 for (zcu.outdated.keys(), zcu.outdated.values()) |o, opod| {
2488 const func = o.unwrap().func;
2489 const nav = zcu.funcInfo(func).owner_nav;
2490 std.io.getStdErr().writer().print("outdated: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {};
2491 }
2492 }
2493
2494 log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{
2495 fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? }), zcu),
2450 chosen_cau_dependers,2496 chosen_cau_dependers,
2451 });2497 });
24522498
...@@ -3090,7 +3136,6 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve...@@ -3090,7 +3136,6 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
3090 });3136 });
3091 defer gpa.free(resolved_path);3137 defer gpa.free(resolved_path);
3092 const file = zcu.import_table.get(resolved_path).?;3138 const file = zcu.import_table.get(resolved_path).?;
3093 if (zcu.fileByIndex(file).status != .success_zir) continue;
3094 const root_ty = zcu.fileRootType(file);3139 const root_ty = zcu.fileRootType(file);
3095 if (root_ty == .none) continue;3140 if (root_ty == .none) continue;
3096 type_queue.putAssumeCapacityNoClobber(root_ty, null);3141 type_queue.putAssumeCapacityNoClobber(root_ty, null);
...@@ -3102,6 +3147,8 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve...@@ -3102,6 +3147,8 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
3102 const referencer = kv.value;3147 const referencer = kv.value;
3103 try checked_types.putNoClobber(gpa, ty, {});3148 try checked_types.putNoClobber(gpa, ty, {});
31043149
3150 log.debug("handle type '{}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
3151
3105 // If this type has a `Cau` for resolution, it's automatically referenced.3152 // If this type has a `Cau` for resolution, it's automatically referenced.
3106 const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) {3153 const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) {
3107 .struct_type => ip.loadStructType(ty).cau,3154 .struct_type => ip.loadStructType(ty).cau,
...@@ -3132,13 +3179,14 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve...@@ -3132,13 +3179,14 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
31323179
3133 // Queue any decls within this type which would be automatically analyzed.3180 // Queue any decls within this type which would be automatically analyzed.
3134 // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.3181 // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.
3135 const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap() orelse continue;3182 const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?;
3136 for (zcu.namespacePtr(ns).other_decls.items) |cau| {3183 for (zcu.namespacePtr(ns).other_decls.items) |cau| {
3137 // These are `comptime` and `test` declarations.3184 // These are `comptime` and `test` declarations.
3138 // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter.3185 // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter.
3139 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;3186 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3140 const file = zcu.fileByIndex(inst_info.file);3187 const file = zcu.fileByIndex(inst_info.file);
3141 const zir = file.zir;3188 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3189 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3142 const declaration = zir.getDeclaration(inst_info.inst)[0];3190 const declaration = zir.getDeclaration(inst_info.inst)[0];
3143 const want_analysis = switch (declaration.name) {3191 const want_analysis = switch (declaration.name) {
3144 .@"usingnamespace" => unreachable,3192 .@"usingnamespace" => unreachable,
...@@ -3158,27 +3206,51 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve...@@ -3158,27 +3206,51 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
3158 };3206 };
3159 if (want_analysis) {3207 if (want_analysis) {
3160 const unit = AnalUnit.wrap(.{ .cau = cau });3208 const unit = AnalUnit.wrap(.{ .cau = cau });
3161 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);3209 if (!result.contains(unit)) {
3210 log.debug("type '{}': ref cau %{}", .{
3211 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
3212 @intFromEnum(inst_info.inst),
3213 });
3214 try unit_queue.put(gpa, unit, referencer);
3215 }
3162 }3216 }
3163 }3217 }
3164 for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {3218 for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {
3165 // These are named declarations. They are analyzed only if marked `export`.3219 // These are named declarations. They are analyzed only if marked `export`.
3166 const cau = ip.getNav(nav).analysis_owner.unwrap().?;3220 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3167 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;3221 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3168 const declaration = zcu.fileByIndex(inst_info.file).zir.getDeclaration(inst_info.inst)[0];3222 const file = zcu.fileByIndex(inst_info.file);
3223 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3224 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3225 const declaration = zir.getDeclaration(inst_info.inst)[0];
3169 if (declaration.flags.is_export) {3226 if (declaration.flags.is_export) {
3170 const unit = AnalUnit.wrap(.{ .cau = cau });3227 const unit = AnalUnit.wrap(.{ .cau = cau });
3171 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);3228 if (!result.contains(unit)) {
3229 log.debug("type '{}': ref cau %{}", .{
3230 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
3231 @intFromEnum(inst_info.inst),
3232 });
3233 try unit_queue.put(gpa, unit, referencer);
3234 }
3172 }3235 }
3173 }3236 }
3174 for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {3237 for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {
3175 // These are named declarations. They are analyzed only if marked `export`.3238 // These are named declarations. They are analyzed only if marked `export`.
3176 const cau = ip.getNav(nav).analysis_owner.unwrap().?;3239 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3177 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;3240 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3178 const declaration = zcu.fileByIndex(inst_info.file).zir.getDeclaration(inst_info.inst)[0];3241 const file = zcu.fileByIndex(inst_info.file);
3242 // If the file failed AstGen, the TrackedInst refers to the old ZIR.
3243 const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
3244 const declaration = zir.getDeclaration(inst_info.inst)[0];
3179 if (declaration.flags.is_export) {3245 if (declaration.flags.is_export) {
3180 const unit = AnalUnit.wrap(.{ .cau = cau });3246 const unit = AnalUnit.wrap(.{ .cau = cau });
3181 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);3247 if (!result.contains(unit)) {
3248 log.debug("type '{}': ref cau %{}", .{
3249 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
3250 @intFromEnum(inst_info.inst),
3251 });
3252 try unit_queue.put(gpa, unit, referencer);
3253 }
3182 }3254 }
3183 }3255 }
3184 // Incremental compilation does not support `usingnamespace`.3256 // Incremental compilation does not support `usingnamespace`.
...@@ -3199,15 +3271,23 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve...@@ -3199,15 +3271,23 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
3199 const unit = kv.key;3271 const unit = kv.key;
3200 try result.putNoClobber(gpa, unit, kv.value);3272 try result.putNoClobber(gpa, unit, kv.value);
32013273
3274 log.debug("handle unit '{}'", .{fmtAnalUnit(unit, zcu)});
3275
3202 if (zcu.reference_table.get(unit)) |first_ref_idx| {3276 if (zcu.reference_table.get(unit)) |first_ref_idx| {
3203 assert(first_ref_idx != std.math.maxInt(u32));3277 assert(first_ref_idx != std.math.maxInt(u32));
3204 var ref_idx = first_ref_idx;3278 var ref_idx = first_ref_idx;
3205 while (ref_idx != std.math.maxInt(u32)) {3279 while (ref_idx != std.math.maxInt(u32)) {
3206 const ref = zcu.all_references.items[ref_idx];3280 const ref = zcu.all_references.items[ref_idx];
3207 if (!result.contains(ref.referenced)) try unit_queue.put(gpa, ref.referenced, .{3281 if (!result.contains(ref.referenced)) {
3208 .referencer = unit,3282 log.debug("unit '{}': ref unit '{}'", .{
3209 .src = ref.src,3283 fmtAnalUnit(unit, zcu),
3210 });3284 fmtAnalUnit(ref.referenced, zcu),
3285 });
3286 try unit_queue.put(gpa, ref.referenced, .{
3287 .referencer = unit,
3288 .src = ref.src,
3289 });
3290 }
3211 ref_idx = ref.next;3291 ref_idx = ref.next;
3212 }3292 }
3213 }3293 }
...@@ -3216,10 +3296,16 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve...@@ -3216,10 +3296,16 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
3216 var ref_idx = first_ref_idx;3296 var ref_idx = first_ref_idx;
3217 while (ref_idx != std.math.maxInt(u32)) {3297 while (ref_idx != std.math.maxInt(u32)) {
3218 const ref = zcu.all_type_references.items[ref_idx];3298 const ref = zcu.all_type_references.items[ref_idx];
3219 if (!checked_types.contains(ref.referenced)) try type_queue.put(gpa, ref.referenced, .{3299 if (!checked_types.contains(ref.referenced)) {
3220 .referencer = unit,3300 log.debug("unit '{}': ref type '{}'", .{
3221 .src = ref.src,3301 fmtAnalUnit(unit, zcu),
3222 });3302 Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip),
3303 });
3304 try type_queue.put(gpa, ref.referenced, .{
3305 .referencer = unit,
3306 .src = ref.src,
3307 });
3308 }
3223 ref_idx = ref.next;3309 ref_idx = ref.next;
3224 }3310 }
3225 }3311 }
...@@ -3293,3 +3379,72 @@ pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {...@@ -3293,3 +3379,72 @@ pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {
3293 const file_index = ip.getCau(cau).zir_index.resolveFile(ip);3379 const file_index = ip.getCau(cau).zir_index.resolveFile(ip);
3294 return zcu.fileByIndex(file_index);3380 return zcu.fileByIndex(file_index);
3295}3381}
3382
3383fn fmtAnalUnit(unit: AnalUnit, zcu: *Zcu) std.fmt.Formatter(formatAnalUnit) {
3384 return .{ .data = .{ .unit = unit, .zcu = zcu } };
3385}
3386fn fmtDependee(d: InternPool.Dependee, zcu: *Zcu) std.fmt.Formatter(formatDependee) {
3387 return .{ .data = .{ .dependee = d, .zcu = zcu } };
3388}
3389
3390fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
3391 _ = .{ fmt, options };
3392 const zcu = data.zcu;
3393 const ip = &zcu.intern_pool;
3394 switch (data.unit.unwrap()) {
3395 .cau => |cau_index| {
3396 const cau = ip.getCau(cau_index);
3397 switch (cau.owner.unwrap()) {
3398 .nav => |nav| return writer.print("cau(decl='{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
3399 .type => |ty| return writer.print("cau(ty='{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
3400 .none => if (cau.zir_index.resolveFull(ip)) |resolved| {
3401 const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
3402 return writer.print("cau(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
3403 } else {
3404 return writer.writeAll("cau(inst=<lost>)");
3405 },
3406 }
3407 },
3408 .func => |func| {
3409 const nav = zcu.funcInfo(func).owner_nav;
3410 return writer.print("func('{}')", .{ip.getNav(nav).fqn.fmt(ip)});
3411 },
3412 }
3413}
3414fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
3415 _ = .{ fmt, options };
3416 const zcu = data.zcu;
3417 const ip = &zcu.intern_pool;
3418 switch (data.dependee) {
3419 .src_hash => |ti| {
3420 const info = ti.resolveFull(ip) orelse {
3421 return writer.writeAll("inst(<lost>)");
3422 };
3423 const file_path = zcu.fileByIndex(info.file).sub_file_path;
3424 return writer.print("inst('{s}', %{d})", .{ file_path, @intFromEnum(info.inst) });
3425 },
3426 .nav_val => |nav| {
3427 const fqn = ip.getNav(nav).fqn;
3428 return writer.print("nav('{}')", .{fqn.fmt(ip)});
3429 },
3430 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
3431 .struct_type, .union_type, .enum_type => return writer.print("type('{}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
3432 .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
3433 else => unreachable,
3434 },
3435 .namespace => |ti| {
3436 const info = ti.resolveFull(ip) orelse {
3437 return writer.writeAll("namespace(<lost>)");
3438 };
3439 const file_path = zcu.fileByIndex(info.file).sub_file_path;
3440 return writer.print("namespace('{s}', %{d})", .{ file_path, @intFromEnum(info.inst) });
3441 },
3442 .namespace_name => |k| {
3443 const info = k.namespace.resolveFull(ip) orelse {
3444 return writer.print("namespace(<lost>, '{}')", .{k.name.fmt(ip)});
3445 };
3446 const file_path = zcu.fileByIndex(info.file).sub_file_path;
3447 return writer.print("namespace('{s}', %{d}, '{}')", .{ file_path, @intFromEnum(info.inst), k.name.fmt(ip) });
3448 },
3449 }
3450}
src/Zcu/PerThread.zig+185-115
...@@ -360,7 +360,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -360,7 +360,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
360 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.360 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
361 log.debug("tracking failed for %{d}", .{old_inst});361 log.debug("tracking failed for %{d}", .{old_inst});
362 tracked_inst.inst = .lost;362 tracked_inst.inst = .lost;
363 try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });363 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
364 continue;364 continue;
365 };365 };
366 tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst);366 tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst);
...@@ -383,7 +383,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -383,7 +383,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
383 });383 });
384 }384 }
385 // The source hash associated with this instruction changed - invalidate relevant dependencies.385 // The source hash associated with this instruction changed - invalidate relevant dependencies.
386 try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });386 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
387 }387 }
388388
389 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.389 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
...@@ -435,7 +435,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -435,7 +435,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
435 if (!old_names.swapRemove(name_ip)) continue;435 if (!old_names.swapRemove(name_ip)) continue;
436 // Name added436 // Name added
437 any_change = true;437 any_change = true;
438 try zcu.markDependeeOutdated(.{ .namespace_name = .{438 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
439 .namespace = tracked_inst_index,439 .namespace = tracked_inst_index,
440 .name = name_ip,440 .name = name_ip,
441 } });441 } });
...@@ -444,14 +444,14 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -444,14 +444,14 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
444 // The only elements remaining in `old_names` now are any names which were removed.444 // The only elements remaining in `old_names` now are any names which were removed.
445 for (old_names.keys()) |name_ip| {445 for (old_names.keys()) |name_ip| {
446 any_change = true;446 any_change = true;
447 try zcu.markDependeeOutdated(.{ .namespace_name = .{447 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
448 .namespace = tracked_inst_index,448 .namespace = tracked_inst_index,
449 .name = name_ip,449 .name = name_ip,
450 } });450 } });
451 }451 }
452452
453 if (any_change) {453 if (any_change) {
454 try zcu.markDependeeOutdated(.{ .namespace = tracked_inst_index });454 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace = tracked_inst_index });
455 }455 }
456 }456 }
457 }457 }
...@@ -508,7 +508,7 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -508,7 +508,7 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
508 const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });508 const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });
509 const cau = ip.getCau(cau_index);509 const cau = ip.getCau(cau_index);
510510
511 log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});511 //log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});
512512
513 assert(!zcu.analysis_in_progress.contains(anal_unit));513 assert(!zcu.analysis_in_progress.contains(anal_unit));
514514
...@@ -527,8 +527,91 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -527,8 +527,91 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
527527
528 if (cau_outdated) {528 if (cau_outdated) {
529 _ = zcu.outdated_ready.swapRemove(anal_unit);529 _ = zcu.outdated_ready.swapRemove(anal_unit);
530 } else {
531 // We can trust the current information about this `Cau`.
532 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
533 return error.AnalysisFail;
534 }
535 // If it wasn't failed and wasn't marked outdated, then either...
536 // * it is a type and is up-to-date, or
537 // * it is a `comptime` decl and is up-to-date, or
538 // * it is another decl and is EITHER up-to-date OR never-referenced (so unresolved)
539 // We just need to check for that last case.
540 switch (cau.owner.unwrap()) {
541 .type, .none => return,
542 .nav => |nav| if (ip.getNav(nav).status == .resolved) return,
543 }
544 }
545
546 const sema_result: SemaCauResult, const analysis_fail = if (pt.ensureCauAnalyzedInner(cau_index, cau_outdated)) |result|
547 .{ result, false }
548 else |err| switch (err) {
549 error.AnalysisFail => res: {
550 if (!zcu.failed_analysis.contains(anal_unit)) {
551 // If this `Cau` caused the error, it would have an entry in `failed_analysis`.
552 // Since it does not, this must be a transitive failure.
553 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
554 }
555 // We treat errors as up-to-date, since those uses would just trigger a transitive error
556 break :res .{ .{
557 .invalidate_decl_val = false,
558 .invalidate_decl_ref = false,
559 }, true };
560 },
561 error.OutOfMemory => res: {
562 try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
563 try zcu.retryable_failures.ensureUnusedCapacity(gpa, 1);
564 const msg = try Zcu.ErrorMsg.create(
565 gpa,
566 .{ .base_node_inst = cau.zir_index, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(0) },
567 "unable to analyze: OutOfMemory",
568 .{},
569 );
570 zcu.retryable_failures.appendAssumeCapacity(anal_unit);
571 zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, msg);
572 // We treat errors as up-to-date, since those uses would just trigger a transitive error
573 break :res .{ .{
574 .invalidate_decl_val = false,
575 .invalidate_decl_ref = false,
576 }, true };
577 },
578 };
579
580 if (cau_outdated) {
581 // TODO: we do not yet have separate dependencies for decl values vs types.
582 const invalidate = sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref;
583 const dependee: InternPool.Dependee = switch (cau.owner.unwrap()) {
584 .none => return, // there are no dependencies on a `comptime` decl!
585 .nav => |nav_index| .{ .nav_val = nav_index },
586 .type => |ty| .{ .interned = ty },
587 };
588
589 if (invalidate) {
590 // This dependency was marked as PO, meaning dependees were waiting
591 // on its analysis result, and it has turned out to be outdated.
592 // Update dependees accordingly.
593 try zcu.markDependeeOutdated(.marked_po, dependee);
594 } else {
595 // This dependency was previously PO, but turned out to be up-to-date.
596 // We do not need to queue successive analysis.
597 try zcu.markPoDependeeUpToDate(dependee);
598 }
530 }599 }
531600
601 if (analysis_fail) return error.AnalysisFail;
602}
603
604fn ensureCauAnalyzedInner(
605 pt: Zcu.PerThread,
606 cau_index: InternPool.Cau.Index,
607 cau_outdated: bool,
608) Zcu.SemaError!SemaCauResult {
609 const zcu = pt.zcu;
610 const ip = &zcu.intern_pool;
611
612 const cau = ip.getCau(cau_index);
613 const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });
614
532 const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;615 const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
533616
534 // TODO: document this elsewhere mlugg!617 // TODO: document this elsewhere mlugg!
...@@ -550,22 +633,6 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -550,22 +633,6 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
550 return error.AnalysisFail;633 return error.AnalysisFail;
551 }634 }
552635
553 if (!cau_outdated) {
554 // We can trust the current information about this `Cau`.
555 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
556 return error.AnalysisFail;
557 }
558 // If it wasn't failed and wasn't marked outdated, then either...
559 // * it is a type and is up-to-date, or
560 // * it is a `comptime` decl and is up-to-date, or
561 // * it is another decl and is EITHER up-to-date OR never-referenced (so unresolved)
562 // We just need to check for that last case.
563 switch (cau.owner.unwrap()) {
564 .type, .none => return,
565 .nav => |nav| if (ip.getNav(nav).status == .resolved) return,
566 }
567 }
568
569 // `cau_outdated` can be true in the initial update for `comptime` declarations,636 // `cau_outdated` can be true in the initial update for `comptime` declarations,
570 // so this isn't a `dev.check`.637 // so this isn't a `dev.check`.
571 if (cau_outdated and dev.env.supports(.incremental)) {638 if (cau_outdated and dev.env.supports(.incremental)) {
...@@ -573,76 +640,34 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu...@@ -573,76 +640,34 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
573 // prior to re-analysis.640 // prior to re-analysis.
574 zcu.deleteUnitExports(anal_unit);641 zcu.deleteUnitExports(anal_unit);
575 zcu.deleteUnitReferences(anal_unit);642 zcu.deleteUnitReferences(anal_unit);
576 }643 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
577644 kv.value.destroy(zcu.gpa);
578 const sema_result: SemaCauResult = res: {
579 if (inst_info.inst == .main_struct_inst) {
580 // Note that this is definitely a *recreation* due to outdated, because
581 // this instruction indicates that `cau.owner` is a `type`, which only
582 // reaches here if `cau_outdated`.
583 try pt.recreateFileRoot(inst_info.file);
584 break :res .{
585 .invalidate_decl_val = true,
586 .invalidate_decl_ref = true,
587 };
588 }645 }
646 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
647 }
589648
590 const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) {649 if (inst_info.inst == .main_struct_inst) {
591 .nav => |nav| ip.getNav(nav).fqn.toSlice(ip),650 // Note that this is definitely a *recreation* due to outdated, because
592 .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),651 // this instruction indicates that `cau.owner` is a `type`, which only
593 .none => "comptime",652 // reaches here if `cau_outdated`.
594 }, 0);653 try pt.recreateFileRoot(inst_info.file);
595 defer decl_prog_node.end();654 return .{
596655 .invalidate_decl_val = true,
597 break :res pt.semaCau(cau_index) catch |err| switch (err) {656 .invalidate_decl_ref = true,
598 error.AnalysisFail => {
599 if (!zcu.failed_analysis.contains(anal_unit)) {
600 // If this `Cau` caused the error, it would have an entry in `failed_analysis`.
601 // Since it does not, this must be a transitive failure.
602 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
603 }
604 return error.AnalysisFail;
605 },
606 error.GenericPoison => unreachable,
607 error.ComptimeBreak => unreachable,
608 error.ComptimeReturn => unreachable,
609 error.OutOfMemory => {
610 try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
611 try zcu.retryable_failures.append(gpa, anal_unit);
612 zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, try Zcu.ErrorMsg.create(
613 gpa,
614 .{ .base_node_inst = cau.zir_index, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(0) },
615 "unable to analyze: OutOfMemory",
616 .{},
617 ));
618 return error.AnalysisFail;
619 },
620 };657 };
621 };
622
623 if (!cau_outdated) {
624 // We definitely don't need to do any dependency tracking, so our work is done.
625 return;
626 }658 }
627659
628 // TODO: we do not yet have separate dependencies for decl values vs types.660 const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) {
629 const invalidate = sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref;661 .nav => |nav| ip.getNav(nav).fqn.toSlice(ip),
630 const dependee: InternPool.Dependee = switch (cau.owner.unwrap()) {662 .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
631 .none => return, // there are no dependencies on a `comptime` decl!663 .none => "comptime",
632 .nav => |nav_index| .{ .nav_val = nav_index },664 }, 0);
633 .type => |ty| .{ .interned = ty },665 defer decl_prog_node.end();
634 };
635666
636 if (invalidate) {667 return pt.semaCau(cau_index) catch |err| switch (err) {
637 // This dependency was marked as PO, meaning dependees were waiting668 error.GenericPoison, error.ComptimeBreak, error.ComptimeReturn => unreachable,
638 // on its analysis result, and it has turned out to be outdated.669 error.AnalysisFail, error.OutOfMemory => |e| return e,
639 // Update dependees accordingly.670 };
640 try zcu.markDependeeOutdated(dependee);
641 } else {
642 // This dependency was previously PO, but turned out to be up-to-date.
643 // We do not need to queue successive analysis.
644 try zcu.markPoDependeeUpToDate(dependee);
645 }
646}671}
647672
648pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {673pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
...@@ -660,7 +685,64 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -660,7 +685,64 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
660685
661 const func = zcu.funcInfo(maybe_coerced_func_index);686 const func = zcu.funcInfo(maybe_coerced_func_index);
662687
663 log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});688 //log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});
689
690 const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });
691 const func_outdated = zcu.outdated.swapRemove(anal_unit) or
692 zcu.potentially_outdated.swapRemove(anal_unit);
693
694 if (func_outdated) {
695 _ = zcu.outdated_ready.swapRemove(anal_unit);
696 } else {
697 // We can trust the current information about this function.
698 if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
699 return error.AnalysisFail;
700 }
701 switch (func.analysisUnordered(ip).state) {
702 .unreferenced => {}, // this is the first reference
703 .queued => {}, // we're waiting on first-time analysis
704 .analyzed => return, // up-to-date
705 }
706 }
707
708 const ies_outdated, const analysis_fail = if (pt.ensureFuncBodyAnalyzedInner(func_index, func_outdated)) |result|
709 .{ result.ies_outdated, false }
710 else |err| switch (err) {
711 error.AnalysisFail => res: {
712 if (!zcu.failed_analysis.contains(anal_unit)) {
713 // If this function caused the error, it would have an entry in `failed_analysis`.
714 // Since it does not, this must be a transitive failure.
715 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
716 }
717 break :res .{ false, true }; // we treat errors as up-to-date IES, since those uses would just trigger a transitive error
718 },
719 error.OutOfMemory => return error.OutOfMemory, // TODO: graceful handling like `ensureCauAnalyzed`
720 };
721
722 if (func_outdated) {
723 if (ies_outdated) {
724 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
725 try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
726 } else {
727 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
728 try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
729 }
730 }
731
732 if (analysis_fail) return error.AnalysisFail;
733}
734
735fn ensureFuncBodyAnalyzedInner(
736 pt: Zcu.PerThread,
737 func_index: InternPool.Index,
738 func_outdated: bool,
739) Zcu.SemaError!struct { ies_outdated: bool } {
740 const zcu = pt.zcu;
741 const gpa = zcu.gpa;
742 const ip = &zcu.intern_pool;
743
744 const func = zcu.funcInfo(func_index);
745 const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });
664746
665 // Here's an interesting question: is this function actually valid?747 // Here's an interesting question: is this function actually valid?
666 // Maybe the signature changed, so we'll end up creating a whole different `func`748 // Maybe the signature changed, so we'll end up creating a whole different `func`
...@@ -681,7 +763,9 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -681,7 +763,9 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
681 });763 });
682764
683 if (ip.isRemoved(func_index) or (func.generic_owner != .none and ip.isRemoved(func.generic_owner))) {765 if (ip.isRemoved(func_index) or (func.generic_owner != .none and ip.isRemoved(func.generic_owner))) {
684 try zcu.markDependeeOutdated(.{ .interned = func_index }); // IES766 if (func_outdated) {
767 try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index }); // IES
768 }
685 ip.removeDependenciesForDepender(gpa, InternPool.AnalUnit.wrap(.{ .func = func_index }));769 ip.removeDependenciesForDepender(gpa, InternPool.AnalUnit.wrap(.{ .func = func_index }));
686 ip.remove(pt.tid, func_index);770 ip.remove(pt.tid, func_index);
687 @panic("TODO: remove orphaned function from binary");771 @panic("TODO: remove orphaned function from binary");
...@@ -694,15 +778,14 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -694,15 +778,14 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
694 else778 else
695 .none;779 .none;
696780
697 const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });
698 const func_outdated = zcu.outdated.swapRemove(anal_unit) or
699 zcu.potentially_outdated.swapRemove(anal_unit);
700
701 if (func_outdated) {781 if (func_outdated) {
702 _ = zcu.outdated_ready.swapRemove(anal_unit);
703 dev.check(.incremental);782 dev.check(.incremental);
704 zcu.deleteUnitExports(anal_unit);783 zcu.deleteUnitExports(anal_unit);
705 zcu.deleteUnitReferences(anal_unit);784 zcu.deleteUnitReferences(anal_unit);
785 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
786 kv.value.destroy(gpa);
787 }
788 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
706 }789 }
707790
708 if (!func_outdated) {791 if (!func_outdated) {
...@@ -713,7 +796,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -713,7 +796,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
713 switch (func.analysisUnordered(ip).state) {796 switch (func.analysisUnordered(ip).state) {
714 .unreferenced => {}, // this is the first reference797 .unreferenced => {}, // this is the first reference
715 .queued => {}, // we're waiting on first-time analysis798 .queued => {}, // we're waiting on first-time analysis
716 .analyzed => return, // up-to-date799 .analyzed => return .{ .ies_outdated = false }, // up-to-date
717 }800 }
718 }801 }
719802
...@@ -722,28 +805,11 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -722,28 +805,11 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
722 if (func_outdated) "outdated" else "never analyzed",805 if (func_outdated) "outdated" else "never analyzed",
723 });806 });
724807
725 var air = pt.analyzeFnBody(func_index) catch |err| switch (err) {808 var air = try pt.analyzeFnBody(func_index);
726 error.AnalysisFail => {
727 if (!zcu.failed_analysis.contains(anal_unit)) {
728 // If this function caused the error, it would have an entry in `failed_analysis`.
729 // Since it does not, this must be a transitive failure.
730 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
731 }
732 return error.AnalysisFail;
733 },
734 error.OutOfMemory => return error.OutOfMemory,
735 };
736 errdefer air.deinit(gpa);809 errdefer air.deinit(gpa);
737810
738 if (func_outdated) {811 const ies_outdated = func_outdated and
739 if (!func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies) {812 (!func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies);
740 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
741 try zcu.markDependeeOutdated(.{ .interned = func_index });
742 } else {
743 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
744 try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
745 }
746 }
747813
748 const comp = zcu.comp;814 const comp = zcu.comp;
749815
...@@ -752,13 +818,15 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -752,13 +818,15 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
752818
753 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {819 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
754 air.deinit(gpa);820 air.deinit(gpa);
755 return;821 return .{ .ies_outdated = ies_outdated };
756 }822 }
757823
758 try comp.queueJob(.{ .codegen_func = .{824 try comp.queueJob(.{ .codegen_func = .{
759 .func = func_index,825 .func = func_index,
760 .air = air,826 .air = air,
761 } });827 } });
828
829 return .{ .ies_outdated = ies_outdated };
762}830}
763831
764/// Takes ownership of `air`, even on error.832/// Takes ownership of `air`, even on error.
...@@ -1935,6 +2003,8 @@ const ScanDeclIter = struct {...@@ -1935,6 +2003,8 @@ const ScanDeclIter = struct {
1935 .@"comptime" => cau: {2003 .@"comptime" => cau: {
1936 const cau = existing_cau orelse try ip.createComptimeCau(gpa, pt.tid, tracked_inst, namespace_index);2004 const cau = existing_cau orelse try ip.createComptimeCau(gpa, pt.tid, tracked_inst, namespace_index);
19372005
2006 try namespace.other_decls.append(gpa, cau);
2007
1938 // For a `comptime` declaration, whether to re-analyze is based solely on whether the2008 // For a `comptime` declaration, whether to re-analyze is based solely on whether the
1939 // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already.2009 // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already.
1940 const unit = InternPool.AnalUnit.wrap(.{ .cau = cau });2010 const unit = InternPool.AnalUnit.wrap(.{ .cau = cau });