| author | |
| committer | |
| log | aa6c1c40ec29d581844ebb5db09a33453c76d4ba |
| tree | 70206cdc7f9ce4adf14b4c98af6f9a7ce6a9add0 |
| parent | 6faa4cc7e60c2ecd26759878a6f9e277d69a4968 |
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 | 2300 | zcu.intern_pool.dumpGenericInstances(gpa); |
| 2301 | 2301 | } |
| 2302 | 2302 | |
| 2303 | if (comp.config.is_test and comp.totalErrorCount() == 0) { | |
| 2303 | if (comp.config.is_test and try comp.totalErrorCount() == 0) { | |
| 2304 | 2304 | // The `test_functions` decl has been intentionally postponed until now, |
| 2305 | 2305 | // at which point we must populate it with the list of test functions that |
| 2306 | 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 | 2310 | try pt.processExports(); |
| 2311 | 2311 | } |
| 2312 | 2312 | |
| 2313 | if (comp.totalErrorCount() != 0) { | |
| 2313 | if (try comp.totalErrorCount() != 0) { | |
| 2314 | 2314 | // Skip flushing and keep source files loaded for error reporting. |
| 2315 | 2315 | comp.link_error_flags = .{}; |
| 2316 | 2316 | return; |
| ... | ... | @@ -2394,7 +2394,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2394 | 2394 | } |
| 2395 | 2395 | |
| 2396 | 2396 | try flush(comp, arena, .main, main_progress_node); |
| 2397 | if (comp.totalErrorCount() != 0) return; | |
| 2397 | if (try comp.totalErrorCount() != 0) return; | |
| 2398 | 2398 | |
| 2399 | 2399 | // Failure here only means an unnecessary cache miss. |
| 2400 | 2400 | man.writeManifest() catch |err| { |
| ... | ... | @@ -2411,7 +2411,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2411 | 2411 | }, |
| 2412 | 2412 | .incremental => { |
| 2413 | 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 | 3048 | } |
| 3049 | 3049 | |
| 3050 | 3050 | /// This function is temporally single-threaded. |
| 3051 | pub fn totalErrorCount(comp: *Compilation) u32 { | |
| 3051 | pub fn totalErrorCount(comp: *Compilation) Allocator.Error!u32 { | |
| 3052 | 3052 | var total: usize = |
| 3053 | 3053 | comp.misc_failures.count() + |
| 3054 | 3054 | @intFromBool(comp.alloc_failure_occurred) + |
| ... | ... | @@ -3088,7 +3088,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 { |
| 3088 | 3088 | // the previous parse success, including compile errors, but we cannot |
| 3089 | 3089 | // emit them until the file succeeds parsing. |
| 3090 | 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 | 3092 | const file_index = switch (anal_unit.unwrap()) { |
| 3093 | 3093 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, |
| 3094 | 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 | 3225 | if (err) |e| return e; |
| 3226 | 3226 | } |
| 3227 | 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; | |
| 3229 | 3229 | |
| 3230 | 3230 | const file_index = switch (anal_unit.unwrap()) { |
| 3231 | 3231 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, |
| ... | ... | @@ -3341,10 +3341,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3341 | 3341 | } |
| 3342 | 3342 | } |
| 3343 | 3343 | |
| 3344 | assert(comp.totalErrorCount() == bundle.root_list.items.len); | |
| 3344 | assert(try comp.totalErrorCount() == bundle.root_list.items.len); | |
| 3345 | 3345 | |
| 3346 | 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 | 3348 | const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| { |
| 3349 | 3349 | if (all_references.contains(failed_unit)) break true; |
| 3350 | 3350 | } else false; |
| ... | ... | @@ -3448,6 +3448,7 @@ pub fn addModuleErrorMsg( |
| 3448 | 3448 | const span = try src.span(gpa); |
| 3449 | 3449 | const loc = std.zig.findLineColumn(source.bytes, span.main); |
| 3450 | 3450 | const rt_file_path = try src.file_scope.fullPath(gpa); |
| 3451 | defer gpa.free(rt_file_path); | |
| 3451 | 3452 | const name = switch (ref.referencer.unwrap()) { |
| 3452 | 3453 | .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) { |
| 3453 | 3454 | .nav => |nav| ip.getNav(nav).name.toSlice(ip), |
src/Sema.zig+25-25| ... | ... | @@ -112,6 +112,11 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{}, |
| 112 | 112 | references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, |
| 113 | 113 | type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, |
| 114 | 114 | |
| 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. | |
| 118 | dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .{}, | |
| 119 | ||
| 115 | 120 | const MaybeComptimeAlloc = struct { |
| 116 | 121 | /// The runtime index of the `alloc` instruction. |
| 117 | 122 | runtime_index: Value.RuntimeIndex, |
| ... | ... | @@ -879,6 +884,7 @@ pub fn deinit(sema: *Sema) void { |
| 879 | 884 | sema.exports.deinit(gpa); |
| 880 | 885 | sema.references.deinit(gpa); |
| 881 | 886 | sema.type_references.deinit(gpa); |
| 887 | sema.dependencies.deinit(gpa); | |
| 882 | 888 | sema.* = undefined; |
| 883 | 889 | } |
| 884 | 890 | |
| ... | ... | @@ -2740,7 +2746,7 @@ fn maybeRemoveOutdatedType(sema: *Sema, ty: InternPool.Index) !bool { |
| 2740 | 2746 | _ = zcu.outdated_ready.swapRemove(cau_unit); |
| 2741 | 2747 | zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, cau_unit); |
| 2742 | 2748 | zcu.intern_pool.remove(pt.tid, ty); |
| 2743 | try zcu.markDependeeOutdated(.{ .interned = ty }); | |
| 2749 | try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty }); | |
| 2744 | 2750 | return true; |
| 2745 | 2751 | } |
| 2746 | 2752 | |
| ... | ... | @@ -6066,7 +6072,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr |
| 6066 | 6072 | // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to |
| 6067 | 6073 | // trigger re-analysis later. |
| 6068 | 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 | } |
| 6071 | 6079 | |
| 6072 | 6080 | fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -6820,6 +6828,13 @@ fn lookupInNamespace( |
| 6820 | 6828 | |
| 6821 | 6829 | const src_file = zcu.namespacePtr(block.namespace).file_scope; |
| 6822 | 6830 | |
| 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 | 6838 | if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) { |
| 6824 | 6839 | const gpa = sema.gpa; |
| 6825 | 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 | 13996 | }); |
| 13982 | 13997 | |
| 13983 | 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 | } | |
| 13990 | 13999 | |
| 13991 | 14000 | const namespace = container_type.getNamespace(mod).unwrap() orelse return .bool_false; |
| 13992 | 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 | 14035 | // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to |
| 14027 | 14036 | // trigger re-analysis later. |
| 14028 | 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 | } |
| 14031 | 14042 | |
| 14032 | 14043 | fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -27696,13 +27707,6 @@ fn fieldVal( |
| 27696 | 27707 | const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; |
| 27697 | 27708 | const child_type = val.toType(); |
| 27698 | 27709 | |
| 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 | 27710 | switch (try child_type.zigTypeTagOrPoison(mod)) { |
| 27707 | 27711 | .ErrorSet => { |
| 27708 | 27712 | switch (ip.indexToKey(child_type.toIntern())) { |
| ... | ... | @@ -27934,13 +27938,6 @@ fn fieldPtr( |
| 27934 | 27938 | const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; |
| 27935 | 27939 | const child_type = val.toType(); |
| 27936 | 27940 | |
| 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 | 27941 | switch (child_type.zigTypeTag(mod)) { |
| 27945 | 27942 | .ErrorSet => { |
| 27946 | 27943 | switch (ip.indexToKey(child_type.toIntern())) { |
| ... | ... | @@ -32260,7 +32257,7 @@ fn addReferenceEntry( |
| 32260 | 32257 | referenced_unit: AnalUnit, |
| 32261 | 32258 | ) !void { |
| 32262 | 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 | 32261 | const gop = try sema.references.getOrPut(sema.gpa, referenced_unit); |
| 32265 | 32262 | if (gop.found_existing) return; |
| 32266 | 32263 | // TODO: we need to figure out how to model inline calls here. |
| ... | ... | @@ -32275,7 +32272,7 @@ fn addTypeReferenceEntry( |
| 32275 | 32272 | referenced_type: InternPool.Index, |
| 32276 | 32273 | ) !void { |
| 32277 | 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 | 32276 | const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type); |
| 32280 | 32277 | if (gop.found_existing) return; |
| 32281 | 32278 | try zcu.addTypeReference(sema.owner, referenced_type, src); |
| ... | ... | @@ -38272,6 +38269,9 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { |
| 38272 | 38269 | const zcu = sema.pt.zcu; |
| 38273 | 38270 | if (!zcu.comp.incremental) return; |
| 38274 | 38271 | |
| 38272 | const gop = try sema.dependencies.getOrPut(sema.gpa, dependee); | |
| 38273 | if (gop.found_existing) return; | |
| 38274 | ||
| 38275 | 38275 | // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields |
| 38276 | 38276 | // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would |
| 38277 | 38277 | // 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 | 10 | const mem = std.mem; |
| 11 | 11 | const Allocator = std.mem.Allocator; |
| 12 | 12 | const assert = std.debug.assert; |
| 13 | const log = std.log.scoped(.module); | |
| 13 | const log = std.log.scoped(.zcu); | |
| 14 | 14 | const BigIntConst = std.math.big.int.Const; |
| 15 | 15 | const BigIntMutable = std.math.big.int.Mutable; |
| 16 | 16 | const Target = std.Target; |
| ... | ... | @@ -153,9 +153,11 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = . |
| 153 | 153 | /// Maximum amount of distinct error values, set by --error-limit |
| 154 | 154 | error_limit: ErrorInt, |
| 155 | 155 | |
| 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`. | |
| 157 | 159 | potentially_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 | 161 | /// Once this value drops to 0, the AnalUnit is a candidate for re-analysis. |
| 160 | 162 | outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, |
| 161 | 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 | 2278 | return zir; |
| 2277 | 2279 | } |
| 2278 | 2280 | |
| 2279 | pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { | |
| 2280 | log.debug("outdated dependee: {}", .{dependee}); | |
| 2281 | pub fn markDependeeOutdated( | |
| 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 | 2290 | var it = zcu.intern_pool.dependencyIterator(dependee); |
| 2282 | 2291 | while (it.next()) |depender| { |
| 2283 | if (zcu.outdated.contains(depender)) { | |
| 2284 | // We do not need to increment the PO dep count, as if the outdated | |
| 2285 | // dependee is a Decl, we had already marked this as PO. | |
| 2292 | if (zcu.outdated.getPtr(depender)) |po_dep_count| { | |
| 2293 | switch (marked_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 | 2304 | continue; |
| 2287 | 2305 | } |
| 2288 | 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 | 2316 | try zcu.outdated.putNoClobber( |
| 2290 | 2317 | zcu.gpa, |
| 2291 | 2318 | depender, |
| 2292 | // We do not need to increment this count for the same reason as above. | |
| 2293 | if (opt_po_entry) |e| e.value else 0, | |
| 2319 | new_po_dep_count, | |
| 2294 | 2320 | ); |
| 2295 | log.debug("outdated: {}", .{depender}); | |
| 2296 | if (opt_po_entry == null) { | |
| 2297 | // This is a new entry with no PO dependencies. | |
| 2321 | log.debug("outdated: {}", .{fmtAnalUnit(depender, zcu)}); | |
| 2322 | if (new_po_dep_count == 0) { | |
| 2323 | log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)}); | |
| 2298 | 2324 | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| 2299 | 2325 | } |
| 2300 | 2326 | // If this is a Decl and was not previously PO, we must recursively |
| 2301 | 2327 | // mark dependencies on its tyval as PO. |
| 2302 | 2328 | if (opt_po_entry == null) { |
| 2329 | assert(marked_po == .not_marked_po); | |
| 2303 | 2330 | try zcu.markTransitiveDependersPotentiallyOutdated(depender); |
| 2304 | 2331 | } |
| 2305 | 2332 | } |
| 2306 | 2333 | } |
| 2307 | 2334 | |
| 2308 | 2335 | pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 2336 | log.debug("up-to-date dependee: {}", .{fmtDependee(dependee, zcu)}); | |
| 2309 | 2337 | var it = zcu.intern_pool.dependencyIterator(dependee); |
| 2310 | 2338 | while (it.next()) |depender| { |
| 2311 | 2339 | if (zcu.outdated.getPtr(depender)) |po_dep_count| { |
| 2312 | 2340 | // This depender is already outdated, but it now has one |
| 2313 | 2341 | // less PO dependency! |
| 2314 | 2342 | po_dep_count.* -= 1; |
| 2343 | log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), po_dep_count.* }); | |
| 2315 | 2344 | if (po_dep_count.* == 0) { |
| 2345 | log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)}); | |
| 2316 | 2346 | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| 2317 | 2347 | } |
| 2318 | 2348 | continue; |
| 2319 | 2349 | } |
| 2320 | 2350 | // This depender is definitely at least PO, because this Decl was just analyzed |
| 2321 | 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 | 2357 | if (ptr.* > 1) { |
| 2324 | 2358 | ptr.* -= 1; |
| 2359 | log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), ptr.* }); | |
| 2325 | 2360 | continue; |
| 2326 | 2361 | } |
| 2327 | 2362 | |
| 2363 | log.debug("up-to-date (po deps = 0): {}", .{fmtAnalUnit(depender, zcu)}); | |
| 2364 | ||
| 2328 | 2365 | // This dependency is no longer PO, i.e. is known to be up-to-date. |
| 2329 | 2366 | assert(zcu.potentially_outdated.swapRemove(depender)); |
| 2330 | 2367 | // 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 | 2381 | /// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES. |
| 2345 | 2382 | fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void { |
| 2346 | 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 | 2385 | .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) { |
| 2349 | 2386 | .nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced |
| 2350 | .none, .type => return, // analysis of this `Cau` can't outdate any dependencies | |
| 2387 | .type => |ty| .{ .interned = ty }, | |
| 2388 | .none => return, // analysis of this `Cau` can't outdate any dependencies | |
| 2351 | 2389 | }, |
| 2352 | 2390 | .func => |func_index| .{ .interned = func_index }, // IES |
| 2353 | }); | |
| 2354 | ||
| 2391 | }; | |
| 2392 | log.debug("marking dependee po: {}", .{fmtDependee(dependee, zcu)}); | |
| 2393 | var it = ip.dependencyIterator(dependee); | |
| 2355 | 2394 | while (it.next()) |po| { |
| 2356 | 2395 | if (zcu.outdated.getPtr(po)) |po_dep_count| { |
| 2357 | 2396 | // This dependency is already outdated, but it now has one more PO |
| ... | ... | @@ -2360,14 +2399,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni |
| 2360 | 2399 | _ = zcu.outdated_ready.swapRemove(po); |
| 2361 | 2400 | } |
| 2362 | 2401 | po_dep_count.* += 1; |
| 2402 | log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), po_dep_count.* }); | |
| 2363 | 2403 | continue; |
| 2364 | 2404 | } |
| 2365 | 2405 | if (zcu.potentially_outdated.getPtr(po)) |n| { |
| 2366 | 2406 | // There is now one more PO dependency. |
| 2367 | 2407 | n.* += 1; |
| 2408 | log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), n.* }); | |
| 2368 | 2409 | continue; |
| 2369 | 2410 | } |
| 2370 | 2411 | try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); |
| 2412 | log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), 1 }); | |
| 2371 | 2413 | // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO. |
| 2372 | 2414 | try zcu.markTransitiveDependersPotentiallyOutdated(po); |
| 2373 | 2415 | } |
| ... | ... | @@ -2391,13 +2433,9 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { |
| 2391 | 2433 | // In this case, we must defer to more complex logic below. |
| 2392 | 2434 | |
| 2393 | 2435 | if (zcu.outdated_ready.count() > 0) { |
| 2394 | log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{ | |
| 2395 | @tagName(zcu.outdated_ready.keys()[0].unwrap()), | |
| 2396 | switch (zcu.outdated_ready.keys()[0].unwrap()) { | |
| 2397 | inline else => |x| @intFromEnum(x), | |
| 2398 | }, | |
| 2399 | }); | |
| 2400 | return zcu.outdated_ready.keys()[0]; | |
| 2436 | const unit = zcu.outdated_ready.keys()[0]; | |
| 2437 | log.debug("findOutdatedToAnalyze: trivial {}", .{fmtAnalUnit(unit, zcu)}); | |
| 2438 | return unit; | |
| 2401 | 2439 | } |
| 2402 | 2440 | |
| 2403 | 2441 | // 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 | 2483 | } |
| 2446 | 2484 | } |
| 2447 | 2485 | |
| 2448 | log.debug("findOutdatedToAnalyze: heuristic returned Cau {d} ({d} dependers)", .{ | |
| 2449 | @intFromEnum(chosen_cau.?), | |
| 2486 | if (chosen_cau == null) { | |
| 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 | 2496 | chosen_cau_dependers, |
| 2451 | 2497 | }); |
| 2452 | 2498 | |
| ... | ... | @@ -3090,7 +3136,6 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve |
| 3090 | 3136 | }); |
| 3091 | 3137 | defer gpa.free(resolved_path); |
| 3092 | 3138 | const file = zcu.import_table.get(resolved_path).?; |
| 3093 | if (zcu.fileByIndex(file).status != .success_zir) continue; | |
| 3094 | 3139 | const root_ty = zcu.fileRootType(file); |
| 3095 | 3140 | if (root_ty == .none) continue; |
| 3096 | 3141 | type_queue.putAssumeCapacityNoClobber(root_ty, null); |
| ... | ... | @@ -3102,6 +3147,8 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve |
| 3102 | 3147 | const referencer = kv.value; |
| 3103 | 3148 | try checked_types.putNoClobber(gpa, ty, {}); |
| 3104 | 3149 | |
| 3150 | log.debug("handle type '{}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}); | |
| 3151 | ||
| 3105 | 3152 | // If this type has a `Cau` for resolution, it's automatically referenced. |
| 3106 | 3153 | const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) { |
| 3107 | 3154 | .struct_type => ip.loadStructType(ty).cau, |
| ... | ... | @@ -3132,13 +3179,14 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve |
| 3132 | 3179 | |
| 3133 | 3180 | // Queue any decls within this type which would be automatically analyzed. |
| 3134 | 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 | 3183 | for (zcu.namespacePtr(ns).other_decls.items) |cau| { |
| 3137 | 3184 | // These are `comptime` and `test` declarations. |
| 3138 | 3185 | // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter. |
| 3139 | 3186 | const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue; |
| 3140 | 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 | 3190 | const declaration = zir.getDeclaration(inst_info.inst)[0]; |
| 3143 | 3191 | const want_analysis = switch (declaration.name) { |
| 3144 | 3192 | .@"usingnamespace" => unreachable, |
| ... | ... | @@ -3158,27 +3206,51 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve |
| 3158 | 3206 | }; |
| 3159 | 3207 | if (want_analysis) { |
| 3160 | 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 | 3218 | for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| { |
| 3165 | 3219 | // These are named declarations. They are analyzed only if marked `export`. |
| 3166 | 3220 | const cau = ip.getNav(nav).analysis_owner.unwrap().?; |
| 3167 | 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 | 3226 | if (declaration.flags.is_export) { |
| 3170 | 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 | 3237 | for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| { |
| 3175 | 3238 | // These are named declarations. They are analyzed only if marked `export`. |
| 3176 | 3239 | const cau = ip.getNav(nav).analysis_owner.unwrap().?; |
| 3177 | 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 | 3245 | if (declaration.flags.is_export) { |
| 3180 | 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 | 3256 | // Incremental compilation does not support `usingnamespace`. |
| ... | ... | @@ -3199,15 +3271,23 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve |
| 3199 | 3271 | const unit = kv.key; |
| 3200 | 3272 | try result.putNoClobber(gpa, unit, kv.value); |
| 3201 | 3273 | |
| 3274 | log.debug("handle unit '{}'", .{fmtAnalUnit(unit, zcu)}); | |
| 3275 | ||
| 3202 | 3276 | if (zcu.reference_table.get(unit)) |first_ref_idx| { |
| 3203 | 3277 | assert(first_ref_idx != std.math.maxInt(u32)); |
| 3204 | 3278 | var ref_idx = first_ref_idx; |
| 3205 | 3279 | while (ref_idx != std.math.maxInt(u32)) { |
| 3206 | 3280 | const ref = zcu.all_references.items[ref_idx]; |
| 3207 | if (!result.contains(ref.referenced)) try unit_queue.put(gpa, ref.referenced, .{ | |
| 3208 | .referencer = unit, | |
| 3209 | .src = ref.src, | |
| 3210 | }); | |
| 3281 | if (!result.contains(ref.referenced)) { | |
| 3282 | log.debug("unit '{}': ref unit '{}'", .{ | |
| 3283 | fmtAnalUnit(unit, zcu), | |
| 3284 | fmtAnalUnit(ref.referenced, zcu), | |
| 3285 | }); | |
| 3286 | try unit_queue.put(gpa, ref.referenced, .{ | |
| 3287 | .referencer = unit, | |
| 3288 | .src = ref.src, | |
| 3289 | }); | |
| 3290 | } | |
| 3211 | 3291 | ref_idx = ref.next; |
| 3212 | 3292 | } |
| 3213 | 3293 | } |
| ... | ... | @@ -3216,10 +3296,16 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve |
| 3216 | 3296 | var ref_idx = first_ref_idx; |
| 3217 | 3297 | while (ref_idx != std.math.maxInt(u32)) { |
| 3218 | 3298 | const ref = zcu.all_type_references.items[ref_idx]; |
| 3219 | if (!checked_types.contains(ref.referenced)) try type_queue.put(gpa, ref.referenced, .{ | |
| 3220 | .referencer = unit, | |
| 3221 | .src = ref.src, | |
| 3222 | }); | |
| 3299 | if (!checked_types.contains(ref.referenced)) { | |
| 3300 | log.debug("unit '{}': ref type '{}'", .{ | |
| 3301 | fmtAnalUnit(unit, zcu), | |
| 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 | 3309 | ref_idx = ref.next; |
| 3224 | 3310 | } |
| 3225 | 3311 | } |
| ... | ... | @@ -3293,3 +3379,72 @@ pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File { |
| 3293 | 3379 | const file_index = ip.getCau(cau).zir_index.resolveFile(ip); |
| 3294 | 3380 | return zcu.fileByIndex(file_index); |
| 3295 | 3381 | } |
| 3382 | ||
| 3383 | fn fmtAnalUnit(unit: AnalUnit, zcu: *Zcu) std.fmt.Formatter(formatAnalUnit) { | |
| 3384 | return .{ .data = .{ .unit = unit, .zcu = zcu } }; | |
| 3385 | } | |
| 3386 | fn fmtDependee(d: InternPool.Dependee, zcu: *Zcu) std.fmt.Formatter(formatDependee) { | |
| 3387 | return .{ .data = .{ .dependee = d, .zcu = zcu } }; | |
| 3388 | } | |
| 3389 | ||
| 3390 | fn 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 | } | |
| 3414 | fn 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 | 360 | // Tracking failed for this instruction. Invalidate associated `src_hash` deps. |
| 361 | 361 | log.debug("tracking failed for %{d}", .{old_inst}); |
| 362 | 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 | 364 | continue; |
| 365 | 365 | }; |
| 366 | 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 | 383 | }); |
| 384 | 384 | } |
| 385 | 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 | } |
| 388 | 388 | |
| 389 | 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 | 435 | if (!old_names.swapRemove(name_ip)) continue; |
| 436 | 436 | // Name added |
| 437 | 437 | any_change = true; |
| 438 | try zcu.markDependeeOutdated(.{ .namespace_name = .{ | |
| 438 | try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{ | |
| 439 | 439 | .namespace = tracked_inst_index, |
| 440 | 440 | .name = name_ip, |
| 441 | 441 | } }); |
| ... | ... | @@ -444,14 +444,14 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { |
| 444 | 444 | // The only elements remaining in `old_names` now are any names which were removed. |
| 445 | 445 | for (old_names.keys()) |name_ip| { |
| 446 | 446 | any_change = true; |
| 447 | try zcu.markDependeeOutdated(.{ .namespace_name = .{ | |
| 447 | try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{ | |
| 448 | 448 | .namespace = tracked_inst_index, |
| 449 | 449 | .name = name_ip, |
| 450 | 450 | } }); |
| 451 | 451 | } |
| 452 | 452 | |
| 453 | 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 | 508 | const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index }); |
| 509 | 509 | const cau = ip.getCau(cau_index); |
| 510 | 510 | |
| 511 | log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)}); | |
| 511 | //log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)}); | |
| 512 | 512 | |
| 513 | 513 | assert(!zcu.analysis_in_progress.contains(anal_unit)); |
| 514 | 514 | |
| ... | ... | @@ -527,8 +527,91 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 527 | 527 | |
| 528 | 528 | if (cau_outdated) { |
| 529 | 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 | } |
| 531 | 600 | |
| 601 | if (analysis_fail) return error.AnalysisFail; | |
| 602 | } | |
| 603 | ||
| 604 | fn 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 | 615 | const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail; |
| 533 | 616 | |
| 534 | 617 | // TODO: document this elsewhere mlugg! |
| ... | ... | @@ -550,22 +633,6 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 550 | 633 | return error.AnalysisFail; |
| 551 | 634 | } |
| 552 | 635 | |
| 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 | 636 | // `cau_outdated` can be true in the initial update for `comptime` declarations, |
| 570 | 637 | // so this isn't a `dev.check`. |
| 571 | 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 | 640 | // prior to re-analysis. |
| 574 | 641 | zcu.deleteUnitExports(anal_unit); |
| 575 | 642 | zcu.deleteUnitReferences(anal_unit); |
| 576 | } | |
| 577 | ||
| 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 | }; | |
| 643 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { | |
| 644 | kv.value.destroy(zcu.gpa); | |
| 588 | 645 | } |
| 646 | _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); | |
| 647 | } | |
| 589 | 648 | |
| 590 | const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) { | |
| 591 | .nav => |nav| ip.getNav(nav).fqn.toSlice(ip), | |
| 592 | .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), | |
| 593 | .none => "comptime", | |
| 594 | }, 0); | |
| 595 | defer decl_prog_node.end(); | |
| 596 | ||
| 597 | break :res pt.semaCau(cau_index) catch |err| switch (err) { | |
| 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 | }, | |
| 649 | if (inst_info.inst == .main_struct_inst) { | |
| 650 | // Note that this is definitely a *recreation* due to outdated, because | |
| 651 | // this instruction indicates that `cau.owner` is a `type`, which only | |
| 652 | // reaches here if `cau_outdated`. | |
| 653 | try pt.recreateFileRoot(inst_info.file); | |
| 654 | return .{ | |
| 655 | .invalidate_decl_val = true, | |
| 656 | .invalidate_decl_ref = true, | |
| 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 | } |
| 627 | 659 | |
| 628 | // TODO: we do not yet have separate dependencies for decl values vs types. | |
| 629 | const invalidate = sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref; | |
| 630 | const dependee: InternPool.Dependee = switch (cau.owner.unwrap()) { | |
| 631 | .none => return, // there are no dependencies on a `comptime` decl! | |
| 632 | .nav => |nav_index| .{ .nav_val = nav_index }, | |
| 633 | .type => |ty| .{ .interned = ty }, | |
| 634 | }; | |
| 660 | const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) { | |
| 661 | .nav => |nav| ip.getNav(nav).fqn.toSlice(ip), | |
| 662 | .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), | |
| 663 | .none => "comptime", | |
| 664 | }, 0); | |
| 665 | defer decl_prog_node.end(); | |
| 635 | 666 | |
| 636 | if (invalidate) { | |
| 637 | // This dependency was marked as PO, meaning dependees were waiting | |
| 638 | // on its analysis result, and it has turned out to be outdated. | |
| 639 | // Update dependees accordingly. | |
| 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 | } | |
| 667 | return pt.semaCau(cau_index) catch |err| switch (err) { | |
| 668 | error.GenericPoison, error.ComptimeBreak, error.ComptimeReturn => unreachable, | |
| 669 | error.AnalysisFail, error.OutOfMemory => |e| return e, | |
| 670 | }; | |
| 646 | 671 | } |
| 647 | 672 | |
| 648 | 673 | pub 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 | 685 | |
| 661 | 686 | const func = zcu.funcInfo(maybe_coerced_func_index); |
| 662 | 687 | |
| 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 | ||
| 735 | fn 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 }); | |
| 664 | 746 | |
| 665 | 747 | // Here's an interesting question: is this function actually valid? |
| 666 | 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 | 763 | }); |
| 682 | 764 | |
| 683 | 765 | if (ip.isRemoved(func_index) or (func.generic_owner != .none and ip.isRemoved(func.generic_owner))) { |
| 684 | try zcu.markDependeeOutdated(.{ .interned = func_index }); // IES | |
| 766 | if (func_outdated) { | |
| 767 | try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index }); // IES | |
| 768 | } | |
| 685 | 769 | ip.removeDependenciesForDepender(gpa, InternPool.AnalUnit.wrap(.{ .func = func_index })); |
| 686 | 770 | ip.remove(pt.tid, func_index); |
| 687 | 771 | @panic("TODO: remove orphaned function from binary"); |
| ... | ... | @@ -694,15 +778,14 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 694 | 778 | else |
| 695 | 779 | .none; |
| 696 | 780 | |
| 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 | 781 | if (func_outdated) { |
| 702 | _ = zcu.outdated_ready.swapRemove(anal_unit); | |
| 703 | 782 | dev.check(.incremental); |
| 704 | 783 | zcu.deleteUnitExports(anal_unit); |
| 705 | 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 | } |
| 707 | 790 | |
| 708 | 791 | if (!func_outdated) { |
| ... | ... | @@ -713,7 +796,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 713 | 796 | switch (func.analysisUnordered(ip).state) { |
| 714 | 797 | .unreferenced => {}, // this is the first reference |
| 715 | 798 | .queued => {}, // we're waiting on first-time analysis |
| 716 | .analyzed => return, // up-to-date | |
| 799 | .analyzed => return .{ .ies_outdated = false }, // up-to-date | |
| 717 | 800 | } |
| 718 | 801 | } |
| 719 | 802 | |
| ... | ... | @@ -722,28 +805,11 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 722 | 805 | if (func_outdated) "outdated" else "never analyzed", |
| 723 | 806 | }); |
| 724 | 807 | |
| 725 | var air = pt.analyzeFnBody(func_index) catch |err| switch (err) { | |
| 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 | }; | |
| 808 | var air = try pt.analyzeFnBody(func_index); | |
| 736 | 809 | errdefer air.deinit(gpa); |
| 737 | 810 | |
| 738 | if (func_outdated) { | |
| 739 | if (!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 | } | |
| 811 | const ies_outdated = func_outdated and | |
| 812 | (!func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies); | |
| 747 | 813 | |
| 748 | 814 | const comp = zcu.comp; |
| 749 | 815 | |
| ... | ... | @@ -752,13 +818,15 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 752 | 818 | |
| 753 | 819 | if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) { |
| 754 | 820 | air.deinit(gpa); |
| 755 | return; | |
| 821 | return .{ .ies_outdated = ies_outdated }; | |
| 756 | 822 | } |
| 757 | 823 | |
| 758 | 824 | try comp.queueJob(.{ .codegen_func = .{ |
| 759 | 825 | .func = func_index, |
| 760 | 826 | .air = air, |
| 761 | 827 | } }); |
| 828 | ||
| 829 | return .{ .ies_outdated = ies_outdated }; | |
| 762 | 830 | } |
| 763 | 831 | |
| 764 | 832 | /// Takes ownership of `air`, even on error. |
| ... | ... | @@ -1935,6 +2003,8 @@ const ScanDeclIter = struct { |
| 1935 | 2003 | .@"comptime" => cau: { |
| 1936 | 2004 | const cau = existing_cau orelse try ip.createComptimeCau(gpa, pt.tid, tracked_inst, namespace_index); |
| 1937 | 2005 | |
| 2006 | try namespace.other_decls.append(gpa, cau); | |
| 2007 | ||
| 1938 | 2008 | // For a `comptime` declaration, whether to re-analyze is based solely on whether the |
| 1939 | 2009 | // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already. |
| 1940 | 2010 | const unit = InternPool.AnalUnit.wrap(.{ .cau = cau }); |