aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-07-16 10:47:42 -0700
committerGitHub <noreply@github.com>2024-07-16 10:47:42 -0700
commita58ceb3d554a9565a6cc0443f6384149ae2b3145 (patch)
treeaf66e289a0ae028be92389722979ed270b42ee42 /src
parenta9d544575d5bd2a939b09b8f088bee5d8ff7b0d9 (diff)
parent7dbd2a6bb549afa6dc3c95df46f40bf144db23a6 (diff)
downloadzig-a58ceb3d554a9565a6cc0443f6384149ae2b3145.tar.gz
zig-a58ceb3d554a9565a6cc0443f6384149ae2b3145.zip
Merge pull request #20646 from ziglang/fix-updateZirRefs
frontend: fix updateZirRefs
Diffstat (limited to 'src')
-rw-r--r--src/Air.zig3
-rw-r--r--src/Compilation.zig9
-rw-r--r--src/InternPool.zig10
-rw-r--r--src/Sema.zig2
-rw-r--r--src/Zcu.zig57
-rw-r--r--src/Zcu/PerThread.zig264
-rw-r--r--src/main.zig3
7 files changed, 188 insertions, 160 deletions
diff --git a/src/Air.zig b/src/Air.zig
index d1c6d184e6..4a442aa332 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -1,4 +1,5 @@
//! Analyzed Intermediate Representation.
+//!
//! This data is produced by Sema and consumed by codegen.
//! Unlike ZIR where there is one instance for an entire source file, each function
//! gets its own `Air` instance.
@@ -12,8 +13,6 @@ const Value = @import("Value.zig");
const Type = @import("Type.zig");
const InternPool = @import("InternPool.zig");
const Zcu = @import("Zcu.zig");
-/// Deprecated.
-const Module = Zcu;
instructions: std.MultiArrayList(Inst).Slice,
/// The meaning of this data is determined by `Inst.Tag` value.
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 76dc42fc64..8243bc3620 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -3595,7 +3595,12 @@ fn performAllTheWorkInner(
}
if (comp.module) |zcu| {
- const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = .main };
+ const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main };
+ if (comp.incremental) {
+ const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
+ defer update_zir_refs_node.end();
+ try pt.updateZirRefs();
+ }
try reportMultiModuleErrors(pt);
try zcu.flushRetryableFailures();
zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
@@ -4306,7 +4311,7 @@ fn workerAstGenFile(
defer child_prog_node.end();
const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) };
- pt.astGenFile(file, file_index, path_digest, root_decl) catch |err| switch (err) {
+ pt.astGenFile(file, path_digest, root_decl) catch |err| switch (err) {
error.AnalysisFail => return,
else => {
file.status = .retryable_failure;
diff --git a/src/InternPool.zig b/src/InternPool.zig
index ff748ebc62..2440ccff3b 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -283,10 +283,12 @@ pub const DependencyIterator = struct {
ip: *const InternPool,
next_entry: DepEntry.Index.Optional,
pub fn next(it: *DependencyIterator) ?AnalUnit {
- const idx = it.next_entry.unwrap() orelse return null;
- const entry = it.ip.dep_entries.items[@intFromEnum(idx)];
- it.next_entry = entry.next;
- return entry.depender.unwrap().?;
+ while (true) {
+ const idx = it.next_entry.unwrap() orelse return null;
+ const entry = it.ip.dep_entries.items[@intFromEnum(idx)];
+ it.next_entry = entry.next;
+ if (entry.depender.unwrap()) |depender| return depender;
+ }
}
};
diff --git a/src/Sema.zig b/src/Sema.zig
index d0b14434f3..63632e9f0f 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -6065,7 +6065,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
const path_digest = zcu.filePathDigest(result.file_index);
const root_decl = zcu.fileRootDecl(result.file_index);
- pt.astGenFile(result.file, result.file_index, path_digest, root_decl) catch |err|
+ pt.astGenFile(result.file, path_digest, root_decl) catch |err|
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
try pt.ensureFileAnalyzed(result.file_index);
diff --git a/src/Zcu.zig b/src/Zcu.zig
index 36b4c95571..dbc2e5a2c4 100644
--- a/src/Zcu.zig
+++ b/src/Zcu.zig
@@ -1,5 +1,8 @@
-//! Compilation of all Zig source code is represented by one `Module`.
-//! Each `Compilation` has exactly one or zero `Module`, depending on whether
+//! Zig Compilation Unit
+//!
+//! Compilation of all Zig source code is represented by one `Zcu`.
+//!
+//! Each `Compilation` has exactly one or zero `Zcu`, depending on whether
//! there is or is not any zig source code, respectively.
const std = @import("std");
@@ -13,8 +16,6 @@ const BigIntMutable = std.math.big.int.Mutable;
const Target = std.Target;
const Ast = std.zig.Ast;
-/// Deprecated, use `Zcu`.
-const Module = Zcu;
const Zcu = @This();
const Compilation = @import("Compilation.zig");
const Cache = std.Build.Cache;
@@ -2393,7 +2394,7 @@ pub const CompileError = error{
ComptimeBreak,
};
-pub fn init(mod: *Module, thread_count: usize) !void {
+pub fn init(mod: *Zcu, thread_count: usize) !void {
const gpa = mod.gpa;
try mod.intern_pool.init(gpa, thread_count);
}
@@ -2487,20 +2488,20 @@ pub fn deinit(zcu: *Zcu) void {
zcu.intern_pool.deinit(gpa);
}
-pub fn declPtr(mod: *Module, index: Decl.Index) *Decl {
+pub fn declPtr(mod: *Zcu, index: Decl.Index) *Decl {
return mod.intern_pool.declPtr(index);
}
-pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace {
+pub fn namespacePtr(mod: *Zcu, index: Namespace.Index) *Namespace {
return mod.intern_pool.namespacePtr(index);
}
-pub fn namespacePtrUnwrap(mod: *Module, index: Namespace.OptionalIndex) ?*Namespace {
+pub fn namespacePtrUnwrap(mod: *Zcu, index: Namespace.OptionalIndex) ?*Namespace {
return mod.namespacePtr(index.unwrap() orelse return null);
}
/// Returns true if and only if the Decl is the top level struct associated with a File.
-pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {
+pub fn declIsRoot(mod: *Zcu, decl_index: Decl.Index) bool {
const decl = mod.declPtr(decl_index);
const namespace = mod.namespacePtr(decl.src_namespace);
if (namespace.parent != .none) return false;
@@ -2940,7 +2941,7 @@ pub fn mapOldZirToNew(
/// analyzed, and for ensuring it can exist at runtime (see
/// `sema.fnHasRuntimeBits`). This function does *not* guarantee that the body
/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`.
-pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) !void {
+pub fn ensureFuncBodyAnalysisQueued(mod: *Zcu, func_index: InternPool.Index) !void {
const ip = &mod.intern_pool;
const func = mod.funcInfo(func_index);
const decl_index = func.owner_decl;
@@ -3102,13 +3103,13 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit
gop.value_ptr.* = @intCast(ref_idx);
}
-pub fn errorSetBits(mod: *Module) u16 {
+pub fn errorSetBits(mod: *Zcu) u16 {
if (mod.error_limit == 0) return 0;
return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error
}
pub fn errNote(
- mod: *Module,
+ mod: *Zcu,
src_loc: LazySrcLoc,
parent: *ErrorMsg,
comptime format: []const u8,
@@ -3138,7 +3139,7 @@ pub fn optimizeMode(zcu: *const Zcu) std.builtin.OptimizeMode {
return zcu.root_mod.optimize_mode;
}
-fn lockAndClearFileCompileError(mod: *Module, file: *File) void {
+fn lockAndClearFileCompileError(mod: *Zcu, file: *File) void {
switch (file.status) {
.success_zir, .retryable_failure => {},
.never_loaded, .parse_failure, .astgen_failure => {
@@ -3172,7 +3173,7 @@ pub fn handleUpdateExports(
};
}
-pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void {
+pub fn addGlobalAssembly(mod: *Zcu, decl_index: Decl.Index, source: []const u8) !void {
const gop = try mod.global_assembly.getOrPut(mod.gpa, decl_index);
if (gop.found_existing) {
const new_value = try std.fmt.allocPrint(mod.gpa, "{s}\n{s}", .{ gop.value_ptr.*, source });
@@ -3226,7 +3227,7 @@ pub const AtomicPtrAlignmentDiagnostics = struct {
// TODO this function does not take into account CPU features, which can affect
// this value. Audit this!
pub fn atomicPtrAlignment(
- mod: *Module,
+ mod: *Zcu,
ty: Type,
diags: *AtomicPtrAlignmentDiagnostics,
) AtomicPtrAlignmentError!Alignment {
@@ -3332,7 +3333,7 @@ pub fn atomicPtrAlignment(
return error.BadType;
}
-pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File {
+pub fn declFileScope(mod: *Zcu, decl_index: Decl.Index) *File {
return mod.declPtr(decl_index).getFileScope(mod);
}
@@ -3340,7 +3341,7 @@ pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File {
/// * `@TypeOf(.{})`
/// * A struct which has no fields (`struct {}`).
/// * Not a struct.
-pub fn typeToStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType {
+pub fn typeToStruct(mod: *Zcu, ty: Type) ?InternPool.LoadedStructType {
if (ty.ip_index == .none) return null;
const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.ip_index)) {
@@ -3349,13 +3350,13 @@ pub fn typeToStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType {
};
}
-pub fn typeToPackedStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType {
+pub fn typeToPackedStruct(mod: *Zcu, ty: Type) ?InternPool.LoadedStructType {
const s = mod.typeToStruct(ty) orelse return null;
if (s.layout != .@"packed") return null;
return s;
}
-pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.LoadedUnionType {
+pub fn typeToUnion(mod: *Zcu, ty: Type) ?InternPool.LoadedUnionType {
if (ty.ip_index == .none) return null;
const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.ip_index)) {
@@ -3364,32 +3365,32 @@ pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.LoadedUnionType {
};
}
-pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType {
+pub fn typeToFunc(mod: *Zcu, ty: Type) ?InternPool.Key.FuncType {
if (ty.ip_index == .none) return null;
return mod.intern_pool.indexToFuncType(ty.toIntern());
}
-pub fn funcOwnerDeclPtr(mod: *Module, func_index: InternPool.Index) *Decl {
+pub fn funcOwnerDeclPtr(mod: *Zcu, func_index: InternPool.Index) *Decl {
return mod.declPtr(mod.funcOwnerDeclIndex(func_index));
}
-pub fn funcOwnerDeclIndex(mod: *Module, func_index: InternPool.Index) Decl.Index {
+pub fn funcOwnerDeclIndex(mod: *Zcu, func_index: InternPool.Index) Decl.Index {
return mod.funcInfo(func_index).owner_decl;
}
-pub fn iesFuncIndex(mod: *const Module, ies_index: InternPool.Index) InternPool.Index {
+pub fn iesFuncIndex(mod: *const Zcu, ies_index: InternPool.Index) InternPool.Index {
return mod.intern_pool.iesFuncIndex(ies_index);
}
-pub fn funcInfo(mod: *Module, func_index: InternPool.Index) InternPool.Key.Func {
+pub fn funcInfo(mod: *Zcu, func_index: InternPool.Index) InternPool.Key.Func {
return mod.intern_pool.indexToKey(func_index).func;
}
-pub fn toEnum(mod: *Module, comptime E: type, val: Value) E {
+pub fn toEnum(mod: *Zcu, comptime E: type, val: Value) E {
return mod.intern_pool.toEnum(E, val.toIntern());
}
-pub fn isAnytypeParam(mod: *Module, func: InternPool.Index, index: u32) bool {
+pub fn isAnytypeParam(mod: *Zcu, func: InternPool.Index, index: u32) bool {
const file = mod.declPtr(func.owner_decl).getFileScope(mod);
const tags = file.zir.instructions.items(.tag);
@@ -3404,7 +3405,7 @@ pub fn isAnytypeParam(mod: *Module, func: InternPool.Index, index: u32) bool {
};
}
-pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]const u8 {
+pub fn getParamName(mod: *Zcu, func_index: InternPool.Index, index: u32) [:0]const u8 {
const func = mod.funcInfo(func_index);
const file = mod.declPtr(func.owner_decl).getFileScope(mod);
@@ -3441,7 +3442,7 @@ pub const UnionLayout = struct {
};
/// Returns the index of the active field, given the current tag value
-pub fn unionTagFieldIndex(mod: *Module, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 {
+pub fn unionTagFieldIndex(mod: *Zcu, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 {
const ip = &mod.intern_pool;
if (enum_tag.toIntern() == .none) return null;
assert(ip.typeOf(enum_tag.toIntern()) == loaded_union.enum_tag_ty);
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
index c3f569cc7d..d639f3c824 100644
--- a/src/Zcu/PerThread.zig
+++ b/src/Zcu/PerThread.zig
@@ -60,10 +60,6 @@ pub fn destroyFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void {
pub fn astGenFile(
pt: Zcu.PerThread,
file: *Zcu.File,
- /// This parameter is provided separately from `file` because it is not
- /// safe to access `import_table` without a lock, and this index is needed
- /// in the call to `updateZirRefs`.
- file_index: Zcu.File.Index,
path_digest: Cache.BinDigest,
opt_root_decl: Zcu.Decl.OptionalIndex,
) !void {
@@ -210,13 +206,18 @@ pub fn astGenFile(
pt.lockAndClearFileCompileError(file);
- // If the previous ZIR does not have compile errors, keep it around
- // in case parsing or new ZIR fails. In case of successful ZIR update
- // at the end of this function we will free it.
- // We keep the previous ZIR loaded so that we can use it
- // for the update next time it does not have any compile errors. This avoids
- // needlessly tossing out semantic analysis work when an error is
- // temporarily introduced.
+ // Previous ZIR is kept for two reasons:
+ //
+ // 1. In case an update to the file causes a Parse or AstGen failure, we
+ // need to compare two successful ZIR files in order to proceed with an
+ // incremental update. This avoids needlessly tossing out semantic
+ // analysis work when an error is temporarily introduced.
+ //
+ // 2. In order to detect updates, we need to iterate over the intern pool
+ // values while comparing old ZIR to new ZIR. This is better done in a
+ // single-threaded context, so we need to keep both versions around
+ // until that point in the pipeline. Previous ZIR data is freed after
+ // that.
if (file.zir_loaded and !file.zir.hasCompileErrors()) {
assert(file.prev_zir == null);
const prev_zir_ptr = try gpa.create(Zir);
@@ -320,14 +321,6 @@ pub fn astGenFile(
return error.AnalysisFail;
}
- if (file.prev_zir) |prev_zir| {
- try pt.updateZirRefs(file, file_index, prev_zir.*);
- // No need to keep previous ZIR.
- prev_zir.deinit(gpa);
- gpa.destroy(prev_zir);
- file.prev_zir = null;
- }
-
if (opt_root_decl.unwrap()) |root_decl| {
// The root of this file must be re-analyzed, since the file has changed.
comp.mutex.lock();
@@ -338,137 +331,162 @@ pub fn astGenFile(
}
}
-/// This is called from the AstGen thread pool, so must acquire
-/// the Compilation mutex when acting on shared state.
-fn updateZirRefs(pt: Zcu.PerThread, file: *Zcu.File, file_index: Zcu.File.Index, old_zir: Zir) !void {
+const UpdatedFile = struct {
+ file_index: Zcu.File.Index,
+ file: *Zcu.File,
+ inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
+};
+
+fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.ArrayListUnmanaged(UpdatedFile)) void {
+ for (updated_files.items) |*elem| elem.inst_map.deinit(gpa);
+ updated_files.deinit(gpa);
+}
+
+pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
+ assert(pt.tid == .main);
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const gpa = zcu.gpa;
- const new_zir = file.zir;
-
- var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
- defer inst_map.deinit(gpa);
- try Zcu.mapOldZirToNew(gpa, old_zir, new_zir, &inst_map);
+ // We need to visit every updated File for every TrackedInst in InternPool.
+ var updated_files: std.ArrayListUnmanaged(UpdatedFile) = .{};
+ defer cleanupUpdatedFiles(gpa, &updated_files);
+ for (zcu.import_table.values()) |file_index| {
+ const file = zcu.fileByIndex(file_index);
+ const old_zir = file.prev_zir orelse continue;
+ const new_zir = file.zir;
+ try updated_files.append(gpa, .{
+ .file_index = file_index,
+ .file = file,
+ .inst_map = .{},
+ });
+ const inst_map = &updated_files.items[updated_files.items.len - 1].inst_map;
+ try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, inst_map);
+ }
- const old_tag = old_zir.instructions.items(.tag);
- const old_data = old_zir.instructions.items(.data);
+ if (updated_files.items.len == 0)
+ return;
- // TODO: this should be done after all AstGen workers complete, to avoid
- // iterating over this full set for every updated file.
for (ip.locals, 0..) |*local, tid| {
- local.mutate.tracked_insts.mutex.lock();
- defer local.mutate.tracked_insts.mutex.unlock();
const tracked_insts_list = local.getMutableTrackedInsts(gpa);
for (tracked_insts_list.view().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| {
- if (tracked_inst.file != file_index) continue;
- const old_inst = tracked_inst.inst;
- const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
- .tid = @enumFromInt(tid),
- .index = @intCast(tracked_inst_unwrapped_index),
- }).wrap(ip);
- tracked_inst.inst = inst_map.get(old_inst) orelse {
- // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
- zcu.comp.mutex.lock();
- defer zcu.comp.mutex.unlock();
- log.debug("tracking failed for %{d}", .{old_inst});
- try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });
- continue;
- };
+ for (updated_files.items) |updated_file| {
+ const file_index = updated_file.file_index;
+ if (tracked_inst.file != file_index) continue;
+
+ const file = updated_file.file;
+ const old_zir = file.prev_zir.?.*;
+ const new_zir = file.zir;
+ const old_tag = old_zir.instructions.items(.tag);
+ const old_data = old_zir.instructions.items(.data);
+ const inst_map = &updated_file.inst_map;
+
+ const old_inst = tracked_inst.inst;
+ const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
+ .tid = @enumFromInt(tid),
+ .index = @intCast(tracked_inst_unwrapped_index),
+ }).wrap(ip);
+ tracked_inst.inst = inst_map.get(old_inst) orelse {
+ // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
+ log.debug("tracking failed for %{d}", .{old_inst});
+ try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });
+ continue;
+ };
- if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {
- if (new_zir.getAssociatedSrcHash(tracked_inst.inst)) |new_hash| {
- if (std.zig.srcHashEql(old_hash, new_hash)) {
- break :hash_changed;
+ if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {
+ if (new_zir.getAssociatedSrcHash(tracked_inst.inst)) |new_hash| {
+ if (std.zig.srcHashEql(old_hash, new_hash)) {
+ break :hash_changed;
+ }
+ log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{
+ old_inst,
+ tracked_inst.inst,
+ std.fmt.fmtSliceHexLower(&old_hash),
+ std.fmt.fmtSliceHexLower(&new_hash),
+ });
}
- log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{
- old_inst,
- tracked_inst.inst,
- std.fmt.fmtSliceHexLower(&old_hash),
- std.fmt.fmtSliceHexLower(&new_hash),
- });
+ // The source hash associated with this instruction changed - invalidate relevant dependencies.
+ try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });
}
- // The source hash associated with this instruction changed - invalidate relevant dependencies.
- zcu.comp.mutex.lock();
- defer zcu.comp.mutex.unlock();
- try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index });
- }
- // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
- const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {
- .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {
- .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
+ // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
+ const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {
+ .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {
+ .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
+ else => false,
+ },
else => false,
- },
- else => false,
- };
- if (!has_namespace) continue;
-
- var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
- defer old_names.deinit(zcu.gpa);
- {
- var it = old_zir.declIterator(old_inst);
- while (it.next()) |decl_inst| {
- const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
- switch (decl_name) {
- .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
- _ => if (decl_name.isNamedTest(old_zir)) continue,
+ };
+ if (!has_namespace) continue;
+
+ var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
+ defer old_names.deinit(zcu.gpa);
+ {
+ var it = old_zir.declIterator(old_inst);
+ while (it.next()) |decl_inst| {
+ const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
+ switch (decl_name) {
+ .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
+ _ => if (decl_name.isNamedTest(old_zir)) continue,
+ }
+ const name_zir = decl_name.toString(old_zir).?;
+ const name_ip = try zcu.intern_pool.getOrPutString(
+ zcu.gpa,
+ pt.tid,
+ old_zir.nullTerminatedString(name_zir),
+ .no_embedded_nulls,
+ );
+ try old_names.put(zcu.gpa, name_ip, {});
}
- const name_zir = decl_name.toString(old_zir).?;
- const name_ip = try zcu.intern_pool.getOrPutString(
- zcu.gpa,
- pt.tid,
- old_zir.nullTerminatedString(name_zir),
- .no_embedded_nulls,
- );
- try old_names.put(zcu.gpa, name_ip, {});
}
- }
- var any_change = false;
- {
- var it = new_zir.declIterator(tracked_inst.inst);
- while (it.next()) |decl_inst| {
- const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
- switch (decl_name) {
- .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
- _ => if (decl_name.isNamedTest(old_zir)) continue,
+ var any_change = false;
+ {
+ var it = new_zir.declIterator(tracked_inst.inst);
+ while (it.next()) |decl_inst| {
+ const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
+ switch (decl_name) {
+ .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
+ _ => if (decl_name.isNamedTest(old_zir)) continue,
+ }
+ const name_zir = decl_name.toString(old_zir).?;
+ const name_ip = try zcu.intern_pool.getOrPutString(
+ zcu.gpa,
+ pt.tid,
+ old_zir.nullTerminatedString(name_zir),
+ .no_embedded_nulls,
+ );
+ if (!old_names.swapRemove(name_ip)) continue;
+ // Name added
+ any_change = true;
+ try zcu.markDependeeOutdated(.{ .namespace_name = .{
+ .namespace = tracked_inst_index,
+ .name = name_ip,
+ } });
}
- const name_zir = decl_name.toString(old_zir).?;
- const name_ip = try zcu.intern_pool.getOrPutString(
- zcu.gpa,
- pt.tid,
- old_zir.nullTerminatedString(name_zir),
- .no_embedded_nulls,
- );
- if (!old_names.swapRemove(name_ip)) continue;
- // Name added
+ }
+ // The only elements remaining in `old_names` now are any names which were removed.
+ for (old_names.keys()) |name_ip| {
any_change = true;
- zcu.comp.mutex.lock();
- defer zcu.comp.mutex.unlock();
try zcu.markDependeeOutdated(.{ .namespace_name = .{
.namespace = tracked_inst_index,
.name = name_ip,
} });
}
- }
- // The only elements remaining in `old_names` now are any names which were removed.
- for (old_names.keys()) |name_ip| {
- any_change = true;
- zcu.comp.mutex.lock();
- defer zcu.comp.mutex.unlock();
- try zcu.markDependeeOutdated(.{ .namespace_name = .{
- .namespace = tracked_inst_index,
- .name = name_ip,
- } });
- }
- if (any_change) {
- zcu.comp.mutex.lock();
- defer zcu.comp.mutex.unlock();
- try zcu.markDependeeOutdated(.{ .namespace = tracked_inst_index });
+ if (any_change) {
+ try zcu.markDependeeOutdated(.{ .namespace = tracked_inst_index });
+ }
}
}
}
+
+ for (updated_files.items) |updated_file| {
+ const file = updated_file.file;
+ const prev_zir = file.prev_zir.?;
+ file.prev_zir = null;
+ prev_zir.deinit(gpa);
+ gpa.destroy(prev_zir);
+ }
}
/// Like `ensureDeclAnalyzed`, but the Decl is a file's root Decl.
diff --git a/src/main.zig b/src/main.zig
index 2b36c31865..a5b315c2bb 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -4230,6 +4230,9 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
});
return;
}
+
+ // Serve empty error bundle to indicate the update is done.
+ try s.serveErrorBundle(std.zig.ErrorBundle.empty);
}
fn runOrTest(