authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-10-14 10:21:20-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-21 10:04:01-07:00
log203bbaee07dfd8e67ea102dbb29afb75be246e53
treea9f4352e17246561d99119cbbb5d4e5198456f07
parentf08a8f0fc217914690a58035291da8bcc7a86e47

main: support multiple non-incremental protocol update messages


5 files changed, 475 insertions(+), 344 deletions(-)

src/Compilation.zig+178-139
......@@ -20,7 +20,6 @@ const link = @import("link.zig");
2020const tracy = @import("tracy.zig");
2121const trace = tracy.trace;
2222const build_options = @import("build_options");
23const debugLogScopeEnabled = @import("main.zig").debugLogScopeEnabled;
2423const LibCInstallation = std.zig.LibCInstallation;
2524const glibc = @import("libs/glibc.zig");
2625const musl = @import("libs/musl.zig");
......@@ -148,6 +147,7 @@ failed_win32_resources: if (dev.env.supports(.win32_resource)) std.array_hash_ma
148147 pub fn values(_: @This()) [0]void {
149148 return .{};
150149 }
150 pub fn clearRetainingCapacity(_: @This()) void {}
151151 pub fn deinit(_: @This(), _: Allocator) void {}
152152} = .{},
153153
......@@ -238,8 +238,6 @@ reference_trace: ?u32 = null,
238238/// This mutex guards all `Compilation` mutable state.
239239mutex: std.Io.Mutex = .init,
240240
241test_filters: []const []const u8,
242
243241link_prog_node: std.Progress.Node = .none,
244242
245243llvm_opt_bisect_limit: c_int,
......@@ -1635,28 +1633,11 @@ pub const CreateOptions = struct {
16351633 };
16361634};
16371635
1638fn addModuleTableToCacheHash(zcu: *Zcu, hash: *Cache.HashHelper) error{ OutOfMemory, Unexpected }!void {
1639 assert(zcu.module_roots.count() != 0); // module_roots is populated
1640
1641 for (zcu.module_roots.keys(), zcu.module_roots.values()) |mod, opt_mod_root_file| {
1642 if (mod == zcu.std_mod) continue; // redundant
1643 if (opt_mod_root_file.unwrap()) |mod_root_file| {
1644 if (zcu.fileByIndex(mod_root_file).is_builtin) continue; // redundant
1645 }
1646 cache_helpers.addModule(hash, mod);
1647 hash.add(mod.root.root);
1648 hash.addBytes(mod.root.sub_path);
1649 hash.addBytes(mod.root_src_path);
1650 hash.addListOfBytes(mod.deps.keys());
1651 }
1652}
1653
16541636const RtStrat = enum { none, lib, obj, zcu };
16551637
16561638pub const CreateDiagnostic = union(enum) {
16571639 export_table_import_table_conflict,
16581640 emit_h_without_zcu,
1659 illegal_zig_import,
16601641 cross_libc_unavailable,
16611642 find_native_libc: std.zig.LibCInstallation.FindError,
16621643 libc_installation_missing_cc_dir,
......@@ -1672,7 +1653,6 @@ pub const CreateDiagnostic = union(enum) {
16721653 switch (diag) {
16731654 .export_table_import_table_conflict => try w.writeAll("'--import-table' and '--export-table' cannot be used together"),
16741655 .emit_h_without_zcu => try w.writeAll("cannot emit C header with no Zig source files"),
1675 .illegal_zig_import => try w.writeAll("this compiler implementation does not support importing the root source file of a provided module"),
16761656 .cross_libc_unavailable => try w.writeAll("unable to provide libc for this target"),
16771657 .find_native_libc => |err| try w.print("failed to find libc installation: {t}", .{err}),
16781658 .libc_installation_missing_cc_dir => try w.writeAll("libc installation is missing cc directory"),
......@@ -1720,7 +1700,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
17201700 const use_llvm = options.config.use_llvm;
17211701 const target = &options.root_mod.resolved_target.result;
17221702
1723 const comp: *Compilation = comp: {
1703 const comp = comp: {
17241704 // We put the `Compilation` itself in the arena. Freeing the arena will free the module.
17251705 // It's initialized later after we prepare the initialization options.
17261706 const root_name = try arena.dupeSentinel(u8, options.root_name, 0);
......@@ -1988,7 +1968,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
19881968 // TODO audit this and make sure everything is in it
19891969
19901970 const comp = try arena.create(Compilation);
1991 const opt_zcu: ?*Zcu = if (have_zcu) blk: {
1971 const opt_zcu = if (have_zcu) blk: {
19921972 // Pre-open the directory handles for cached ZIR code so that it does not need
19931973 // to redundantly happen for each AstGen operation.
19941974 const zir_sub_dir = "z";
......@@ -2041,19 +2021,95 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
20412021 zcu.* = .{
20422022 .gpa = gpa,
20432023 .comp = comp,
2044 .main_mod = main_mod,
2024 .llvm_object = null,
2025
20452026 .root_mod = options.root_mod,
2027 .main_mod = main_mod,
20462028 .std_mod = std_mod,
2029 .sema_prog_node = .none,
2030 .codegen_prog_node = .none,
2031 .pending_codegen_jobs = .init(0),
2032
2033 .cur_sema_prog_node = .none,
2034
20472035 .global_zir_cache = global_zir_cache,
20482036 .local_zir_cache = local_zir_cache,
2037
2038 .all_exports = .empty,
2039 .free_exports = .empty,
2040 .single_exports = .empty,
2041 .multi_exports = .empty,
2042
2043 .module_roots = .empty,
2044 .builtin_modules = .empty,
2045
2046 .import_table = .empty,
2047 .alive_files = .empty,
2048 .embed_table = .empty,
2049
2050 .intern_pool = .empty,
2051
2052 .compile_logs = .empty,
2053 .compile_log_lines = .empty,
2054 .free_compile_log_lines = .empty,
2055
2056 .illegal_module_root = null,
2057 .multi_module_err = null,
2058 .failed_imports = .empty,
2059 .failed_files = .empty,
2060 .skip_analysis_this_update = false,
2061 .analysis_in_progress = .empty,
2062 .failed_analysis = .empty,
2063 .transitive_failed_analysis = .empty,
2064 .dependency_loops = .empty,
2065 .dependency_loop_nodes = .empty,
2066 .failed_codegen = .empty,
2067 .failed_types = .empty,
2068 .failed_exports = .empty,
2069
20492070 .error_limit = error_limit,
2050 .llvm_object = null,
2071
2072 .outdated_lock = if (std.debug.runtime_safety) .init,
2073 .potentially_outdated = .empty,
2074 .outdated = .empty,
2075 .outdated_ready = .{
2076 .funcs = .empty,
2077 .other = .empty,
2078 },
2079 .retryable_failures = .empty,
2080
20512081 .analysis_roots_buffer = undefined,
20522082 .analysis_roots_len = 0,
2083 .resolved_references = null,
2084
2085 .test_filters = options.test_filters,
2086 .test_functions = .empty,
2087
2088 .global_assembly = .empty,
2089
2090 .reference_table = .empty,
2091 .all_references = .empty,
2092 .free_references = .empty,
2093
2094 .inline_reference_frames = .empty,
2095 .free_inline_reference_frames = .empty,
2096
2097 .type_reference_table = .empty,
2098 .all_type_references = .empty,
2099 .free_type_references = .empty,
2100
2101 .std_lang_decl_values = .initFill(.none),
2102
2103 .incremental_debug_state = if (build_options.enable_debug_extensions) .init,
2104
2105 .cur_analysis_timer = null,
2106
20532107 .codegen_task_pool = try .init(arena),
2108
2109 .generation = 0,
2110
20542111 .anon_name_counter = 0,
20552112 };
2056 try zcu.init(gpa, io, options.thread_limit);
20572113 break :blk zcu;
20582114 } else blk: {
20592115 if (options.emit_h != .no) return diag.fail(.emit_h_without_zcu);
......@@ -2091,7 +2147,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
20912147 .reference_trace = options.reference_trace,
20922148 .time_report = if (options.time_report) .init else null,
20932149 .stack_report = options.stack_report,
2094 .test_filters = options.test_filters,
20952150 .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
20962151 .debug_compile_errors = options.debug_compile_errors,
20972152 .debug_incremental = options.debug_incremental,
......@@ -2143,18 +2198,8 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
21432198 comp.config.any_sanitize_c = any_sanitize_c;
21442199 comp.config.any_fuzz = any_fuzz;
21452200
2146 if (opt_zcu) |zcu| {
2147 // Finish initializing the `zcu` after the fields on `comp` have been initialized.
2148 zcu.initAfterCompilation();
2149
2150 // Populate `zcu.module_roots`.
2151 const active = zcu.acquire();
2152 defer active.release();
2153 active.pt.populateModuleRootTable() catch |err| switch (err) {
2154 error.OutOfMemory => |e| return e,
2155 error.IllegalZigImport => return diag.fail(.illegal_zig_import),
2156 };
2157 }
2201 // Initialize the `zcu` after the fields on `comp` have been initialized.
2202 if (opt_zcu) |zcu| zcu.init();
21582203
21592204 const lf_open_opts: link.File.OpenOptions = .{
21602205 .linker_script = options.linker_script,
......@@ -2250,20 +2295,10 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
22502295 hash.add(options.config.is_test);
22512296 hash.addListOfBytes(options.test_filters);
22522297 hash.add(options.skip_linker_dependencies);
2253 hash.add(options.emit_h != .no);
2298 //hash.add(options.emit_h != .no);
22542299 hash.add(error_limit);
22552300
2256 // Here we put the root source file path name, but *not* with addFile.
2257 // We want the hash to be the same regardless of the contents of the
2258 // source file, because incremental compilation will handle it, but we
2259 // do want to namespace different source file names because they are
2260 // likely different compilations and therefore this would be likely to
2261 // cause cache hits.
2262 if (comp.zcu) |zcu| {
2263 try addModuleTableToCacheHash(zcu, &hash);
2264 } else {
2265 cache_helpers.addModule(&hash, options.root_mod);
2266 }
2301 try comp.addModuleTableToCacheHash(&hash);
22672302
22682303 // In the case of incremental cache mode, this `artifact_directory`
22692304 // is computed based on a hash of non-linker inputs, and it is where all
......@@ -2315,22 +2350,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
23152350 comp.cache_use = .{ .whole = whole };
23162351 },
23172352 }
2318
2319 if (use_llvm and
2320 (comp.emit_bin != null or
2321 comp.emit_asm != null or
2322 comp.emit_llvm_ir != null or
2323 comp.emit_llvm_bc != null or
2324 log_llvm_ir: {
2325 break :log_llvm_ir debugLogScopeEnabled("llvm-ir") or debugLogScopeEnabled("opt-llvm-ir");
2326 }))
2327 {
2328 if (opt_zcu) |zcu| {
2329 dev.check(.llvm_backend);
2330 zcu.llvm_object = try LlvmObject.create(arena, zcu);
2331 }
2332 }
2333
23342353 break :comp comp;
23352354 };
23362355 errdefer comp.destroy();
......@@ -2599,8 +2618,8 @@ pub fn destroy(comp: *Compilation) void {
25992618 }
26002619 comp.win32_resources.deinit(gpa);
26012620
2602 for (comp.failed_win32_resources.values()) |*value| {
2603 value.deinit(gpa);
2621 for (comp.failed_win32_resources.values()) |*bundle| {
2622 bundle.deinit(gpa);
26042623 }
26052624 comp.failed_win32_resources.deinit(gpa);
26062625
......@@ -2624,8 +2643,8 @@ pub fn clearMiscFailures(comp: *Compilation) void {
26242643 comp.misc_failures = .{};
26252644}
26262645
2627pub fn getTarget(self: *const Compilation) *const Target {
2628 return &self.root_mod.resolved_target.result;
2646pub fn getTarget(comp: *const Compilation) *const Target {
2647 return &comp.root_mod.resolved_target.result;
26292648}
26302649
26312650/// Only legal to call when cache mode is incremental and a link file is present.
......@@ -2644,6 +2663,18 @@ pub fn hotCodeSwap(
26442663fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
26452664 const io = comp.io;
26462665
2666 if (comp.bin_file) |lf| {
2667 lf.endProgress();
2668 comp.link_prog_node.end();
2669 comp.link_prog_node = .none;
2670 switch (comp.cache_use) {
2671 .none, .whole => {
2672 lf.destroy();
2673 comp.bin_file = null;
2674 },
2675 .incremental => {},
2676 }
2677 }
26472678 switch (comp.cache_use) {
26482679 .none => |none| {
26492680 if (none.tmp_artifact_directory) |*tmp_dir| {
......@@ -2660,17 +2691,14 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
26602691 // replicated in the output binary (the output instead contains a reference to that
26612692 // file which debug tooling can look through). So, in that particular case, we need
26622693 // to keep this directory around so that the output binary can be debugged.
2663 if (comp.bin_file != null and comp.getTarget().ofmt == .macho and comp.config.debug_format != .strip) {
2664 // We are emitting an unstripped Mach-O binary with the LLVM backend: the ZCU
2665 // object file must remain on-disk for its debug info.
2666 return;
2694 if (comp.bin_file == null or comp.getTarget().ofmt != .macho or comp.config.debug_format == .strip) {
2695 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2696 comp.dirs.local_cache.handle.deleteTree(io, tmp_dir_sub_path) catch |err| {
2697 log.warn("failed to delete temporary directory '{s}{c}{s}': {t}", .{
2698 comp.dirs.local_cache.path orelse ".", fs.path.sep, tmp_dir_sub_path, err,
2699 });
2700 };
26672701 }
2668 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2669 comp.dirs.local_cache.handle.deleteTree(io, tmp_dir_sub_path) catch |err| {
2670 log.warn("failed to delete temporary directory '{s}{c}{s}': {t}", .{
2671 comp.dirs.local_cache.path orelse ".", fs.path.sep, tmp_dir_sub_path, err,
2672 });
2673 };
26742702 }
26752703 },
26762704 .incremental => return,
......@@ -2679,10 +2707,6 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
26792707 man.deinit();
26802708 whole.cache_manifest = null;
26812709 }
2682 if (comp.bin_file) |lf| {
2683 lf.destroy();
2684 comp.bin_file = null;
2685 }
26862710 if (whole.tmp_artifact_directory) |*tmp_dir| {
26872711 tmp_dir.handle.close(io);
26882712 whole.tmp_artifact_directory = null;
......@@ -2695,6 +2719,13 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
26952719 }
26962720 },
26972721 }
2722 assert(!comp.config.incremental); // always uses incremental cache mode
2723 if (comp.zcu) |zcu| {
2724 if (zcu.llvm_object) |llvm_object| {
2725 llvm_object.deinit();
2726 zcu.llvm_object = null;
2727 }
2728 }
26982729}
26992730
27002731pub const UpdateError = error{
......@@ -2716,11 +2747,24 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
27162747 defer arena_allocator.deinit();
27172748 const arena = arena_allocator.allocator();
27182749
2719 comp.clearMiscFailures();
2720 comp.last_update_was_cache_hit = false;
2721 if (comp.time_report) |*tr| {
2722 tr.deinit(gpa); // this is information about an old update
2723 tr.* = .init;
2750 reset: {
2751 comp.clearMiscFailures();
2752 comp.last_update_was_cache_hit = false;
2753 if (comp.time_report) |*tr| {
2754 tr.deinit(comp.gpa);
2755 tr.* = .init;
2756 }
2757 if (comp.file_system_inputs) |file_system_inputs| file_system_inputs.clearRetainingCapacity();
2758
2759 if (comp.zcu) |zcu| {
2760 zcu.skip_analysis_this_update = false;
2761 if (zcu.generation > 0) break :reset;
2762 try zcu.reset(arena);
2763 }
2764 for (comp.failed_c_objects.values()) |bundle| bundle.destroy(comp.gpa);
2765 comp.failed_c_objects.clearRetainingCapacity();
2766 for (comp.failed_win32_resources.values()) |*bundle| bundle.deinit(comp.gpa);
2767 comp.failed_win32_resources.clearRetainingCapacity();
27242768 }
27252769
27262770 var tmp_dir_rand_int: u64 = undefined;
......@@ -2865,39 +2909,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
28652909 if (comp.zcu) |zcu| {
28662910 assert(zcu.cur_analysis_timer == null);
28672911
2868 zcu.skip_analysis_this_update = false;
2869
28702912 // TODO: doing this in `resolveReferences` later could avoid adding inputs for dead embedfiles. Investigate!
28712913 for (zcu.embed_table.keys()) |embed_file| {
28722914 try comp.appendFileSystemInput(embed_file.path);
28732915 }
2874
2875 zcu.analysis_roots_len = 0;
2876
2877 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod;
2878 zcu.analysis_roots_len += 1;
2879
2880 // Normally we rely on importing std to in turn import the root source file in the start code.
2881 // However, the main module is distinct from the root module in tests, so that won't happen there.
2882 if (comp.config.is_test and zcu.main_mod != zcu.std_mod) {
2883 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod;
2884 zcu.analysis_roots_len += 1;
2885 }
2886
2887 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2888 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod;
2889 zcu.analysis_roots_len += 1;
2890 }
2891
2892 if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
2893 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod;
2894 zcu.analysis_roots_len += 1;
2895 }
2896
2897 if (zcu.root_mod.deps.get("zigc")) |zigc_mod| {
2898 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zigc_mod;
2899 zcu.analysis_roots_len += 1;
2900 }
29012916 }
29022917
29032918 // The linker progress node is set up here instead of in `performAllTheWork`, because
......@@ -2919,11 +2934,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
29192934 comp.link_prog_node = main_progress_node.start("Linking", initial_estimated_total);
29202935 lf.startProgress(comp.link_prog_node);
29212936 }
2922 defer if (comp.bin_file) |lf| {
2923 lf.endProgress();
2924 comp.link_prog_node.end();
2925 comp.link_prog_node = .none;
2926 };
29272937
29282938 try comp.performAllTheWork(main_progress_node, arena);
29292939
......@@ -2969,7 +2979,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
29692979 comp.setMiscFailure(.link_depfile, "writing linker dependency file failed: {t}", .{err});
29702980 };
29712981
2972 if (anyErrors(comp)) {
2982 if (comp.anyErrors()) {
29732983 // Skip flushing and keep source files loaded for error reporting.
29742984 return;
29752985 }
......@@ -2983,7 +2993,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
29832993 }
29842994
29852995 switch (comp.cache_use) {
2986 .none, .incremental => try flush(comp, arena),
2996 .none, .incremental => try comp.flush(arena),
29872997 .whole => |whole| {
29882998 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
29892999 if (comp.parent_whole_cache) |pwc| {
......@@ -3064,11 +3074,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
30643074 };
30653075 }
30663076
3067 try flush(comp, arena);
3077 try comp.flush(arena);
30683078
30693079 // Calling `flush` may have produced errors, in which case the
30703080 // cache manifest must not be written.
3071 if (anyErrors(comp)) return;
3081 if (comp.anyErrors()) return;
30723082
30733083 // Release the lock and close the file before writing the manifest.
30743084 // This avoids the potential of another process getting a cache hit and
......@@ -3168,7 +3178,6 @@ fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error
31683178 defer tid.release(io);
31693179 if (comp.zcu) |zcu| {
31703180 if (zcu.llvm_object) |llvm_object| {
3171
31723181 // Emit the ZCU object from LLVM now; it's required to flush the output file.
31733182 // If there's an output file, it wants to decide where the LLVM object goes!
31743183 const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0);
......@@ -3303,23 +3312,42 @@ fn renameTmpIntoCache(
33033312/// anything from the link cache manifest.
33043313pub const link_hash_implementation_version = 14;
33053314
3306fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
3307 comptime assert(link_hash_implementation_version == 14);
3315fn addModuleTableToCacheHash(comp: *Compilation, hash: *Cache.HashHelper) Allocator.Error!void {
3316 const gpa = comp.gpa;
3317 var mods: std.array_hash_map.Auto(*Module, void) = .empty;
3318 defer mods.deinit(gpa);
3319
3320 try mods.putNoClobber(gpa, comp.root_mod, {});
3321 if (comp.zcu) |zcu| try mods.put(gpa, zcu.main_mod, {});
3322
3323 var mod_index: usize = 0;
3324 while (mod_index < mods.count()) : (mod_index += 1) {
3325 const mod = mods.keys()[mod_index];
33083326
3309 if (comp.zcu) |zcu| {
33103327 // No need to hash the actual file contents here because it is
33113328 // redundant with the logic in `PerThread.update` which iterates over
33123329 // `zcu.alive_files` and adds those files discovered via `@import` to
33133330 // the whole cache manifest.
3314 try addModuleTableToCacheHash(zcu, &man.hash);
3331 cache_helpers.addModule(hash, mod);
3332 hash.add(mod.root.root);
3333 hash.addBytes(mod.root.sub_path);
3334 hash.addBytes(mod.root_src_path);
3335 hash.addListOfBytes(mod.deps.keys());
33153336
3337 for (mod.deps.values()) |dep_mod| try mods.put(gpa, dep_mod, {});
3338 }
3339}
3340
3341fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
3342 comptime assert(link_hash_implementation_version == 14);
3343
3344 try comp.addModuleTableToCacheHash(&man.hash);
3345
3346 if (comp.zcu) |zcu| {
33163347 // Synchronize with other matching comments: ZigOnlyHashStuff
3317 man.hash.addListOfBytes(comp.test_filters);
3318 man.hash.add(comp.skip_linker_dependencies);
3348 man.hash.addListOfBytes(zcu.test_filters);
33193349 //man.hash.add(zcu.emit_h != .no);
33203350 man.hash.add(zcu.error_limit);
3321 } else {
3322 cache_helpers.addModule(&man.hash, comp.root_mod);
33233351 }
33243352
33253353 try link.hashInputs(man, comp.link_inputs);
......@@ -3788,7 +3816,7 @@ fn addBuf(list: *std.array_list.Managed([]const u8), buf: []const u8) void {
37883816}
37893817
37903818/// This function is temporally single-threaded.
3791pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
3819pub fn getAllErrorsAlloc(comp: *Compilation) Allocator.Error!ErrorBundle {
37923820 const gpa = comp.gpa;
37933821 const io = comp.io;
37943822
......@@ -3831,6 +3859,15 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
38313859 }
38323860
38333861 if (comp.zcu) |zcu| zcu_errors: {
3862 if (zcu.illegal_module_root) |mod| {
3863 try bundle.addRootErrorMessage(.{
3864 .msg = try bundle.printString(
3865 "this compiler implementation does not support using '{f}/{s}' as the root of module '{s}'",
3866 .{ mod.root.fmt(comp), mod.root_src_path, mod.fully_qualified_name },
3867 ),
3868 .notes_len = 0,
3869 });
3870 }
38343871 if (zcu.multi_module_err != null) {
38353872 try zcu.addFileInMultipleModulesError(&bundle);
38363873 break :zcu_errors;
......@@ -4359,7 +4396,9 @@ fn performAllTheWork(
43594396 defer if (comp.zcu) |zcu| zcu.codegen_task_pool.cancel(zcu);
43604397 if (comp.zcu) |zcu| {
43614398 // Regardless of errors, `comp.zcu` needs to update its generation number.
4362 defer zcu.generation += 1;
4399 defer if (comp.config.incremental) {
4400 zcu.generation += 1;
4401 };
43634402 const active = zcu.acquire();
43644403 defer active.release();
43654404 try active.pt.update(main_progress_node, &decl_work_timer);
src/Zcu.zig+250-156
......@@ -40,6 +40,7 @@ const AnalUnit = InternPool.AnalUnit;
4040const BuiltinFn = std.zig.BuiltinFn;
4141const codegen = @import("codegen.zig");
4242const LlvmObject = @import("codegen/llvm.zig").Object;
43const debugLogScopeEnabled = @import("main.zig").debugLogScopeEnabled;
4344const dev = @import("dev.zig");
4445const Zoir = std.zig.Zoir;
4546const ZonGen = std.zig.ZonGen;
......@@ -69,18 +70,18 @@ root_mod: *Module,
6970/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
7071main_mod: *Module,
7172std_mod: *Module,
72sema_prog_node: std.Progress.Node = .none,
73codegen_prog_node: std.Progress.Node = .none,
73sema_prog_node: std.Progress.Node,
74codegen_prog_node: std.Progress.Node,
7475/// The number of codegen jobs which are pending or in-progress. Whichever thread drops this value
7576/// to 0 is responsible for ending `codegen_prog_node`. While semantic analysis is happening, this
7677/// value bottoms out at 1 instead of 0, to ensure that it can only drop to 0 after analysis is
7778/// completed (since semantic analysis could trigger more codegen work).
78pending_codegen_jobs: std.atomic.Value(u32) = .init(0),
79pending_codegen_jobs: std.atomic.Value(u32),
7980
8081/// This is the progress node *under* `sema_prog_node` which is currently running.
8182/// When we have to pause to analyze something else, we just temporarily rename this node.
8283/// Eventually, when we thread semantic analysis, we will want one of these per thread.
83cur_sema_prog_node: std.Progress.Node = .none,
84cur_sema_prog_node: std.Progress.Node,
8485
8586/// Used by AstGen worker to load and store ZIR cache.
8687global_zir_cache: Cache.Directory,
......@@ -89,27 +90,26 @@ local_zir_cache: Cache.Directory,
8990
9091/// This is where all `Export` values are stored. Not all values here are necessarily valid exports;
9192/// to enumerate all exports, `single_exports` and `multi_exports` must be consulted.
92all_exports: std.ArrayList(Export) = .empty,
93all_exports: std.ArrayList(Export),
9394/// This is a list of free indices in `all_exports`. These indices may be reused by exports from
9495/// future semantic analysis.
95free_exports: std.ArrayList(Export.Index) = .empty,
96free_exports: std.ArrayList(Export.Index),
9697/// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of
9798/// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit`
9899/// whose analysis triggered the export.
99single_exports: std.array_hash_map.Auto(AnalUnit, Export.Index) = .empty,
100single_exports: std.array_hash_map.Auto(AnalUnit, Export.Index),
100101/// Like `single_exports`, but for `AnalUnit`s which perform multiple exports.
101102/// The exports are `all_exports.items[index..][0..len]`.
102103multi_exports: std.array_hash_map.Auto(AnalUnit, extern struct {
103104 index: u32,
104105 len: u32,
105}) = .empty,
106
107/// Key is the digest returned by `Builtin.hash`; value is the corresponding module.
108builtin_modules: std.array_hash_map.Auto(Cache.BinDigest, *Module) = .empty,
106}),
109107
110108/// Populated as soon as the `Compilation` is created. Guaranteed to contain all modules, even builtin ones.
111109/// Modules whose root file is not a Zig or ZON file have the value `.none`.
112module_roots: std.array_hash_map.Auto(*Module, File.Index.Optional) = .empty,
110module_roots: std.array_hash_map.Auto(*Module, File.Index.Optional),
111/// Key is the digest returned by `Builtin.hash`; value is the corresponding module.
112builtin_modules: std.array_hash_map.Auto(Cache.BinDigest, *Module),
113113
114114/// The set of all the Zig source files in the Zig Compilation Unit. Tracked in
115115/// order to iterate over it and check which source files have been modified on
......@@ -132,26 +132,14 @@ import_table: std.array_hash_map.Custom(
132132 pub const eql = @compileError("all accesses should be through ImportTableAdapter");
133133 },
134134 true, // This is necessary! Without it, the map tries to use its Context to rehash. #21918
135) = .empty,
136
135),
137136/// The set of all files in `import_table` which are "alive" this update, meaning
138137/// they are reachable by traversing imports starting from an analysis root. This
139138/// is usually all files in `import_table`, but some could be omitted if an incremental
140139/// update removes an import, or if a module specified on the CLI is never imported.
141140/// Reconstructed on every update, after AstGen and before Sema.
142141/// Value is why the file is alive.
143alive_files: std.array_hash_map.Auto(File.Index, File.Reference) = .empty,
144
145/// If this is populated, a "file exists in multiple modules" error should be emitted.
146/// This causes file errors to not be shown, because we don't really know which files
147/// should be alive (because the user has messed up their imports somewhere!).
148/// Cleared and recomputed every update, after AstGen and before Sema.
149multi_module_err: ?struct {
150 file: File.Index,
151 modules: [2]*Module,
152 refs: [2]File.Reference,
153} = null,
154
142alive_files: std.array_hash_map.Auto(File.Index, File.Reference),
155143/// The set of all the files which have been loaded with `@embedFile` in the Module.
156144/// We keep track of this in order to iterate over it and check which files have been
157145/// modified on the file system when an update is requested, as well as to cache
......@@ -162,49 +150,19 @@ multi_module_err: ?struct {
162150///
163151/// This table owns all of the `*EmbedFile` memory, which is allocated into gpa.
164152embed_table: std.array_hash_map.Custom(
165 *EmbedFile,
153 EmbedFile,
166154 void,
167155 struct {
168156 pub const hash = @compileError("all accesses should be through EmbedTableAdapter");
169157 pub const eql = @compileError("all accesses should be through EmbedTableAdapter");
170158 },
171159 true, // This is necessary! Without it, the map tries to use its Context to rehash. #21918
172) = .empty,
160),
173161
174162/// Stores all Type and Value objects.
175163/// The idea is that this will be periodically garbage-collected, but such logic
176164/// is not yet implemented.
177intern_pool: InternPool = .empty,
178
179/// Value explains why this `AnalUnit` is being analyzed. It is `null` for the topmost analysis
180/// (index 0), and non-`null` for all others.
181analysis_in_progress: std.array_hash_map.Auto(AnalUnit, ?*const DependencyReason) = .empty,
182/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
183failed_analysis: std.array_hash_map.Auto(AnalUnit, *ErrorMsg) = .empty,
184/// This `AnalUnit` failed semantic analysis because it required analysis of another `AnalUnit` which itself failed.
185transitive_failed_analysis: std.array_hash_map.Auto(
186 AnalUnit,
187 if (build_options.enable_debug_extensions) TransitiveFailureReason else void,
188) = .empty,
189/// This `Nav` succeeded analysis, but failed codegen.
190/// This may be a simple "value" `Nav`, or it may be a function.
191/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
192/// While multiple threads are active (most of the time!), this is guarded by `zcu.comp.mutex`, as
193/// codegen and linking run on a separate thread.
194failed_codegen: std.array_hash_map.Auto(InternPool.Nav.Index, *ErrorMsg) = .empty,
195failed_types: std.array_hash_map.Auto(InternPool.Index, *ErrorMsg) = .empty,
196
197/// Key is an `AnalUnit` which is in `dependency_loop_nodes`. For each dependency loop, exactly one
198/// unit in the loop is in this map, though the choice is arbitrary and not necessarily reproducible
199/// between compilations. So, instead of (for instance) defining where the dependency loop "starts",
200/// this map simply exists to allow easily iterating all dependency loops exactly once.
201dependency_loops: std.array_hash_map.Auto(AnalUnit, void) = .empty,
202/// Key is an `AnalUnit`, value is the `AnalUnit` which the key references and why it does so.
203/// All units in here form loops. To iterate loops, see `dependency_loops`.
204dependency_loop_nodes: std.array_hash_map.Auto(AnalUnit, struct {
205 unit: AnalUnit,
206 reason: DependencyReason,
207}) = .empty,
165intern_pool: InternPool,
208166
209167/// Keep track of `@compileLog`s per `AnalUnit`.
210168/// We track the source location of the first `@compileLog` call, and all logged lines as a linked list.
......@@ -220,17 +178,20 @@ compile_logs: std.array_hash_map.Auto(AnalUnit, struct {
220178 .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset),
221179 };
222180 }
223}) = .empty,
224compile_log_lines: std.ArrayList(CompileLogLine) = .empty,
225free_compile_log_lines: std.ArrayList(CompileLogLine.Index) = .empty,
226/// This tracks files which triggered errors when generating AST/ZIR/ZOIR.
227/// If not `null`, the value is a retryable error (the file status is guaranteed
228/// to be `.retryable_failure`). Otherwise, the file status is `.astgen_failure`
229/// or `.success`, and there are ZIR/ZOIR errors which should be printed.
230/// We just store a `[]u8` instead of a full `*ErrorMsg`, because the source
231/// location is always the entire file. The `[]u8` memory is owned by the map
232/// and allocated into `gpa`.
233failed_files: std.array_hash_map.Auto(File.Index, ?[]u8) = .empty,
181}),
182compile_log_lines: std.ArrayList(CompileLogLine),
183free_compile_log_lines: std.ArrayList(CompileLogLine.Index),
184
185illegal_module_root: ?*Module,
186/// If this is populated, a "file exists in multiple modules" error should be emitted.
187/// This causes file errors to not be shown, because we don't really know which files
188/// should be alive (because the user has messed up their imports somewhere!).
189/// Cleared and recomputed every update, after AstGen and before Sema.
190multi_module_err: ?struct {
191 file: File.Index,
192 modules: [2]*Module,
193 refs: [2]File.Reference,
194},
234195/// AstGen is not aware of modules, and so cannot determine whether an import
235196/// string makes sense. That is the job of a traversal after AstGen.
236197///
......@@ -257,11 +218,48 @@ failed_imports: std.ArrayList(struct {
257218 import_string: Zir.NullTerminatedString,
258219 import_token: Ast.TokenIndex,
259220 kind: enum { file_outside_module_root, illegal_zig_import },
260}) = .empty,
261failed_exports: std.array_hash_map.Auto(Export.Index, *ErrorMsg) = .empty,
262/// If analysis failed due to a cimport error, the corresponding Clang errors
263/// are stored here.
264cimport_errors: std.array_hash_map.Auto(AnalUnit, std.zig.ErrorBundle) = .empty,
221}),
222/// This tracks files which triggered errors when generating AST/ZIR/ZOIR.
223/// If not `null`, the value is a retryable error (the file status is guaranteed
224/// to be `.retryable_failure`). Otherwise, the file status is `.astgen_failure`
225/// or `.success`, and there are ZIR/ZOIR errors which should be printed.
226/// We just store a `[]u8` instead of a full `*ErrorMsg`, because the source
227/// location is always the entire file. The `[]u8` memory is owned by the map
228/// and allocated into `gpa`.
229failed_files: std.array_hash_map.Auto(File.Index, ?[]u8),
230/// If `true`, then semantic analysis must not occur on this update due to AstGen errors.
231/// Essentially the entire pipeline after AstGen, including Sema, codegen, and link, is skipped.
232/// Reset to `false` at the start of each update in `Compilation.update`.
233skip_analysis_this_update: bool,
234/// Value explains why this `AnalUnit` is being analyzed. It is `null` for the topmost analysis
235/// (index 0), and non-`null` for all others.
236analysis_in_progress: std.array_hash_map.Auto(AnalUnit, ?*const DependencyReason),
237/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
238failed_analysis: std.array_hash_map.Auto(AnalUnit, *ErrorMsg),
239/// This `AnalUnit` failed semantic analysis because it required analysis of another `AnalUnit` which itself failed.
240transitive_failed_analysis: std.array_hash_map.Auto(
241 AnalUnit,
242 if (build_options.enable_debug_extensions) TransitiveFailureReason else void,
243),
244/// Key is an `AnalUnit` which is in `dependency_loop_nodes`. For each dependency loop, exactly one
245/// unit in the loop is in this map, though the choice is arbitrary and not necessarily reproducible
246/// between compilations. So, instead of (for instance) defining where the dependency loop "starts",
247/// this map simply exists to allow easily iterating all dependency loops exactly once.
248dependency_loops: std.array_hash_map.Auto(AnalUnit, void),
249/// Key is an `AnalUnit`, value is the `AnalUnit` which the key references and why it does so.
250/// All units in here form loops. To iterate loops, see `dependency_loops`.
251dependency_loop_nodes: std.array_hash_map.Auto(AnalUnit, struct {
252 unit: AnalUnit,
253 reason: DependencyReason,
254}),
255/// This `Nav` succeeded analysis, but failed codegen.
256/// This may be a simple "value" `Nav`, or it may be a function.
257/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
258/// While multiple threads are active (most of the time!), this is guarded by `zcu.comp.mutex`, as
259/// codegen and linking run on a separate thread.
260failed_codegen: std.array_hash_map.Auto(InternPool.Nav.Index, *ErrorMsg),
261failed_types: std.array_hash_map.Auto(InternPool.Index, *ErrorMsg),
262failed_exports: std.array_hash_map.Auto(Export.Index, *ErrorMsg),
265263
266264/// Maximum amount of distinct error values, set by --error-limit
267265error_limit: ErrorInt,
......@@ -269,14 +267,14 @@ error_limit: ErrorInt,
269267/// In safe builds, `Type.assertHasLayout` may be called cross-thread, so this lock
270268/// guards accesses to `outdated` and `potentially_outdated`. In unsafe builds, the
271269/// lock is not needed and is compiled out.
272outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void = if (std.debug.runtime_safety) .init,
270outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void,
273271/// Value is the number of PO dependencies of this AnalUnit.
274272/// This value will decrease as we perform semantic analysis to learn what is outdated.
275273/// If any of these PO deps is outdated, this value will be moved to `outdated`.
276potentially_outdated: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
274potentially_outdated: std.array_hash_map.Auto(AnalUnit, u32),
277275/// Value is the number of PO dependencies of this AnalUnit.
278276/// Once this value drops to 0, the AnalUnit is a candidate for re-analysis.
279outdated: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
277outdated: std.array_hash_map.Auto(AnalUnit, u32),
280278/// This is the set of all `AnalUnit`s in `outdated` whose PO dependency count is 0.
281279/// Such `AnalUnit`s are ready for immediate re-analysis.
282280/// See `findOutdatedToAnalyze` for details.
......@@ -286,69 +284,64 @@ outdated_ready: struct {
286284 funcs: std.array_hash_map.Auto(InternPool.Index, void),
287285 /// Does not contain `.func` units.
288286 other: std.array_hash_map.Auto(AnalUnit, void),
289} = .{ .funcs = .empty, .other = .empty },
287},
290288/// This contains a list of AnalUnit whose analysis or codegen failed, but the
291289/// failure was something like running out of disk space, and trying again may
292290/// succeed. On the next update, we will flush this list, marking all members of
293291/// it as outdated.
294retryable_failures: std.ArrayList(AnalUnit) = .empty,
292retryable_failures: std.ArrayList(AnalUnit),
295293
296294/// These are the modules which we initially queue for analysis in `Compilation.update`.
297295/// `resolveReferences` will use these as the root of its reachability traversal.
298296analysis_roots_buffer: [5]*Module,
299analysis_roots_len: usize = 0,
297analysis_roots_len: usize,
300298/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and
301299/// reset to `null` when any semantic analysis occurs (since this invalidates the data).
302300/// Allocated into `gpa`.
303resolved_references: ?std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) = null,
304
305/// If `true`, then semantic analysis must not occur on this update due to AstGen errors.
306/// Essentially the entire pipeline after AstGen, including Sema, codegen, and link, is skipped.
307/// Reset to `false` at the start of each update in `Compilation.update`.
308skip_analysis_this_update: bool = false,
301resolved_references: ?std.array_hash_map.Auto(AnalUnit, ?ResolvedReference),
309302
310test_functions: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty,
303test_filters: []const []const u8,
304test_functions: std.array_hash_map.Auto(InternPool.Nav.Index, void),
311305
312global_assembly: std.array_hash_map.Auto(AnalUnit, []u8) = .empty,
306global_assembly: std.array_hash_map.Auto(AnalUnit, []u8),
313307
314308/// Key is the `AnalUnit` *performing* the reference. This representation allows
315309/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
316310/// Value is index into `all_references` of the first reference triggered by the unit.
317311/// The `next` field on the `Reference` forms a linked list of all references
318312/// triggered by the key `AnalUnit`.
319reference_table: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
320all_references: std.ArrayList(Reference) = .empty,
313reference_table: std.array_hash_map.Auto(AnalUnit, u32),
314all_references: std.ArrayList(Reference),
321315/// Freelist of indices in `all_references`.
322free_references: std.ArrayList(u32) = .empty,
316free_references: std.ArrayList(u32),
323317
324inline_reference_frames: std.ArrayList(InlineReferenceFrame) = .empty,
325free_inline_reference_frames: std.ArrayList(InlineReferenceFrame.Index) = .empty,
318inline_reference_frames: std.ArrayList(InlineReferenceFrame),
319free_inline_reference_frames: std.ArrayList(InlineReferenceFrame.Index),
326320
327321/// Key is the `AnalUnit` *performing* the reference. This representation allows
328322/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
329323/// Value is index into `all_type_reference` of the first reference triggered by the unit.
330324/// The `next` field on the `TypeReference` forms a linked list of all type references
331325/// triggered by the key `AnalUnit`.
332type_reference_table: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
333all_type_references: std.ArrayList(TypeReference) = .empty,
326type_reference_table: std.array_hash_map.Auto(AnalUnit, u32),
327all_type_references: std.ArrayList(TypeReference),
334328/// Freelist of indices in `all_type_references`.
335free_type_references: std.ArrayList(u32) = .empty,
329free_type_references: std.ArrayList(u32),
336330
337331/// Populated by analysis of `AnalUnit.wrap(.{ .memoized_state = s })`, where `s` depends on the element.
338std_lang_decl_values: StdLangDecl.Memoized = .initFill(.none),
332std_lang_decl_values: StdLangDecl.Memoized,
339333
340incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void =
341 if (build_options.enable_debug_extensions) .init else {},
334incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void,
342335
343336/// Times semantic analysis of the current `AnalUnit`. When we pause to analyze a different unit,
344337/// this timer must be temporarily paused and resumed later.
345cur_analysis_timer: ?Compilation.Timer = null,
338cur_analysis_timer: ?Compilation.Timer,
346339
347340codegen_task_pool: CodegenTaskPool,
348341
349generation: u32 = 0,
342generation: u32,
350343
351/// Only access from the Sema thread.
344/// Only accessed from the Sema thread.
352345anon_name_counter: u32,
353346
354347pub const DependencyReason = struct {
......@@ -434,7 +427,7 @@ pub const EmbedTableAdapter = struct {
434427 _ = ctx;
435428 return @truncate(std.hash.Wyhash.hash(@backingInt(path.root), path.sub_path));
436429 }
437 pub fn eql(ctx: EmbedTableAdapter, a_path: Compilation.Path, b_file: *EmbedFile, b_index: usize) bool {
430 pub fn eql(ctx: EmbedTableAdapter, a_path: Compilation.Path, b_file: EmbedFile, b_index: usize) bool {
438431 _ = ctx;
439432 _ = b_index;
440433 const b_path = b_file.path;
......@@ -1250,10 +1243,15 @@ pub const EmbedFile = struct {
12501243
12511244 pub const Index = enum(u32) {
12521245 _,
1253 pub fn get(idx: Index, zcu: *const Zcu) *EmbedFile {
1254 return zcu.embed_table.keys()[@backingInt(idx)];
1246 pub fn get(idx: Index, zcu: *const Zcu) *const EmbedFile {
1247 return &zcu.embed_table.keys()[@backingInt(idx)];
12551248 }
12561249 };
1250
1251 pub fn deinit(ef: *EmbedFile, gpa: Allocator) void {
1252 ef.path.deinit(gpa);
1253 ef.* = undefined;
1254 }
12571255};
12581256
12591257/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
......@@ -2894,16 +2892,130 @@ pub const CompileError = error{
28942892 ComptimeBreak,
28952893};
28962894
2897pub fn init(zcu: *Zcu, gpa: Allocator, io: Io, thread_count: usize) !void {
2898 try zcu.intern_pool.init(gpa, io, thread_count);
2899}
2900
29012895/// It is valid to not call this function before `deinit` in error paths.
29022896/// Requires the fields on `zcu.comp` to already be initialized.
2903pub fn initAfterCompilation(zcu: *Zcu) void {
2897pub fn init(zcu: *Zcu) void {
29042898 zcu.initTracyPlots();
29052899}
29062900
2901pub fn reset(zcu: *Zcu, arena: Allocator) Allocator.Error!void {
2902 const comp = zcu.comp;
2903 const gpa = comp.gpa;
2904 const io = comp.io;
2905
2906 zcu.all_exports.clearRetainingCapacity();
2907 zcu.free_exports.clearRetainingCapacity();
2908 zcu.single_exports.clearRetainingCapacity();
2909 zcu.multi_exports.clearRetainingCapacity();
2910
2911 zcu.module_roots.clearRetainingCapacity();
2912 // builtin modules can still be reused from a previous non-incremental update
2913
2914 for (zcu.import_table.keys()) |file| zcu.destroyFile(file);
2915 zcu.import_table.clearRetainingCapacity();
2916 zcu.alive_files.clearRetainingCapacity();
2917 for (zcu.embed_table.keys()) |*ef| ef.deinit(gpa);
2918 zcu.embed_table.clearRetainingCapacity();
2919
2920 zcu.compile_logs.clearRetainingCapacity();
2921 zcu.compile_log_lines.clearRetainingCapacity();
2922 zcu.free_compile_log_lines.clearRetainingCapacity();
2923
2924 zcu.illegal_module_root = null;
2925 zcu.multi_module_err = null;
2926 zcu.failed_imports.clearRetainingCapacity();
2927 for (zcu.failed_files.values()) |maybe_msg| if (maybe_msg) |msg| gpa.free(msg);
2928 zcu.failed_files.clearRetainingCapacity();
2929 zcu.analysis_in_progress.clearRetainingCapacity();
2930 for (zcu.failed_analysis.values()) |msg| msg.destroy(gpa);
2931 zcu.failed_analysis.clearRetainingCapacity();
2932 zcu.transitive_failed_analysis.clearRetainingCapacity();
2933 zcu.dependency_loops.clearRetainingCapacity();
2934 zcu.dependency_loop_nodes.clearRetainingCapacity();
2935 for (zcu.failed_codegen.values()) |msg| msg.destroy(gpa);
2936 zcu.failed_codegen.clearRetainingCapacity();
2937 for (zcu.failed_types.values()) |msg| msg.destroy(gpa);
2938 zcu.failed_types.clearRetainingCapacity();
2939 for (zcu.failed_exports.values()) |msg| msg.destroy(gpa);
2940 zcu.failed_exports.clearRetainingCapacity();
2941
2942 assert(zcu.potentially_outdated.count() == 0);
2943 assert(zcu.outdated.count() == 0);
2944 assert(zcu.outdated_ready.funcs.count() == 0);
2945 assert(zcu.outdated_ready.other.count() == 0);
2946 zcu.retryable_failures.clearRetainingCapacity();
2947
2948 zcu.analysis_roots_len = 0;
2949 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod;
2950 zcu.analysis_roots_len += 1;
2951 // Normally we rely on importing std to in turn import the root source file in the start code.
2952 // However, the main module is distinct from the root module in tests, so that won't happen there.
2953 if (comp.config.is_test and zcu.main_mod != zcu.std_mod) {
2954 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod;
2955 zcu.analysis_roots_len += 1;
2956 }
2957 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2958 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod;
2959 zcu.analysis_roots_len += 1;
2960 }
2961 if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
2962 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod;
2963 zcu.analysis_roots_len += 1;
2964 }
2965 if (zcu.root_mod.deps.get("zigc")) |zigc_mod| {
2966 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zigc_mod;
2967 zcu.analysis_roots_len += 1;
2968 }
2969 zcu.clearCachedResolvedReferences();
2970
2971 zcu.test_functions.clearRetainingCapacity();
2972
2973 zcu.global_assembly.clearRetainingCapacity();
2974
2975 zcu.reference_table.clearRetainingCapacity();
2976 zcu.all_references.clearRetainingCapacity();
2977 zcu.free_references.clearRetainingCapacity();
2978
2979 zcu.inline_reference_frames.clearRetainingCapacity();
2980 zcu.free_inline_reference_frames.clearRetainingCapacity();
2981
2982 zcu.type_reference_table.clearRetainingCapacity();
2983 zcu.all_type_references.clearRetainingCapacity();
2984 zcu.free_type_references.clearRetainingCapacity();
2985
2986 zcu.std_lang_decl_values = .initFill(.none);
2987
2988 for (zcu.codegen_task_pool.task_funcs) |task_func| assert(task_func == .none);
2989
2990 assert(zcu.generation == 0);
2991
2992 zcu.anon_name_counter = 0;
2993
2994 zcu.intern_pool.deinit(gpa, io);
2995 zcu.intern_pool = .empty;
2996 try zcu.intern_pool.init(gpa, io, comp.thread_limit);
2997
2998 {
2999 const active = zcu.acquire();
3000 defer active.release();
3001 try active.pt.populateModuleRootTable();
3002 }
3003
3004 assert(zcu.llvm_object == null);
3005 if (comp.config.use_llvm and
3006 (comp.emit_bin != null or
3007 comp.emit_asm != null or
3008 comp.emit_llvm_ir != null or
3009 comp.emit_llvm_bc != null or
3010 log_llvm_ir: {
3011 break :log_llvm_ir debugLogScopeEnabled("llvm-ir") or debugLogScopeEnabled("opt-llvm-ir");
3012 }))
3013 {
3014 dev.check(.llvm_backend);
3015 zcu.llvm_object = try .create(if (comp.config.incremental) comp.arena else arena, zcu);
3016 }
3017}
3018
29073019pub fn deinit(zcu: *Zcu) void {
29083020 const comp = zcu.comp;
29093021 const io = comp.io;
......@@ -2911,70 +3023,54 @@ pub fn deinit(zcu: *Zcu) void {
29113023 {
29123024 if (zcu.llvm_object) |llvm_object| llvm_object.deinit();
29133025
2914 zcu.builtin_modules.deinit(gpa);
3026 zcu.global_zir_cache.handle.close(io);
3027 zcu.local_zir_cache.handle.close(io);
3028
3029 zcu.all_exports.deinit(gpa);
3030 zcu.free_exports.deinit(gpa);
3031 zcu.single_exports.deinit(gpa);
3032 zcu.multi_exports.deinit(gpa);
3033
29153034 zcu.module_roots.deinit(gpa);
2916 for (zcu.import_table.keys()) |file_index| {
2917 zcu.destroyFile(file_index);
2918 }
3035 zcu.builtin_modules.deinit(gpa);
3036
3037 for (zcu.import_table.keys()) |file| zcu.destroyFile(file);
29193038 zcu.import_table.deinit(gpa);
29203039 zcu.alive_files.deinit(gpa);
2921
2922 for (zcu.embed_table.keys()) |embed_file| {
2923 embed_file.path.deinit(gpa);
2924 gpa.destroy(embed_file);
2925 }
3040 for (zcu.embed_table.keys()) |*ef| ef.deinit(gpa);
29263041 zcu.embed_table.deinit(gpa);
29273042
2928 zcu.local_zir_cache.handle.close(io);
2929 zcu.global_zir_cache.handle.close(io);
3043 zcu.compile_logs.deinit(gpa);
3044 zcu.compile_log_lines.deinit(gpa);
3045 zcu.free_compile_log_lines.deinit(gpa);
29303046
2931 for (zcu.failed_analysis.values()) |value| value.destroy(gpa);
2932 for (zcu.failed_codegen.values()) |value| value.destroy(gpa);
2933 for (zcu.failed_types.values()) |value| value.destroy(gpa);
3047 zcu.failed_imports.deinit(gpa);
3048 for (zcu.failed_files.values()) |maybe_msg| if (maybe_msg) |msg| gpa.free(msg);
3049 zcu.failed_files.deinit(gpa);
29343050 zcu.analysis_in_progress.deinit(gpa);
3051 for (zcu.failed_analysis.values()) |msg| msg.destroy(gpa);
29353052 zcu.failed_analysis.deinit(gpa);
29363053 zcu.transitive_failed_analysis.deinit(gpa);
29373054 zcu.dependency_loops.deinit(gpa);
29383055 zcu.dependency_loop_nodes.deinit(gpa);
3056 for (zcu.failed_codegen.values()) |msg| msg.destroy(gpa);
29393057 zcu.failed_codegen.deinit(gpa);
3058 for (zcu.failed_types.values()) |msg| msg.destroy(gpa);
29403059 zcu.failed_types.deinit(gpa);
2941
2942 for (zcu.failed_files.values()) |value| {
2943 if (value) |msg| gpa.free(msg);
2944 }
2945 zcu.failed_files.deinit(gpa);
2946 zcu.failed_imports.deinit(gpa);
2947
2948 for (zcu.failed_exports.values()) |value| {
2949 value.destroy(gpa);
2950 }
3060 for (zcu.failed_exports.values()) |msg| msg.destroy(gpa);
29513061 zcu.failed_exports.deinit(gpa);
29523062
2953 for (zcu.cimport_errors.values()) |*errs| {
2954 errs.deinit(gpa);
2955 }
2956 zcu.cimport_errors.deinit(gpa);
2957
2958 zcu.compile_logs.deinit(gpa);
2959 zcu.compile_log_lines.deinit(gpa);
2960 zcu.free_compile_log_lines.deinit(gpa);
2961
2962 zcu.all_exports.deinit(gpa);
2963 zcu.free_exports.deinit(gpa);
2964 zcu.single_exports.deinit(gpa);
2965 zcu.multi_exports.deinit(gpa);
2966
29673063 zcu.potentially_outdated.deinit(gpa);
29683064 zcu.outdated.deinit(gpa);
29693065 zcu.outdated_ready.funcs.deinit(gpa);
29703066 zcu.outdated_ready.other.deinit(gpa);
29713067 zcu.retryable_failures.deinit(gpa);
29723068
3069 if (zcu.resolved_references) |*r| r.deinit(gpa);
3070
29733071 zcu.test_functions.deinit(gpa);
29743072
2975 for (zcu.global_assembly.values()) |s| {
2976 gpa.free(s);
2977 }
3073 for (zcu.global_assembly.values()) |s| gpa.free(s);
29783074 zcu.global_assembly.deinit(gpa);
29793075
29803076 zcu.reference_table.deinit(gpa);
......@@ -2988,8 +3084,6 @@ pub fn deinit(zcu: *Zcu) void {
29883084 zcu.all_type_references.deinit(gpa);
29893085 zcu.free_type_references.deinit(gpa);
29903086
2991 if (zcu.resolved_references) |*r| r.deinit(gpa);
2992
29933087 if (comp.debugIncremental()) {
29943088 zcu.incremental_debug_state.deinit(gpa);
29953089 }
......@@ -4352,8 +4446,8 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana
43524446 .unnamed_test => true,
43534447 .@"test", .decltest => a: {
43544448 const fqn_slice = nav.fqn.toSlice(ip);
4355 if (comp.test_filters.len > 0) {
4356 for (comp.test_filters) |test_filter| {
4449 if (zcu.test_filters.len > 0) {
4450 for (zcu.test_filters) |test_filter| {
43574451 if (std.mem.find(u8, fqn_slice, test_filter) != null) break;
43584452 } else break :a false;
43594453 }
src/Zcu/PerThread.zig+46-44
......@@ -148,6 +148,12 @@ pub fn update(
148148 comp.link_queue.finishZcuQueue(comp);
149149 }
150150
151 if (zcu.illegal_module_root) |_| {
152 // We cannot run AstGen because we failed to set up the module roots. We can return early
153 // because this is a guaranteed compile error.
154 return;
155 }
156
151157 {
152158 const tracy_trace = traceNamed(@src(), "astgen");
153159 defer tracy_trace.end();
......@@ -203,11 +209,9 @@ pub fn update(
203209 // On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here
204210 // because `workerUpdateEmbedFile` can't invalidate it. The different here is that one
205211 // `@embedFile` can't trigger analysis of a new `@embedFile`!
206 for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| {
212 for (0.., zcu.embed_table.keys()) |ef_index_usize, *ef| {
207213 const ef_index: Zcu.EmbedFile.Index = @fromBackingInt(@intCast(ef_index_usize));
208 astgen_group.async(io, workerUpdateEmbedFile, .{
209 comp, ef_index, ef,
210 });
214 astgen_group.async(io, workerUpdateEmbedFile, .{ comp, ef_index, ef });
211215 }
212216
213217 try astgen_group.await(io);
......@@ -219,8 +223,8 @@ pub fn update(
219223 // they belong to.
220224 const any_fatal_files = try pt.computeAliveFiles();
221225
222 // If the cache mode is `whole`, add every alive source file to the manifest.
223226 switch (comp.cache_use) {
227 .none => {},
224228 .whole => |whole| if (whole.cache_manifest) |man| {
225229 for (zcu.alive_files.keys()) |file_index| {
226230 const file = zcu.fileByIndex(file_index);
......@@ -250,7 +254,11 @@ pub fn update(
250254 };
251255 }
252256 },
253 .none, .incremental => {},
257 .incremental => {
258 // Do not include file contents in the cache manifest because we
259 // want the hash to be the same regardless of the contents of the
260 // source files so that incremental can do its thing.
261 },
254262 }
255263
256264 if (comp.time_report) |*tr| {
......@@ -2494,11 +2502,7 @@ pub fn doImport(
24942502}
24952503/// This is called once during `Compilation.create` and never again. "builtin" modules don't yet
24962504/// exist, so are not added to `module_roots` here. They must be added when they are created.
2497pub fn populateModuleRootTable(pt: Zcu.PerThread) error{
2498 OutOfMemory,
2499 /// One of the specified modules had its root source file at an illegal path.
2500 IllegalZigImport,
2501}!void {
2505pub fn populateModuleRootTable(pt: Zcu.PerThread) Allocator.Error!void {
25022506 const zcu = pt.zcu;
25032507 const comp = zcu.comp;
25042508 const gpa = comp.gpa;
......@@ -2507,7 +2511,7 @@ pub fn populateModuleRootTable(pt: Zcu.PerThread) error{
25072511 // We'll initially add [mod, undefined] pairs, and when we reach the pair while
25082512 // iterating, rewrite the undefined value.
25092513 const roots = &zcu.module_roots;
2510 roots.clearRetainingCapacity();
2514 assert(roots.count() == 0);
25112515
25122516 // Start with:
25132517 // * `std_mod`, which is the main root of analysis
......@@ -2542,7 +2546,9 @@ pub fn populateModuleRootTable(pt: Zcu.PerThread) error{
25422546 errdefer path.deinit(gpa);
25432547
25442548 if (try path.isIllegalZigImport(gpa, zcu.comp.dirs)) {
2545 return error.IllegalZigImport;
2549 // One of the specified modules had its root source file at an illegal path.
2550 zcu.illegal_module_root = mod;
2551 return;
25462552 }
25472553
25482554 const gop = try zcu.import_table.getOrPutAdapted(gpa, path, Zcu.ImportTableAdapter{ .zcu = zcu });
......@@ -2781,15 +2787,21 @@ fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!struct
27812787 const gpa = comp.gpa;
27822788 const io = comp.io;
27832789
2784 const gop = try zcu.builtin_modules.getOrPut(gpa, opts.hash());
2785 if (gop.found_existing) return .{ // the `File` is up-to-date
2786 .file = zcu.module_roots.get(gop.value_ptr.*).?.unwrap().?,
2787 .module_root = gop.value_ptr.*,
2790 const mod: *Module = mod: {
2791 const gop = try zcu.builtin_modules.getOrPut(gpa, opts.hash());
2792 if (!gop.found_existing) {
2793 errdefer _ = zcu.builtin_modules.pop();
2794 gop.value_ptr.* = try .createBuiltin(comp.arena, opts, comp.dirs);
2795 }
2796 const mod = gop.value_ptr.*;
2797 assert(std.mem.eql(u8, &mod.getBuiltinOptions(comp.config).hash(), gop.key_ptr)); // builtin is its own builtin
2798 break :mod mod;
27882799 };
2789 errdefer _ = zcu.builtin_modules.pop();
27902800
2791 const mod: *Module = try .createBuiltin(comp.arena, opts, comp.dirs);
2792 assert(std.mem.eql(u8, &mod.getBuiltinOptions(comp.config).hash(), gop.key_ptr)); // builtin is its own builtin
2801 if (zcu.module_roots.get(mod)) |file| {
2802 // We have already loaded this builtin.zig before!
2803 return .{ .file = file.unwrap().?, .module_root = mod };
2804 }
27932805
27942806 const path = try mod.root.join(gpa, comp.dirs, "builtin.zig");
27952807 errdefer path.deinit(gpa);
......@@ -2827,7 +2839,6 @@ fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!struct
28272839 .root_type = .none,
28282840 });
28292841
2830 gop.value_ptr.* = mod;
28312842 file_gop.key_ptr.* = file_index;
28322843 zcu.module_roots.putAssumeCapacityNoClobber(mod, file_index.toOptional());
28332844 try opts.populateFile(gpa, file);
......@@ -2883,22 +2894,19 @@ pub fn embedFile(
28832894 return @fromBackingInt(@intCast(gop.index));
28842895 }
28852896 errdefer _ = zcu.embed_table.pop();
2886 gop.key_ptr.* = try pt.newEmbedFile(path);
2897 try pt.initEmbedFile(gop.key_ptr, path);
28872898 return @fromBackingInt(@intCast(gop.index));
28882899 }
28892900
2890 const embed_file: *Zcu.EmbedFile, const embed_file_idx: Zcu.EmbedFile.Index = ef: {
2901 const embed_file, const embed_file_idx: Zcu.EmbedFile.Index = ef: {
28912902 const path = try cur_file.path.upJoin(gpa, zcu.comp.dirs, import_string);
28922903 errdefer path.deinit(gpa);
28932904 const gop = try zcu.embed_table.getOrPutAdapted(gpa, path, Zcu.EmbedTableAdapter{});
2894 if (gop.found_existing) {
2895 path.deinit(gpa); // we're not using this key
2896 break :ef .{ gop.key_ptr.*, @fromBackingInt(@intCast(gop.index)) };
2897 } else {
2905 if (!gop.found_existing) {
28982906 errdefer _ = zcu.embed_table.pop();
2899 gop.key_ptr.* = try pt.newEmbedFile(path);
2900 break :ef .{ gop.key_ptr.*, @fromBackingInt(@intCast(gop.index)) };
2901 }
2907 try pt.initEmbedFile(gop.key_ptr, path);
2908 } else path.deinit(gpa); // we're not using this key
2909 break :ef .{ gop.key_ptr, @fromBackingInt(@intCast(gop.index)) };
29022910 };
29032911
29042912 switch (embed_file.path.isNested(cur_file.mod.?.root)) {
......@@ -2913,7 +2921,7 @@ pub fn updateEmbedFile(
29132921 pt: Zcu.PerThread,
29142922 ef: *Zcu.EmbedFile,
29152923 /// If not `null`, the interned file data is stored here, if it was loaded.
2916 /// `newEmbedFile` uses this to add the file to the `whole` cache manifest.
2924 /// `initEmbedFile` uses this to add the file to the `whole` cache manifest.
29172925 ip_str_out: ?*?InternPool.String,
29182926) Allocator.Error!void {
29192927 pt.updateEmbedFileInner(ef, ip_str_out) catch |err| switch (err) {
......@@ -3000,17 +3008,13 @@ fn updateEmbedFileInner(
30003008}
30013009
30023010/// Assumes that `path` is allocated into `gpa`. Takes ownership of `path` on success.
3003fn newEmbedFile(pt: Zcu.PerThread, path: Compilation.Path) EmbedFileError!*Zcu.EmbedFile {
3011fn initEmbedFile(pt: Zcu.PerThread, ef: *Zcu.EmbedFile, path: Compilation.Path) EmbedFileError!void {
30043012 const zcu = pt.zcu;
30053013 const comp = zcu.comp;
30063014 const io = comp.io;
3007 const gpa = comp.gpa;
30083015 const ip = &zcu.intern_pool;
30093016
3010 const new_file = try gpa.create(Zcu.EmbedFile);
3011 errdefer gpa.destroy(new_file);
3012
3013 new_file.* = .{
3017 ef.* = .{
30143018 .path = path,
30153019 .val = .none,
30163020 .err = null,
......@@ -3018,7 +3022,7 @@ fn newEmbedFile(pt: Zcu.PerThread, path: Compilation.Path) EmbedFileError!*Zcu.E
30183022 };
30193023
30203024 var opt_ip_str: ?InternPool.String = null;
3021 try pt.updateEmbedFile(new_file, &opt_ip_str);
3025 try pt.updateEmbedFile(ef, &opt_ip_str);
30223026
30233027 try comp.appendFileSystemInput(path);
30243028
......@@ -3031,19 +3035,17 @@ fn newEmbedFile(pt: Zcu.PerThread, path: Compilation.Path) EmbedFileError!*Zcu.E
30313035 const man = whole.cache_manifest orelse break :cache;
30323036 const ip_str = opt_ip_str orelse break :cache; // this will be a compile error
30333037
3034 const array_len = Value.fromInterned(new_file.val).typeOf(zcu).childType(zcu).arrayLen(zcu);
3038 const array_len = Value.fromInterned(ef.val).typeOf(zcu).childType(zcu).arrayLen(zcu);
30353039 const contents = ip_str.toSlice(array_len, ip);
30363040
30373041 try whole.cache_manifest_mutex.lock(io);
30383042 defer whole.cache_manifest_mutex.unlock(io);
30393043
3040 path.addToCacheManifestAsDiscovered(man, &comp.dirs, contents, new_file.stat) catch |err| switch (err) {
3044 path.addToCacheManifestAsDiscovered(man, &comp.dirs, contents, ef.stat) catch |err| switch (err) {
30413045 error.FileSystemFailure => unreachable, // contents and stat are both provided
30423046 else => |e| return e,
30433047 };
30443048 }
3045
3046 return new_file;
30473049}
30483050
30493051pub fn scanNamespace(
......@@ -3242,9 +3244,9 @@ const ScanDeclIter = struct {
32423244 // Perhaps we should add all test indiscriminately and filter at the end of the update.
32433245 if (!comp.config.is_test) break :a false;
32443246 if (file.mod != zcu.main_mod) break :a false;
3245 if (is_named and comp.test_filters.len > 0) {
3247 if (is_named and zcu.test_filters.len > 0) {
32463248 const fqn_slice = fqn.toSlice(ip);
3247 for (comp.test_filters) |test_filter| {
3249 for (zcu.test_filters) |test_filter| {
32483250 if (std.mem.find(u8, fqn_slice, test_filter) != null) break;
32493251 } else break :a false;
32503252 }
src/main.zig+1-4
......@@ -4413,13 +4413,12 @@ fn serve(
44134413 switch (hdr.tag) {
44144414 .exit => return cleanExit(io),
44154415 .update => {
4416 file_system_inputs.clearRetainingCapacity();
4417
44184416 if (arg_mode == .translate_c) {
44194417 var arena_instance = std.heap.ArenaAllocator.init(gpa);
44204418 defer arena_instance.deinit();
44214419 const arena = arena_instance.allocator();
44224420 var output: Compilation.TranslateCResult = undefined;
4421 file_system_inputs.clearRetainingCapacity();
44234422 try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node, environ_map);
44244423 defer output.deinit(gpa);
44254424
......@@ -4437,7 +4436,6 @@ fn serve(
44374436
44384437 continue;
44394438 }
4440
44414439 try comp.makeBinFileWritable();
44424440 try comp.update(main_progress_node);
44434441 try comp.makeBinFileExecutable();
......@@ -4463,7 +4461,6 @@ fn serve(
44634461 //);
44644462 },
44654463 .hot_update => {
4466 file_system_inputs.clearRetainingCapacity();
44674464 if (child_pid) |pid| {
44684465 try comp.hotCodeSwap(main_progress_node, pid);
44694466 try serveUpdateResults(&server, comp);
test/src/Cases.zig-1
......@@ -75,7 +75,6 @@ pub const Case = struct {
7575
7676 emit_asm: bool = false,
7777 emit_bin: bool = true,
78 emit_h: bool = false,
7978 is_test: bool = false,
8079 expect_exact: bool = false,
8180 backend: Backend = .auto,