From 93a5bd262defe4c09a617a4ecc68340e20a2b20b Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 18 Aug 2024 13:35:35 +0100 Subject: [PATCH] frontend: removed resolved IES data for outdated functions Without this, incremental updates which would change inferred error sets fail, since they assume the IES is resolved and equals the old set, resulting in false positive compile errors when e.g. coercing to an IES. --- src/InternPool.zig | 8 ++++++++ src/Sema.zig | 5 +++-- src/Zcu.zig | 26 ++++++++++++++++++++++++++ src/Zcu/PerThread.zig | 5 ++++- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index d0dc16c47d128db95cbc1965d70fe484aca8aa36..99f937cacfa4d98c4c2b1312e0c47accd3e4a7b0 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2160,6 +2160,14 @@ pub const Key = union(enum) { pub fn resolvedErrorSetUnordered(func: Func, ip: *const InternPool) Index { return @atomicLoad(Index, func.resolvedErrorSetPtr(@constCast(ip)), .unordered); } + + pub fn setResolvedErrorSet(func: Func, ip: *InternPool, ies: Index) void { + const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex; + extra_mutex.lock(); + defer extra_mutex.unlock(); + + @atomicStore(Index, func.resolvedErrorSetPtr(ip), ies, .release); + } }; pub const Int = struct { diff --git a/src/Sema.zig b/src/Sema.zig index 7930c8f08002ec35a1fbecbd7de8094e4dde587b..756e0ac9cab590b36518c8116f3495ab866f67fd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -32527,6 +32527,7 @@ fn analyzeIsNonErrComptimeOnly( // If the error set is empty, we must return a comptime true or false. // However we want to avoid unnecessarily resolving an inferred error set // in case it is already non-empty. + try mod.maybeUnresolveIes(func_index); switch (ip.funcIesResolvedUnordered(func_index)) { .anyerror_type => break :blk, .none => {}, @@ -33596,6 +33597,7 @@ fn wrapErrorUnionSet( .inferred_error_set_type => |func_index| ok: { // We carefully do this in an order that avoids unnecessarily // resolving the destination error set type. + try mod.maybeUnresolveIes(func_index); switch (ip.funcIesResolvedUnordered(func_index)) { .anyerror_type => break :ok, .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { @@ -35853,8 +35855,7 @@ fn resolveInferredErrorSet( try sema.declareDependency(.{ .interned = func_index }); // resolved IES - // TODO: during an incremental update this might not be `.none`, but the - // function might be out-of-date! + try zcu.maybeUnresolveIes(func_index); const resolved_ty = func.resolvedErrorSetUnordered(ip); if (resolved_ty != .none) return resolved_ty; diff --git a/src/Zcu.zig b/src/Zcu.zig index a47979bf60e5522369eff94ef4f068fa21cc6310..53ad80444e70660ea4af91828a43fb224e6e367b 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3468,3 +3468,29 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com }, } } + +/// Given the `InternPool.Index` of a function, set its resolved IES to `.none` if it +/// may be outdated. `Sema` should do this before ever loading a resolved IES. +pub fn maybeUnresolveIes(zcu: *Zcu, func_index: InternPool.Index) !void { + const unit = AnalUnit.wrap(.{ .func = func_index }); + if (zcu.outdated.contains(unit) or zcu.potentially_outdated.contains(unit)) { + // We're consulting the resolved IES now, but the function is outdated, so its + // IES may have changed. We have to assume the IES is outdated and set the resolved + // set back to `.none`. + // + // This will cause `PerThread.analyzeFnBody` to mark the IES as outdated when it's + // eventually hit. + // + // Since the IES needs to be resolved, the function body will now definitely need + // re-analysis (even if the IES turns out to be the same!), so mark it as + // definitely-outdated if it's only PO. + if (zcu.potentially_outdated.fetchSwapRemove(unit)) |kv| { + const gpa = zcu.gpa; + try zcu.outdated.putNoClobber(gpa, unit, kv.value); + if (kv.value == 0) { + try zcu.outdated_ready.put(gpa, unit, {}); + } + } + zcu.intern_pool.funcSetIesResolved(func_index, .none); + } +} diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 700d8708b1acbc5b7b36ad4eba01de92a53e4391..88bc318b3cf49ed43762cee8a369f65028a5eda9 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -2072,6 +2072,9 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError! errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); func.setAnalysisState(ip, .analyzed); + if (func.analysisUnordered(ip).inferred_error_set) { + func.setResolvedErrorSet(ip, .none); + } // This is the `Cau` corresponding to the `declaration` instruction which the function or its generic owner originates from. const decl_cau = ip.getCau(cau: { @@ -2278,7 +2281,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError! else => |e| return e, }; assert(ies.resolved != .none); - ip.funcSetIesResolved(func_index, ies.resolved); + func.setResolvedErrorSet(ip, ies.resolved); } assert(zcu.analysis_in_progress.swapRemove(anal_unit)); -- 2.54.0