diff --git a/src/InternPool.zig b/src/InternPool.zig index b1acb0f9b0..2367edff45 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -67,6 +67,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index) /// Dependencies on the value of a Decl. /// Value is index into `dep_entries` of the first dependency on this Decl value. decl_val_deps: std.AutoArrayHashMapUnmanaged(DeclIndex, DepEntry.Index) = .{}, +/// Dependencies on the IES of a runtime function. +/// Value is index into `dep_entries` of the first dependency on this Decl value. +func_ies_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index) = .{}, /// Dependencies on the full set of names in a ZIR namespace. /// Key refers to a `struct_decl`, `union_decl`, etc. /// Value is index into `dep_entries` of the first dependency on this namespace. @@ -167,6 +170,7 @@ pub const Depender = enum(u32) { pub const Dependee = union(enum) { src_hash: TrackedInst.Index, decl_val: DeclIndex, + func_ies: Index, namespace: TrackedInst.Index, namespace_name: NamespaceNameKey, }; @@ -212,6 +216,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI const first_entry = switch (dependee) { .src_hash => |x| ip.src_hash_deps.get(x), .decl_val => |x| ip.decl_val_deps.get(x), + .func_ies => |x| ip.func_ies_deps.get(x), .namespace => |x| ip.namespace_deps.get(x), .namespace_name => |x| ip.namespace_name_deps.get(x), } orelse return .{ @@ -251,6 +256,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: Depender, depend const gop = try switch (tag) { .src_hash => ip.src_hash_deps, .decl_val => ip.decl_val_deps, + .func_ies => ip.func_ies_deps, .namespace => ip.namespace_deps, .namespace_name => ip.namespace_name_deps, }.getOrPut(gpa, dependee_payload); @@ -4324,6 +4330,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.src_hash_deps.deinit(gpa); ip.decl_val_deps.deinit(gpa); + ip.func_ies_deps.deinit(gpa); ip.namespace_deps.deinit(gpa); ip.namespace_name_deps.deinit(gpa); diff --git a/src/Module.zig b/src/Module.zig index 45d136cf37..8d0a8fe328 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2661,10 +2661,9 @@ pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { } // If this is a Decl and was not previously PO, we must recursively // mark dependencies on its tyval as PO. - if (opt_po_entry == null) switch (depender.unwrap()) { - .decl => |decl_index| try zcu.markDeclDependenciesPotentiallyOutdated(decl_index), - .func => {}, - }; + if (opt_po_entry == null) { + try zcu.markTransitiveDependersPotentiallyOutdated(depender); + } } } @@ -2694,15 +2693,19 @@ fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { // as no longer PO. switch (depender.unwrap()) { .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }), - .func => {}, + .func => |func_index| try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }), } } } -/// Given a Decl which is newly outdated or PO, mark all dependers which depend -/// on its tyval as PO. -fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !void { - var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); +/// Given a Depender which is newly outdated or PO, mark all Dependers which may +/// in turn be PO, due to a dependency on the original Depender's tyval or IES. +fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: InternPool.Depender) !void { + var it = zcu.intern_pool.dependencyIterator(switch (maybe_outdated.unwrap()) { + .decl => |decl_index| .{ .decl_val = decl_index }, // TODO: also `decl_ref` deps when introduced + .func => |func_index| .{ .func_ies = func_index }, + }); + while (it.next()) |po| { if (zcu.outdated.getPtr(po)) |po_dep_count| { // This dependency is already outdated, but it now has one more PO @@ -2719,14 +2722,9 @@ fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !v continue; } try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); - // If this ia a Decl, we must recursively mark dependencies - // on its tyval as PO. - switch (po.unwrap()) { - .decl => |po_decl| try zcu.markDeclDependenciesPotentiallyOutdated(po_decl), - .func => {}, - } + // This Depender was not already PO, so we must recursively mark its dependers as also PO. + try zcu.markTransitiveDependersPotentiallyOutdated(po); } - // TODO: repeat the above for `decl_ty` dependencies when they are introduced } pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender { @@ -2852,10 +2850,7 @@ pub fn flushRetryableFailures(zcu: *Zcu) !void { // This Depender was not marked PO, but is now outdated. Mark it as // such, then recursively mark transitive dependencies as PO. try zcu.outdated.put(gpa, depender, 0); - switch (depender.unwrap()) { - .decl => |decl| try zcu.markDeclDependenciesPotentiallyOutdated(decl), - .func => {}, - } + try zcu.markTransitiveDependersPotentiallyOutdated(depender); } zcu.retryable_failures.clearRetainingCapacity(); } @@ -3142,11 +3137,19 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError // decl now refers to a different function, making this one orphaned. If // that's the case, we should remove this function from the binary. if (decl.val.ip_index != func_index) { + try zcu.markDependeeOutdated(.{ .func_ies = func_index }); ip.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index })); ip.remove(func_index); @panic("TODO: remove orphaned function from binary"); } + // We'll want to remember what the IES used to be before the update for + // dependency invalidation purposes. + const old_resolved_ies = if (func.analysis(ip).inferred_error_set) + func.resolvedErrorSet(ip).* + else + .none; + switch (decl.analysis) { .unreferenced => unreachable, .in_progress => unreachable, @@ -3203,6 +3206,20 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError }; defer air.deinit(gpa); + const invalidate_ies_deps = i: { + if (!was_outdated) break :i false; + if (!func.analysis(ip).inferred_error_set) break :i true; + const new_resolved_ies = func.resolvedErrorSet(ip).*; + break :i new_resolved_ies != old_resolved_ies; + }; + if (invalidate_ies_deps) { + log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)}); + try zcu.markDependeeOutdated(.{ .func_ies = func_index }); + } else if (was_outdated) { + log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)}); + try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }); + } + const comp = zcu.comp; const dump_air = build_options.enable_debug_extensions and comp.verbose_air; diff --git a/src/Sema.zig b/src/Sema.zig index c8246605aa..54d5e6df7d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -36462,8 +36462,14 @@ fn resolveInferredErrorSet( const ip = &mod.intern_pool; const func_index = ip.iesFuncIndex(ies_index); const func = mod.funcInfo(func_index); + + try sema.declareDependency(.{ .func_ies = func_index }); + + // TODO: during an incremental update this might not be `.none`, but the + // function might be out-of-date! const resolved_ty = func.resolvedErrorSet(ip).*; if (resolved_ty != .none) return resolved_ty; + if (func.analysis(ip).state == .in_progress) return sema.fail(block, src, "unable to resolve inferred error set", .{});