| author | |
| committer | |
| log | ef35c3d5fefb8c14e17f3c7036bb21e808ee59be |
| tree | b2178084647ef4ac98bf4d45273b4708ff0b7607 |
| parent | dc6ffc28b57a96fd03f62bc665b6ed28b8e9e67b |
| parent | 3d8e760552bc60d2c7f1f4df9c8a05c8aae2b769 |
| signature |
incremental: bugfix (and a debugging feature that helped me do that bugfix)11 files changed, 651 insertions(+), 60 deletions(-)
lib/compiler/build_runner.zig+2| ... | @@ -236,6 +236,8 @@ pub fn main() !void { | ... | @@ -236,6 +236,8 @@ pub fn main() !void { |
| 236 | graph.debug_compiler_runtime_libs = true; | 236 | graph.debug_compiler_runtime_libs = true; |
| 237 | } else if (mem.eql(u8, arg, "--debug-compile-errors")) { | 237 | } else if (mem.eql(u8, arg, "--debug-compile-errors")) { |
| 238 | builder.debug_compile_errors = true; | 238 | builder.debug_compile_errors = true; |
| 239 | } else if (mem.eql(u8, arg, "--debug-incremental")) { | ||
| 240 | builder.debug_incremental = true; | ||
| 239 | } else if (mem.eql(u8, arg, "--system")) { | 241 | } else if (mem.eql(u8, arg, "--system")) { |
| 240 | // The usage text shows another argument after this parameter | 242 | // The usage text shows another argument after this parameter |
| 241 | // but it is handled by the parent process. The build runner | 243 | // but it is handled by the parent process. The build runner |
lib/std/Build.zig+2| ... | @@ -59,6 +59,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, | ... | @@ -59,6 +59,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, |
| 59 | args: ?[]const []const u8 = null, | 59 | args: ?[]const []const u8 = null, |
| 60 | debug_log_scopes: []const []const u8 = &.{}, | 60 | debug_log_scopes: []const []const u8 = &.{}, |
| 61 | debug_compile_errors: bool = false, | 61 | debug_compile_errors: bool = false, |
| 62 | debug_incremental: bool = false, | ||
| 62 | debug_pkg_config: bool = false, | 63 | debug_pkg_config: bool = false, |
| 63 | /// Number of stack frames captured when a `StackTrace` is recorded for debug purposes, | 64 | /// Number of stack frames captured when a `StackTrace` is recorded for debug purposes, |
| 64 | /// in particular at `Step` creation. | 65 | /// in particular at `Step` creation. |
| ... | @@ -385,6 +386,7 @@ fn createChildOnly( | ... | @@ -385,6 +386,7 @@ fn createChildOnly( |
| 385 | .cache_root = parent.cache_root, | 386 | .cache_root = parent.cache_root, |
| 386 | .debug_log_scopes = parent.debug_log_scopes, | 387 | .debug_log_scopes = parent.debug_log_scopes, |
| 387 | .debug_compile_errors = parent.debug_compile_errors, | 388 | .debug_compile_errors = parent.debug_compile_errors, |
| 389 | .debug_incremental = parent.debug_incremental, | ||
| 388 | .debug_pkg_config = parent.debug_pkg_config, | 390 | .debug_pkg_config = parent.debug_pkg_config, |
| 389 | .enable_darling = parent.enable_darling, | 391 | .enable_darling = parent.enable_darling, |
| 390 | .enable_qemu = parent.enable_qemu, | 392 | .enable_qemu = parent.enable_qemu, |
lib/std/Build/Step/Compile.zig+4| ... | @@ -1447,6 +1447,10 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { | ... | @@ -1447,6 +1447,10 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { |
| 1447 | try zig_args.append("--debug-compile-errors"); | 1447 | try zig_args.append("--debug-compile-errors"); |
| 1448 | } | 1448 | } |
| 1449 | 1449 | ||
| 1450 | if (b.debug_incremental) { | ||
| 1451 | try zig_args.append("--debug-incremental"); | ||
| 1452 | } | ||
| 1453 | |||
| 1450 | if (b.verbose_cimport) try zig_args.append("--verbose-cimport"); | 1454 | if (b.verbose_cimport) try zig_args.append("--verbose-cimport"); |
| 1451 | if (b.verbose_air) try zig_args.append("--verbose-air"); | 1455 | if (b.verbose_air) try zig_args.append("--verbose-air"); |
| 1452 | if (b.verbose_llvm_ir) |path| try zig_args.append(b.fmt("--verbose-llvm-ir={s}", .{path})); | 1456 | if (b.verbose_llvm_ir) |path| try zig_args.append(b.fmt("--verbose-llvm-ir={s}", .{path})); |
src/Compilation.zig+12| ... | @@ -190,6 +190,8 @@ time_report: bool, | ... | @@ -190,6 +190,8 @@ time_report: bool, |
| 190 | stack_report: bool, | 190 | stack_report: bool, |
| 191 | debug_compiler_runtime_libs: bool, | 191 | debug_compiler_runtime_libs: bool, |
| 192 | debug_compile_errors: bool, | 192 | debug_compile_errors: bool, |
| 193 | /// Do not check this field directly. Instead, use the `debugIncremental` wrapper function. | ||
| 194 | debug_incremental: bool, | ||
| 193 | incremental: bool, | 195 | incremental: bool, |
| 194 | alloc_failure_occurred: bool = false, | 196 | alloc_failure_occurred: bool = false, |
| 195 | last_update_was_cache_hit: bool = false, | 197 | last_update_was_cache_hit: bool = false, |
| ... | @@ -768,6 +770,14 @@ pub const Directories = struct { | ... | @@ -768,6 +770,14 @@ pub const Directories = struct { |
| 768 | } | 770 | } |
| 769 | }; | 771 | }; |
| 770 | 772 | ||
| 773 | /// This small wrapper function just checks whether debug extensions are enabled before checking | ||
| 774 | /// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller, | ||
| 775 | /// preventing debugging features from making it into release builds of the compiler. | ||
| 776 | pub inline fn debugIncremental(comp: *const Compilation) bool { | ||
| 777 | if (!build_options.enable_debug_extensions) return false; | ||
| 778 | return comp.debug_incremental; | ||
| 779 | } | ||
| 780 | |||
| 771 | pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; | 781 | pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; |
| 772 | pub const SemaError = Zcu.SemaError; | 782 | pub const SemaError = Zcu.SemaError; |
| 773 | 783 | ||
| ... | @@ -1598,6 +1608,7 @@ pub const CreateOptions = struct { | ... | @@ -1598,6 +1608,7 @@ pub const CreateOptions = struct { |
| 1598 | verbose_llvm_cpu_features: bool = false, | 1608 | verbose_llvm_cpu_features: bool = false, |
| 1599 | debug_compiler_runtime_libs: bool = false, | 1609 | debug_compiler_runtime_libs: bool = false, |
| 1600 | debug_compile_errors: bool = false, | 1610 | debug_compile_errors: bool = false, |
| 1611 | debug_incremental: bool = false, | ||
| 1601 | incremental: bool = false, | 1612 | incremental: bool = false, |
| 1602 | /// Normally when you create a `Compilation`, Zig will automatically build | 1613 | /// Normally when you create a `Compilation`, Zig will automatically build |
| 1603 | /// and link in required dependencies, such as compiler-rt and libc. When | 1614 | /// and link in required dependencies, such as compiler-rt and libc. When |
| ... | @@ -1968,6 +1979,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1968,6 +1979,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1968 | .test_name_prefix = options.test_name_prefix, | 1979 | .test_name_prefix = options.test_name_prefix, |
| 1969 | .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs, | 1980 | .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs, |
| 1970 | .debug_compile_errors = options.debug_compile_errors, | 1981 | .debug_compile_errors = options.debug_compile_errors, |
| 1982 | .debug_incremental = options.debug_incremental, | ||
| 1971 | .incremental = options.incremental, | 1983 | .incremental = options.incremental, |
| 1972 | .root_name = root_name, | 1984 | .root_name = root_name, |
| 1973 | .sysroot = sysroot, | 1985 | .sysroot = sysroot, |
src/IncrementalDebugServer.zig created+383| ... | @@ -0,0 +1,383 @@ | ||
| 1 | //! This is a simple TCP server which exposes a REPL useful for debugging incremental compilation | ||
| 2 | //! issues. Eventually, this logic should move into `std.zig.Client`/`std.zig.Server` or something | ||
| 3 | //! similar, but for now, this works. The server is enabled by the '--debug-incremental' CLI flag. | ||
| 4 | //! The easiest way to interact with the REPL is to use `telnet`: | ||
| 5 | //! ``` | ||
| 6 | //! telnet "::1" 7623 | ||
| 7 | //! ``` | ||
| 8 | //! 'help' will list available commands. When the debug server is enabled, the compiler tracks a lot | ||
| 9 | //! of extra state (see `Zcu.IncrementalDebugState`), so note that RSS will be higher than usual. | ||
| 10 | |||
| 11 | comptime { | ||
| 12 | // This file should only be referenced when debug extensions are enabled. | ||
| 13 | std.debug.assert(@import("build_options").enable_debug_extensions); | ||
| 14 | } | ||
| 15 | |||
| 16 | zcu: *Zcu, | ||
| 17 | thread: ?std.Thread, | ||
| 18 | running: std.atomic.Value(bool), | ||
| 19 | /// Held by our owner when an update is in-progress, and held by us when responding to a command. | ||
| 20 | /// So, essentially guards all access to `Compilation`, including `Zcu`. | ||
| 21 | mutex: std.Thread.Mutex, | ||
| 22 | |||
| 23 | pub fn init(zcu: *Zcu) IncrementalDebugServer { | ||
| 24 | return .{ | ||
| 25 | .zcu = zcu, | ||
| 26 | .thread = null, | ||
| 27 | .running = .init(true), | ||
| 28 | .mutex = .{}, | ||
| 29 | }; | ||
| 30 | } | ||
| 31 | |||
| 32 | pub fn deinit(ids: *IncrementalDebugServer) void { | ||
| 33 | if (ids.thread) |t| { | ||
| 34 | ids.running.store(false, .monotonic); | ||
| 35 | t.join(); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | const port = 7623; | ||
| 40 | pub fn spawn(ids: *IncrementalDebugServer) void { | ||
| 41 | std.debug.print("spawning incremental debug server on port {d}\n", .{port}); | ||
| 42 | ids.thread = std.Thread.spawn(.{ .allocator = ids.zcu.comp.arena }, runThread, .{ids}) catch |err| | ||
| 43 | std.process.fatal("failed to spawn incremental debug server: {s}", .{@errorName(err)}); | ||
| 44 | } | ||
| 45 | fn runThread(ids: *IncrementalDebugServer) void { | ||
| 46 | const gpa = ids.zcu.gpa; | ||
| 47 | |||
| 48 | var cmd_buf: [1024]u8 = undefined; | ||
| 49 | var text_out: std.ArrayListUnmanaged(u8) = .empty; | ||
| 50 | defer text_out.deinit(gpa); | ||
| 51 | |||
| 52 | const addr = std.net.Address.parseIp6("::", port) catch unreachable; | ||
| 53 | var server = addr.listen(.{}) catch @panic("IncrementalDebugServer: failed to listen"); | ||
| 54 | defer server.deinit(); | ||
| 55 | const conn = server.accept() catch @panic("IncrementalDebugServer: failed to accept"); | ||
| 56 | defer conn.stream.close(); | ||
| 57 | |||
| 58 | while (ids.running.load(.monotonic)) { | ||
| 59 | conn.stream.writeAll("zig> ") catch @panic("IncrementalDebugServer: failed to write"); | ||
| 60 | var fbs = std.io.fixedBufferStream(&cmd_buf); | ||
| 61 | conn.stream.reader().streamUntilDelimiter(fbs.writer(), '\n', cmd_buf.len) catch |err| switch (err) { | ||
| 62 | error.EndOfStream => break, | ||
| 63 | else => @panic("IncrementalDebugServer: failed to read command"), | ||
| 64 | }; | ||
| 65 | const cmd_and_arg = std.mem.trim(u8, fbs.getWritten(), " \t\r\n"); | ||
| 66 | const cmd: []const u8, const arg: []const u8 = if (std.mem.indexOfScalar(u8, cmd_and_arg, ' ')) |i| | ||
| 67 | .{ cmd_and_arg[0..i], cmd_and_arg[i + 1 ..] } | ||
| 68 | else | ||
| 69 | .{ cmd_and_arg, "" }; | ||
| 70 | |||
| 71 | text_out.clearRetainingCapacity(); | ||
| 72 | { | ||
| 73 | if (!ids.mutex.tryLock()) { | ||
| 74 | conn.stream.writeAll("waiting for in-progress update to finish...\n") catch @panic("IncrementalDebugServer: failed to write"); | ||
| 75 | ids.mutex.lock(); | ||
| 76 | } | ||
| 77 | defer ids.mutex.unlock(); | ||
| 78 | handleCommand(ids.zcu, &text_out, cmd, arg) catch @panic("IncrementalDebugServer: out of memory"); | ||
| 79 | } | ||
| 80 | text_out.append(gpa, '\n') catch @panic("IncrementalDebugServer: out of memory"); | ||
| 81 | conn.stream.writeAll(text_out.items) catch @panic("IncrementalDebugServer: failed to write"); | ||
| 82 | } | ||
| 83 | std.debug.print("closing incremental debug server\n", .{}); | ||
| 84 | } | ||
| 85 | |||
| 86 | const help_str: []const u8 = | ||
| 87 | \\[str] arguments are any string. | ||
| 88 | \\[id] arguments are a numeric ID/index, like an InternPool index. | ||
| 89 | \\[unit] arguments are strings like 'func 1234' where '1234' is the relevant index (in this case an InternPool index). | ||
| 90 | \\ | ||
| 91 | \\MISC | ||
| 92 | \\ summary | ||
| 93 | \\ Dump some information about the whole ZCU. | ||
| 94 | \\ nav_info [id] | ||
| 95 | \\ Dump basic info about a NAV. | ||
| 96 | \\ | ||
| 97 | \\SEARCHING | ||
| 98 | \\ find_type [str] | ||
| 99 | \\ Find types (including dead ones) whose names contain the given substring. | ||
| 100 | \\ Starting with '^' or ending with '$' anchors to the start/end of the name. | ||
| 101 | \\ find_nav [str] | ||
| 102 | \\ Find NAVs (including dead ones) whose names contain the given substring. | ||
| 103 | \\ Starting with '^' or ending with '$' anchors to the start/end of the name. | ||
| 104 | \\ | ||
| 105 | \\UNITS | ||
| 106 | \\ unit_info [unit] | ||
| 107 | \\ Dump basic info about an analysis unit. | ||
| 108 | \\ unit_dependencies [unit] | ||
| 109 | \\ List all units which an analysis unit depends on. | ||
| 110 | \\ unit_trace [unit] | ||
| 111 | \\ Dump the current reference trace of an analysis unit. | ||
| 112 | \\ | ||
| 113 | \\TYPES | ||
| 114 | \\ type_info [id] | ||
| 115 | \\ Dump basic info about a type. | ||
| 116 | \\ type_namespace [id] | ||
| 117 | \\ List all declarations in the namespace of a type. | ||
| 118 | \\ | ||
| 119 | ; | ||
| 120 | |||
| 121 | fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []const u8, arg_str: []const u8) Allocator.Error!void { | ||
| 122 | const ip = &zcu.intern_pool; | ||
| 123 | const gpa = zcu.gpa; | ||
| 124 | const w = output.writer(gpa); | ||
| 125 | if (std.mem.eql(u8, cmd_str, "help")) { | ||
| 126 | try w.writeAll(help_str); | ||
| 127 | } else if (std.mem.eql(u8, cmd_str, "summary")) { | ||
| 128 | try w.print( | ||
| 129 | \\last generation: {d} | ||
| 130 | \\total container types: {d} | ||
| 131 | \\total NAVs: {d} | ||
| 132 | \\total units: {d} | ||
| 133 | \\ | ||
| 134 | , .{ | ||
| 135 | zcu.generation - 1, | ||
| 136 | zcu.incremental_debug_state.types.count(), | ||
| 137 | zcu.incremental_debug_state.navs.count(), | ||
| 138 | zcu.incremental_debug_state.units.count(), | ||
| 139 | }); | ||
| 140 | } else if (std.mem.eql(u8, cmd_str, "nav_info")) { | ||
| 141 | const nav_index: InternPool.Nav.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed nav index")); | ||
| 142 | const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index"); | ||
| 143 | const nav = ip.getNav(nav_index); | ||
| 144 | try w.print( | ||
| 145 | \\name: '{}' | ||
| 146 | \\fqn: '{}' | ||
| 147 | \\status: {s} | ||
| 148 | \\created on generation: {d} | ||
| 149 | \\ | ||
| 150 | , .{ | ||
| 151 | nav.name.fmt(ip), | ||
| 152 | nav.fqn.fmt(ip), | ||
| 153 | @tagName(nav.status), | ||
| 154 | create_gen, | ||
| 155 | }); | ||
| 156 | switch (nav.status) { | ||
| 157 | .unresolved => {}, | ||
| 158 | .type_resolved, .fully_resolved => { | ||
| 159 | try w.writeAll("type: "); | ||
| 160 | try printType(.fromInterned(nav.typeOf(ip)), zcu, w); | ||
| 161 | try w.writeByte('\n'); | ||
| 162 | }, | ||
| 163 | } | ||
| 164 | } else if (std.mem.eql(u8, cmd_str, "find_type")) { | ||
| 165 | if (arg_str.len == 0) return w.writeAll("bad usage"); | ||
| 166 | const anchor_start = arg_str[0] == '^'; | ||
| 167 | const anchor_end = arg_str[arg_str.len - 1] == '$'; | ||
| 168 | const query = arg_str[@intFromBool(anchor_start) .. arg_str.len - @intFromBool(anchor_end)]; | ||
| 169 | var num_results: usize = 0; | ||
| 170 | for (zcu.incremental_debug_state.types.keys()) |type_ip_index| { | ||
| 171 | const ty: Type = .fromInterned(type_ip_index); | ||
| 172 | const ty_name = ty.containerTypeName(ip).toSlice(ip); | ||
| 173 | const success = switch (@as(u2, @intFromBool(anchor_start)) << 1 | @intFromBool(anchor_end)) { | ||
| 174 | 0b00 => std.mem.indexOf(u8, ty_name, query) != null, | ||
| 175 | 0b01 => std.mem.endsWith(u8, ty_name, query), | ||
| 176 | 0b10 => std.mem.startsWith(u8, ty_name, query), | ||
| 177 | 0b11 => std.mem.eql(u8, ty_name, query), | ||
| 178 | }; | ||
| 179 | if (success) { | ||
| 180 | num_results += 1; | ||
| 181 | try w.print("* type {d} ('{s}')\n", .{ @intFromEnum(type_ip_index), ty_name }); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | try w.print("Found {d} results\n", .{num_results}); | ||
| 185 | } else if (std.mem.eql(u8, cmd_str, "find_nav")) { | ||
| 186 | if (arg_str.len == 0) return w.writeAll("bad usage"); | ||
| 187 | const anchor_start = arg_str[0] == '^'; | ||
| 188 | const anchor_end = arg_str[arg_str.len - 1] == '$'; | ||
| 189 | const query = arg_str[@intFromBool(anchor_start) .. arg_str.len - @intFromBool(anchor_end)]; | ||
| 190 | var num_results: usize = 0; | ||
| 191 | for (zcu.incremental_debug_state.navs.keys()) |nav_index| { | ||
| 192 | const nav = ip.getNav(nav_index); | ||
| 193 | const nav_fqn = nav.fqn.toSlice(ip); | ||
| 194 | const success = switch (@as(u2, @intFromBool(anchor_start)) << 1 | @intFromBool(anchor_end)) { | ||
| 195 | 0b00 => std.mem.indexOf(u8, nav_fqn, query) != null, | ||
| 196 | 0b01 => std.mem.endsWith(u8, nav_fqn, query), | ||
| 197 | 0b10 => std.mem.startsWith(u8, nav_fqn, query), | ||
| 198 | 0b11 => std.mem.eql(u8, nav_fqn, query), | ||
| 199 | }; | ||
| 200 | if (success) { | ||
| 201 | num_results += 1; | ||
| 202 | try w.print("* nav {d} ('{s}')\n", .{ @intFromEnum(nav_index), nav_fqn }); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | try w.print("Found {d} results\n", .{num_results}); | ||
| 206 | } else if (std.mem.eql(u8, cmd_str, "unit_info")) { | ||
| 207 | const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); | ||
| 208 | const unit_info = zcu.incremental_debug_state.units.get(unit) orelse return w.writeAll("unknown anal unit"); | ||
| 209 | var ref_str_buf: [32]u8 = undefined; | ||
| 210 | const ref_str: []const u8 = ref: { | ||
| 211 | const refs = try zcu.resolveReferences(); | ||
| 212 | const ref = refs.get(unit) orelse break :ref "<unreferenced>"; | ||
| 213 | const referencer = (ref orelse break :ref "<analysis root>").referencer; | ||
| 214 | break :ref printAnalUnit(referencer, &ref_str_buf); | ||
| 215 | }; | ||
| 216 | const has_err: []const u8 = err: { | ||
| 217 | if (zcu.failed_analysis.contains(unit)) break :err "true"; | ||
| 218 | if (zcu.transitive_failed_analysis.contains(unit)) break :err "true (transitive)"; | ||
| 219 | break :err "false"; | ||
| 220 | }; | ||
| 221 | try w.print( | ||
| 222 | \\last update generation: {d} | ||
| 223 | \\current referencer: {s} | ||
| 224 | \\has error: {s} | ||
| 225 | \\ | ||
| 226 | , .{ | ||
| 227 | unit_info.last_update_gen, | ||
| 228 | ref_str, | ||
| 229 | has_err, | ||
| 230 | }); | ||
| 231 | } else if (std.mem.eql(u8, cmd_str, "unit_dependencies")) { | ||
| 232 | const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); | ||
| 233 | const unit_info = zcu.incremental_debug_state.units.get(unit) orelse return w.writeAll("unknown anal unit"); | ||
| 234 | for (unit_info.deps.items, 0..) |dependee, i| { | ||
| 235 | try w.print("[{d}] ", .{i}); | ||
| 236 | switch (dependee) { | ||
| 237 | .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{}", .{zcu.fmtDependee(dependee)}), | ||
| 238 | .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }), | ||
| 239 | .interned => |ip_index| switch (ip.indexToKey(ip_index)) { | ||
| 240 | .struct_type, .union_type, .enum_type => try w.print("type {d}", .{@intFromEnum(ip_index)}), | ||
| 241 | .func => try w.print("func {d}", .{@intFromEnum(ip_index)}), | ||
| 242 | else => unreachable, | ||
| 243 | }, | ||
| 244 | .memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}), | ||
| 245 | } | ||
| 246 | try w.writeByte('\n'); | ||
| 247 | } | ||
| 248 | } else if (std.mem.eql(u8, cmd_str, "unit_trace")) { | ||
| 249 | const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); | ||
| 250 | if (!zcu.incremental_debug_state.units.contains(unit)) return w.writeAll("unknown anal unit"); | ||
| 251 | const refs = try zcu.resolveReferences(); | ||
| 252 | if (!refs.contains(unit)) return w.writeAll("not referenced"); | ||
| 253 | var opt_cur: ?AnalUnit = unit; | ||
| 254 | while (opt_cur) |cur| { | ||
| 255 | var buf: [32]u8 = undefined; | ||
| 256 | try w.print("* {s}\n", .{printAnalUnit(cur, &buf)}); | ||
| 257 | opt_cur = if (refs.get(cur).?) |ref| ref.referencer else null; | ||
| 258 | } | ||
| 259 | } else if (std.mem.eql(u8, cmd_str, "type_info")) { | ||
| 260 | const ip_index: InternPool.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed ip index")); | ||
| 261 | const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type"); | ||
| 262 | try w.print( | ||
| 263 | \\name: '{}' | ||
| 264 | \\created on generation: {d} | ||
| 265 | \\ | ||
| 266 | , .{ | ||
| 267 | Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip), | ||
| 268 | create_gen, | ||
| 269 | }); | ||
| 270 | } else if (std.mem.eql(u8, cmd_str, "type_namespace")) { | ||
| 271 | const ip_index: InternPool.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed ip index")); | ||
| 272 | if (!zcu.incremental_debug_state.types.contains(ip_index)) return w.writeAll("unknown type"); | ||
| 273 | const ns = zcu.namespacePtr(Type.fromInterned(ip_index).getNamespaceIndex(zcu)); | ||
| 274 | try w.print("{d} pub decls:\n", .{ns.pub_decls.count()}); | ||
| 275 | for (ns.pub_decls.keys()) |nav| { | ||
| 276 | try w.print("* nav {d}\n", .{@intFromEnum(nav)}); | ||
| 277 | } | ||
| 278 | try w.print("{d} non-pub decls:\n", .{ns.priv_decls.count()}); | ||
| 279 | for (ns.priv_decls.keys()) |nav| { | ||
| 280 | try w.print("* nav {d}\n", .{@intFromEnum(nav)}); | ||
| 281 | } | ||
| 282 | try w.print("{d} comptime decls:\n", .{ns.comptime_decls.items.len}); | ||
| 283 | for (ns.comptime_decls.items) |id| { | ||
| 284 | try w.print("* comptime {d}\n", .{@intFromEnum(id)}); | ||
| 285 | } | ||
| 286 | try w.print("{d} tests:\n", .{ns.test_decls.items.len}); | ||
| 287 | for (ns.test_decls.items) |nav| { | ||
| 288 | try w.print("* nav {d}\n", .{@intFromEnum(nav)}); | ||
| 289 | } | ||
| 290 | } else { | ||
| 291 | try w.writeAll("command not found; run 'help' for a command list"); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | fn parseIndex(str: []const u8) ?u32 { | ||
| 296 | return std.fmt.parseInt(u32, str, 10) catch null; | ||
| 297 | } | ||
| 298 | fn parseAnalUnit(str: []const u8) ?AnalUnit { | ||
| 299 | const split_idx = std.mem.indexOfScalar(u8, str, ' ') orelse return null; | ||
| 300 | const kind = str[0..split_idx]; | ||
| 301 | const idx_str = str[split_idx + 1 ..]; | ||
| 302 | if (std.mem.eql(u8, kind, "comptime")) { | ||
| 303 | return .wrap(.{ .@"comptime" = @enumFromInt(parseIndex(idx_str) orelse return null) }); | ||
| 304 | } else if (std.mem.eql(u8, kind, "nav_val")) { | ||
| 305 | return .wrap(.{ .nav_val = @enumFromInt(parseIndex(idx_str) orelse return null) }); | ||
| 306 | } else if (std.mem.eql(u8, kind, "nav_ty")) { | ||
| 307 | return .wrap(.{ .nav_ty = @enumFromInt(parseIndex(idx_str) orelse return null) }); | ||
| 308 | } else if (std.mem.eql(u8, kind, "type")) { | ||
| 309 | return .wrap(.{ .type = @enumFromInt(parseIndex(idx_str) orelse return null) }); | ||
| 310 | } else if (std.mem.eql(u8, kind, "func")) { | ||
| 311 | return .wrap(.{ .func = @enumFromInt(parseIndex(idx_str) orelse return null) }); | ||
| 312 | } else if (std.mem.eql(u8, kind, "memoized_state")) { | ||
| 313 | return .wrap(.{ .memoized_state = std.meta.stringToEnum( | ||
| 314 | InternPool.MemoizedStateStage, | ||
| 315 | idx_str, | ||
| 316 | ) orelse return null }); | ||
| 317 | } else { | ||
| 318 | return null; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | fn printAnalUnit(unit: AnalUnit, buf: *[32]u8) []const u8 { | ||
| 322 | const idx: u32 = switch (unit.unwrap()) { | ||
| 323 | .memoized_state => |stage| return std.fmt.bufPrint(buf, "memoized_state {s}", .{@tagName(stage)}) catch unreachable, | ||
| 324 | inline else => |i| @intFromEnum(i), | ||
| 325 | }; | ||
| 326 | return std.fmt.bufPrint(buf, "{s} {d}", .{ @tagName(unit.unwrap()), idx }) catch unreachable; | ||
| 327 | } | ||
| 328 | fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void { | ||
| 329 | const ip = &zcu.intern_pool; | ||
| 330 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 331 | .int_type => |int| try w.print("{c}{d}", .{ | ||
| 332 | @as(u8, if (int.signedness == .unsigned) 'u' else 'i'), | ||
| 333 | int.bits, | ||
| 334 | }), | ||
| 335 | .tuple_type => try w.writeAll("(tuple)"), | ||
| 336 | .error_set_type => try w.writeAll("(error set)"), | ||
| 337 | .inferred_error_set_type => try w.writeAll("(inferred error set)"), | ||
| 338 | .func_type => try w.writeAll("(function)"), | ||
| 339 | .anyframe_type => try w.writeAll("(anyframe)"), | ||
| 340 | .vector_type => { | ||
| 341 | try w.print("@Vector({d}, ", .{ty.vectorLen(zcu)}); | ||
| 342 | try printType(ty.childType(zcu), zcu, w); | ||
| 343 | try w.writeByte(')'); | ||
| 344 | }, | ||
| 345 | .array_type => { | ||
| 346 | try w.print("[{d}]", .{ty.arrayLen(zcu)}); | ||
| 347 | try printType(ty.childType(zcu), zcu, w); | ||
| 348 | }, | ||
| 349 | .opt_type => { | ||
| 350 | try w.writeByte('?'); | ||
| 351 | try printType(ty.optionalChild(zcu), zcu, w); | ||
| 352 | }, | ||
| 353 | .error_union_type => { | ||
| 354 | try printType(ty.errorUnionSet(zcu), zcu, w); | ||
| 355 | try w.writeByte('!'); | ||
| 356 | try printType(ty.errorUnionPayload(zcu), zcu, w); | ||
| 357 | }, | ||
| 358 | .ptr_type => { | ||
| 359 | try w.writeAll("*(attrs) "); | ||
| 360 | try printType(ty.childType(zcu), zcu, w); | ||
| 361 | }, | ||
| 362 | .simple_type => |simple| try w.writeAll(@tagName(simple)), | ||
| 363 | |||
| 364 | .struct_type, | ||
| 365 | .union_type, | ||
| 366 | .enum_type, | ||
| 367 | .opaque_type, | ||
| 368 | => try w.print("{}[{d}]", .{ ty.containerTypeName(ip).fmt(ip), @intFromEnum(ty.toIntern()) }), | ||
| 369 | |||
| 370 | else => unreachable, | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | const std = @import("std"); | ||
| 375 | const Allocator = std.mem.Allocator; | ||
| 376 | |||
| 377 | const Compilation = @import("Compilation.zig"); | ||
| 378 | const Zcu = @import("Zcu.zig"); | ||
| 379 | const InternPool = @import("InternPool.zig"); | ||
| 380 | const Type = @import("Type.zig"); | ||
| 381 | const AnalUnit = InternPool.AnalUnit; | ||
| 382 | |||
| 383 | const IncrementalDebugServer = @This(); | ||
src/Sema.zig+24-13| ... | @@ -2998,11 +2998,7 @@ fn zirStructDecl( | ... | @@ -2998,11 +2998,7 @@ fn zirStructDecl( |
| 2998 | errdefer pt.destroyNamespace(new_namespace_index); | 2998 | errdefer pt.destroyNamespace(new_namespace_index); |
| 2999 | 2999 | ||
| 3000 | if (pt.zcu.comp.incremental) { | 3000 | if (pt.zcu.comp.incremental) { |
| 3001 | try ip.addDependency( | 3001 | try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); |
| 3002 | sema.gpa, | ||
| 3003 | AnalUnit.wrap(.{ .type = wip_ty.index }), | ||
| 3004 | .{ .src_hash = tracked_inst }, | ||
| 3005 | ); | ||
| 3006 | } | 3002 | } |
| 3007 | 3003 | ||
| 3008 | const decls = sema.code.bodySlice(extra_index, decls_len); | 3004 | const decls = sema.code.bodySlice(extra_index, decls_len); |
| ... | @@ -3017,6 +3013,7 @@ fn zirStructDecl( | ... | @@ -3017,6 +3013,7 @@ fn zirStructDecl( |
| 3017 | } | 3013 | } |
| 3018 | try sema.declareDependency(.{ .interned = wip_ty.index }); | 3014 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 3019 | try sema.addTypeReferenceEntry(src, wip_ty.index); | 3015 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| 3016 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 3020 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); | 3017 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); |
| 3021 | } | 3018 | } |
| 3022 | 3019 | ||
| ... | @@ -3247,6 +3244,7 @@ fn zirEnumDecl( | ... | @@ -3247,6 +3244,7 @@ fn zirEnumDecl( |
| 3247 | 3244 | ||
| 3248 | // We've finished the initial construction of this type, and are about to perform analysis. | 3245 | // We've finished the initial construction of this type, and are about to perform analysis. |
| 3249 | // Set the namespace appropriately, and don't destroy anything on failure. | 3246 | // Set the namespace appropriately, and don't destroy anything on failure. |
| 3247 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 3250 | wip_ty.prepare(ip, new_namespace_index); | 3248 | wip_ty.prepare(ip, new_namespace_index); |
| 3251 | done = true; | 3249 | done = true; |
| 3252 | 3250 | ||
| ... | @@ -3377,11 +3375,7 @@ fn zirUnionDecl( | ... | @@ -3377,11 +3375,7 @@ fn zirUnionDecl( |
| 3377 | errdefer pt.destroyNamespace(new_namespace_index); | 3375 | errdefer pt.destroyNamespace(new_namespace_index); |
| 3378 | 3376 | ||
| 3379 | if (pt.zcu.comp.incremental) { | 3377 | if (pt.zcu.comp.incremental) { |
| 3380 | try zcu.intern_pool.addDependency( | 3378 | try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); |
| 3381 | gpa, | ||
| 3382 | AnalUnit.wrap(.{ .type = wip_ty.index }), | ||
| 3383 | .{ .src_hash = tracked_inst }, | ||
| 3384 | ); | ||
| 3385 | } | 3379 | } |
| 3386 | 3380 | ||
| 3387 | const decls = sema.code.bodySlice(extra_index, decls_len); | 3381 | const decls = sema.code.bodySlice(extra_index, decls_len); |
| ... | @@ -3396,6 +3390,7 @@ fn zirUnionDecl( | ... | @@ -3396,6 +3390,7 @@ fn zirUnionDecl( |
| 3396 | } | 3390 | } |
| 3397 | try sema.declareDependency(.{ .interned = wip_ty.index }); | 3391 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 3398 | try sema.addTypeReferenceEntry(src, wip_ty.index); | 3392 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| 3393 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 3399 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); | 3394 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); |
| 3400 | } | 3395 | } |
| 3401 | 3396 | ||
| ... | @@ -3481,6 +3476,7 @@ fn zirOpaqueDecl( | ... | @@ -3481,6 +3476,7 @@ fn zirOpaqueDecl( |
| 3481 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); | 3476 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 3482 | } | 3477 | } |
| 3483 | try sema.addTypeReferenceEntry(src, wip_ty.index); | 3478 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| 3479 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 3484 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); | 3480 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); |
| 3485 | } | 3481 | } |
| 3486 | 3482 | ||
| ... | @@ -8026,6 +8022,11 @@ fn analyzeCall( | ... | @@ -8026,6 +8022,11 @@ fn analyzeCall( |
| 8026 | .generic_owner = func_val.?.toIntern(), | 8022 | .generic_owner = func_val.?.toIntern(), |
| 8027 | .comptime_args = comptime_args, | 8023 | .comptime_args = comptime_args, |
| 8028 | }); | 8024 | }); |
| 8025 | if (zcu.comp.debugIncremental()) { | ||
| 8026 | const nav = ip.indexToKey(func_instance).func.owner_nav; | ||
| 8027 | const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav); | ||
| 8028 | if (!gop.found_existing) gop.value_ptr.* = zcu.generation; | ||
| 8029 | } | ||
| 8029 | 8030 | ||
| 8030 | // This call is problematic as it breaks guarantees about order-independency of semantic analysis. | 8031 | // This call is problematic as it breaks guarantees about order-independency of semantic analysis. |
| 8031 | // These guarantees are necessary for incremental compilation and parallel semantic analysis. | 8032 | // These guarantees are necessary for incremental compilation and parallel semantic analysis. |
| ... | @@ -20345,6 +20346,7 @@ fn structInitAnon( | ... | @@ -20345,6 +20346,7 @@ fn structInitAnon( |
| 20345 | if (block.ownerModule().strip) break :codegen_type; | 20346 | if (block.ownerModule().strip) break :codegen_type; |
| 20346 | try zcu.comp.queueJob(.{ .codegen_type = wip.index }); | 20347 | try zcu.comp.queueJob(.{ .codegen_type = wip.index }); |
| 20347 | } | 20348 | } |
| 20349 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); | ||
| 20348 | break :ty wip.finish(ip, new_namespace_index); | 20350 | break :ty wip.finish(ip, new_namespace_index); |
| 20349 | }, | 20351 | }, |
| 20350 | .existing => |ty| ty, | 20352 | .existing => |ty| ty, |
| ... | @@ -21406,6 +21408,7 @@ fn zirReify( | ... | @@ -21406,6 +21408,7 @@ fn zirReify( |
| 21406 | }); | 21408 | }); |
| 21407 | 21409 | ||
| 21408 | try sema.addTypeReferenceEntry(src, wip_ty.index); | 21410 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| 21411 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 21409 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); | 21412 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); |
| 21410 | }, | 21413 | }, |
| 21411 | .@"union" => { | 21414 | .@"union" => { |
| ... | @@ -21611,6 +21614,7 @@ fn reifyEnum( | ... | @@ -21611,6 +21614,7 @@ fn reifyEnum( |
| 21611 | 21614 | ||
| 21612 | try sema.declareDependency(.{ .interned = wip_ty.index }); | 21615 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 21613 | try sema.addTypeReferenceEntry(src, wip_ty.index); | 21616 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| 21617 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 21614 | wip_ty.prepare(ip, new_namespace_index); | 21618 | wip_ty.prepare(ip, new_namespace_index); |
| 21615 | wip_ty.setTagTy(ip, tag_ty.toIntern()); | 21619 | wip_ty.setTagTy(ip, tag_ty.toIntern()); |
| 21616 | done = true; | 21620 | done = true; |
| ... | @@ -21920,6 +21924,7 @@ fn reifyUnion( | ... | @@ -21920,6 +21924,7 @@ fn reifyUnion( |
| 21920 | } | 21924 | } |
| 21921 | try sema.declareDependency(.{ .interned = wip_ty.index }); | 21925 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 21922 | try sema.addTypeReferenceEntry(src, wip_ty.index); | 21926 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| 21927 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 21923 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); | 21928 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); |
| 21924 | } | 21929 | } |
| 21925 | 21930 | ||
| ... | @@ -22273,6 +22278,7 @@ fn reifyStruct( | ... | @@ -22273,6 +22278,7 @@ fn reifyStruct( |
| 22273 | } | 22278 | } |
| 22274 | try sema.declareDependency(.{ .interned = wip_ty.index }); | 22279 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 22275 | try sema.addTypeReferenceEntry(src, wip_ty.index); | 22280 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| 22281 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 22276 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); | 22282 | return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); |
| 22277 | } | 22283 | } |
| 22278 | 22284 | ||
| ... | @@ -37485,8 +37491,8 @@ fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool | ... | @@ -37485,8 +37491,8 @@ fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool |
| 37485 | } | 37491 | } |
| 37486 | 37492 | ||
| 37487 | pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { | 37493 | pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { |
| 37488 | const zcu = sema.pt.zcu; | 37494 | const pt = sema.pt; |
| 37489 | if (!zcu.comp.incremental) return; | 37495 | if (!pt.zcu.comp.incremental) return; |
| 37490 | 37496 | ||
| 37491 | const gop = try sema.dependencies.getOrPut(sema.gpa, dependee); | 37497 | const gop = try sema.dependencies.getOrPut(sema.gpa, dependee); |
| 37492 | if (gop.found_existing) return; | 37498 | if (gop.found_existing) return; |
| ... | @@ -37508,7 +37514,7 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { | ... | @@ -37508,7 +37514,7 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { |
| 37508 | else => {}, | 37514 | else => {}, |
| 37509 | } | 37515 | } |
| 37510 | 37516 | ||
| 37511 | try zcu.intern_pool.addDependency(sema.gpa, sema.owner, dependee); | 37517 | try pt.addDependency(sema.owner, dependee); |
| 37512 | } | 37518 | } |
| 37513 | 37519 | ||
| 37514 | fn isComptimeMutablePtr(sema: *Sema, val: Value) bool { | 37520 | fn isComptimeMutablePtr(sema: *Sema, val: Value) bool { |
| ... | @@ -37905,6 +37911,11 @@ pub fn resolveDeclaredEnum( | ... | @@ -37905,6 +37911,11 @@ pub fn resolveDeclaredEnum( |
| 37905 | }; | 37911 | }; |
| 37906 | defer sema.deinit(); | 37912 | defer sema.deinit(); |
| 37907 | 37913 | ||
| 37914 | if (zcu.comp.debugIncremental()) { | ||
| 37915 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, sema.owner); | ||
| 37916 | info.last_update_gen = zcu.generation; | ||
| 37917 | } | ||
| 37918 | |||
| 37908 | try sema.declareDependency(.{ .src_hash = tracked_inst }); | 37919 | try sema.declareDependency(.{ .src_hash = tracked_inst }); |
| 37909 | 37920 | ||
| 37910 | var block: Block = .{ | 37921 | var block: Block = .{ |
src/Type.zig+10| ... | @@ -3797,6 +3797,11 @@ fn resolveStructInner( | ... | @@ -3797,6 +3797,11 @@ fn resolveStructInner( |
| 3797 | return error.AnalysisFail; | 3797 | return error.AnalysisFail; |
| 3798 | } | 3798 | } |
| 3799 | 3799 | ||
| 3800 | if (zcu.comp.debugIncremental()) { | ||
| 3801 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner); | ||
| 3802 | info.last_update_gen = zcu.generation; | ||
| 3803 | } | ||
| 3804 | |||
| 3800 | var analysis_arena = std.heap.ArenaAllocator.init(gpa); | 3805 | var analysis_arena = std.heap.ArenaAllocator.init(gpa); |
| 3801 | defer analysis_arena.deinit(); | 3806 | defer analysis_arena.deinit(); |
| 3802 | 3807 | ||
| ... | @@ -3851,6 +3856,11 @@ fn resolveUnionInner( | ... | @@ -3851,6 +3856,11 @@ fn resolveUnionInner( |
| 3851 | return error.AnalysisFail; | 3856 | return error.AnalysisFail; |
| 3852 | } | 3857 | } |
| 3853 | 3858 | ||
| 3859 | if (zcu.comp.debugIncremental()) { | ||
| 3860 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner); | ||
| 3861 | info.last_update_gen = zcu.generation; | ||
| 3862 | } | ||
| 3863 | |||
| 3854 | var analysis_arena = std.heap.ArenaAllocator.init(gpa); | 3864 | var analysis_arena = std.heap.ArenaAllocator.init(gpa); |
| 3855 | defer analysis_arena.deinit(); | 3865 | defer analysis_arena.deinit(); |
| 3856 | 3866 |
src/Zcu.zig+52| ... | @@ -308,8 +308,56 @@ free_type_references: std.ArrayListUnmanaged(u32) = .empty, | ... | @@ -308,8 +308,56 @@ free_type_references: std.ArrayListUnmanaged(u32) = .empty, |
| 308 | /// Populated by analysis of `AnalUnit.wrap(.{ .memoized_state = s })`, where `s` depends on the element. | 308 | /// Populated by analysis of `AnalUnit.wrap(.{ .memoized_state = s })`, where `s` depends on the element. |
| 309 | builtin_decl_values: BuiltinDecl.Memoized = .initFill(.none), | 309 | builtin_decl_values: BuiltinDecl.Memoized = .initFill(.none), |
| 310 | 310 | ||
| 311 | incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void = | ||
| 312 | if (build_options.enable_debug_extensions) .init else {}, | ||
| 313 | |||
| 311 | generation: u32 = 0, | 314 | generation: u32 = 0, |
| 312 | 315 | ||
| 316 | pub const IncrementalDebugState = struct { | ||
| 317 | /// All container types in the ZCU, even dead ones. | ||
| 318 | /// Value is the generation the type was created on. | ||
| 319 | types: std.AutoArrayHashMapUnmanaged(InternPool.Index, u32), | ||
| 320 | /// All `Nav`s in the ZCU, even dead ones. | ||
| 321 | /// Value is the generation the `Nav` was created on. | ||
| 322 | navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, u32), | ||
| 323 | /// All `AnalUnit`s in the ZCU, even dead ones. | ||
| 324 | units: std.AutoArrayHashMapUnmanaged(AnalUnit, UnitInfo), | ||
| 325 | |||
| 326 | pub const init: IncrementalDebugState = .{ | ||
| 327 | .types = .empty, | ||
| 328 | .navs = .empty, | ||
| 329 | .units = .empty, | ||
| 330 | }; | ||
| 331 | pub fn deinit(ids: *IncrementalDebugState, gpa: Allocator) void { | ||
| 332 | for (ids.units.values()) |*unit_info| { | ||
| 333 | unit_info.deps.deinit(gpa); | ||
| 334 | } | ||
| 335 | ids.types.deinit(gpa); | ||
| 336 | ids.navs.deinit(gpa); | ||
| 337 | ids.units.deinit(gpa); | ||
| 338 | } | ||
| 339 | |||
| 340 | pub const UnitInfo = struct { | ||
| 341 | last_update_gen: u32, | ||
| 342 | /// This information isn't easily recoverable from `InternPool`'s dependency storage format. | ||
| 343 | deps: std.ArrayListUnmanaged(InternPool.Dependee), | ||
| 344 | }; | ||
| 345 | pub fn getUnitInfo(ids: *IncrementalDebugState, gpa: Allocator, unit: AnalUnit) Allocator.Error!*UnitInfo { | ||
| 346 | const gop = try ids.units.getOrPut(gpa, unit); | ||
| 347 | if (!gop.found_existing) gop.value_ptr.* = .{ | ||
| 348 | .last_update_gen = std.math.maxInt(u32), | ||
| 349 | .deps = .empty, | ||
| 350 | }; | ||
| 351 | return gop.value_ptr; | ||
| 352 | } | ||
| 353 | pub fn newType(ids: *IncrementalDebugState, zcu: *Zcu, ty: InternPool.Index) Allocator.Error!void { | ||
| 354 | try ids.types.putNoClobber(zcu.gpa, ty, zcu.generation); | ||
| 355 | } | ||
| 356 | pub fn newNav(ids: *IncrementalDebugState, zcu: *Zcu, nav: InternPool.Nav.Index) Allocator.Error!void { | ||
| 357 | try ids.navs.putNoClobber(zcu.gpa, nav, zcu.generation); | ||
| 358 | } | ||
| 359 | }; | ||
| 360 | |||
| 313 | pub const PerThread = @import("Zcu/PerThread.zig"); | 361 | pub const PerThread = @import("Zcu/PerThread.zig"); |
| 314 | 362 | ||
| 315 | pub const ImportTableAdapter = struct { | 363 | pub const ImportTableAdapter = struct { |
| ... | @@ -2746,6 +2794,10 @@ pub fn deinit(zcu: *Zcu) void { | ... | @@ -2746,6 +2794,10 @@ pub fn deinit(zcu: *Zcu) void { |
| 2746 | zcu.free_type_references.deinit(gpa); | 2794 | zcu.free_type_references.deinit(gpa); |
| 2747 | 2795 | ||
| 2748 | if (zcu.resolved_references) |*r| r.deinit(gpa); | 2796 | if (zcu.resolved_references) |*r| r.deinit(gpa); |
| 2797 | |||
| 2798 | if (zcu.comp.debugIncremental()) { | ||
| 2799 | zcu.incremental_debug_state.deinit(gpa); | ||
| 2800 | } | ||
| 2749 | } | 2801 | } |
| 2750 | zcu.intern_pool.deinit(gpa); | 2802 | zcu.intern_pool.deinit(gpa); |
| 2751 | } | 2803 | } |
src/Zcu/PerThread.zig+104-47| ... | @@ -635,6 +635,12 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized | ... | @@ -635,6 +635,12 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized |
| 635 | if (zcu.builtin_decl_values.get(to_check) != .none) return; | 635 | if (zcu.builtin_decl_values.get(to_check) != .none) return; |
| 636 | } | 636 | } |
| 637 | 637 | ||
| 638 | if (zcu.comp.debugIncremental()) { | ||
| 639 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, unit); | ||
| 640 | info.last_update_gen = zcu.generation; | ||
| 641 | info.deps.clearRetainingCapacity(); | ||
| 642 | } | ||
| 643 | |||
| 638 | const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage)) |any_changed| | 644 | const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage)) |any_changed| |
| 639 | .{ any_changed or prev_failed, false } | 645 | .{ any_changed or prev_failed, false } |
| 640 | else |err| switch (err) { | 646 | else |err| switch (err) { |
| ... | @@ -784,6 +790,12 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU | ... | @@ -784,6 +790,12 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU |
| 784 | return; | 790 | return; |
| 785 | } | 791 | } |
| 786 | 792 | ||
| 793 | if (zcu.comp.debugIncremental()) { | ||
| 794 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); | ||
| 795 | info.last_update_gen = zcu.generation; | ||
| 796 | info.deps.clearRetainingCapacity(); | ||
| 797 | } | ||
| 798 | |||
| 787 | const unit_prog_node = zcu.sema_prog_node.start("comptime", 0); | 799 | const unit_prog_node = zcu.sema_prog_node.start("comptime", 0); |
| 788 | defer unit_prog_node.end(); | 800 | defer unit_prog_node.end(); |
| 789 | 801 | ||
| ... | @@ -958,6 +970,12 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu | ... | @@ -958,6 +970,12 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu |
| 958 | } | 970 | } |
| 959 | } | 971 | } |
| 960 | 972 | ||
| 973 | if (zcu.comp.debugIncremental()) { | ||
| 974 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); | ||
| 975 | info.last_update_gen = zcu.generation; | ||
| 976 | info.deps.clearRetainingCapacity(); | ||
| 977 | } | ||
| 978 | |||
| 961 | const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0); | 979 | const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0); |
| 962 | defer unit_prog_node.end(); | 980 | defer unit_prog_node.end(); |
| 963 | 981 | ||
| ... | @@ -1004,6 +1022,35 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu | ... | @@ -1004,6 +1022,35 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu |
| 1004 | } | 1022 | } |
| 1005 | } | 1023 | } |
| 1006 | 1024 | ||
| 1025 | // If there isn't a type annotation, then we have also just resolved the type. That means the | ||
| 1026 | // the type is up-to-date, so it won't have the chance to mark its own dependency on the value; | ||
| 1027 | // we must do that ourselves. | ||
| 1028 | type_deps_on_val: { | ||
| 1029 | const inst_resolved = nav.analysis.?.zir_index.resolveFull(ip) orelse break :type_deps_on_val; | ||
| 1030 | const file = zcu.fileByIndex(inst_resolved.file); | ||
| 1031 | const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst); | ||
| 1032 | if (zir_decl.type_body != null) break :type_deps_on_val; | ||
| 1033 | // The type does indeed depend on the value. We are responsible for populating all state of | ||
| 1034 | // the `nav_ty`, including exports, references, errors, and dependencies. | ||
| 1035 | const ty_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id }); | ||
| 1036 | const ty_was_outdated = zcu.outdated.swapRemove(ty_unit) or | ||
| 1037 | zcu.potentially_outdated.swapRemove(ty_unit); | ||
| 1038 | if (ty_was_outdated) { | ||
| 1039 | _ = zcu.outdated_ready.swapRemove(ty_unit); | ||
| 1040 | zcu.deleteUnitExports(ty_unit); | ||
| 1041 | zcu.deleteUnitReferences(ty_unit); | ||
| 1042 | zcu.deleteUnitCompileLogs(ty_unit); | ||
| 1043 | if (zcu.failed_analysis.fetchSwapRemove(ty_unit)) |kv| { | ||
| 1044 | kv.value.destroy(gpa); | ||
| 1045 | } | ||
| 1046 | _ = zcu.transitive_failed_analysis.swapRemove(ty_unit); | ||
| 1047 | ip.removeDependenciesForDepender(gpa, ty_unit); | ||
| 1048 | } | ||
| 1049 | try pt.addDependency(ty_unit, .{ .nav_val = nav_id }); | ||
| 1050 | if (new_failed) try zcu.transitive_failed_analysis.put(gpa, ty_unit, {}); | ||
| 1051 | if (ty_was_outdated) try zcu.markDependeeOutdated(.marked_po, .{ .nav_ty = nav_id }); | ||
| 1052 | } | ||
| 1053 | |||
| 1007 | if (new_failed) return error.AnalysisFail; | 1054 | if (new_failed) return error.AnalysisFail; |
| 1008 | } | 1055 | } |
| 1009 | 1056 | ||
| ... | @@ -1248,14 +1295,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr | ... | @@ -1248,14 +1295,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr |
| 1248 | // Mark the unit as completed before evaluating the export! | 1295 | // Mark the unit as completed before evaluating the export! |
| 1249 | assert(zcu.analysis_in_progress.swapRemove(anal_unit)); | 1296 | assert(zcu.analysis_in_progress.swapRemove(anal_unit)); |
| 1250 | 1297 | ||
| 1251 | if (zir_decl.type_body == null) { | ||
| 1252 | // In this situation, it's possible that we were triggered by `analyzeNavType` up the stack. In that | ||
| 1253 | // case, we must also signal that the *type* is now populated to make this export behave correctly. | ||
| 1254 | // An alternative strategy would be to just put something on the job queue to perform the export, but | ||
| 1255 | // this is a little more straightforward, if perhaps less elegant. | ||
| 1256 | _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id })); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | if (zir_decl.linkage == .@"export") { | 1298 | if (zir_decl.linkage == .@"export") { |
| 1260 | const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) }); | 1299 | const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) }); |
| 1261 | const name_slice = zir.nullTerminatedString(zir_decl.name); | 1300 | const name_slice = zir.nullTerminatedString(zir_decl.name); |
| ... | @@ -1296,6 +1335,18 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc | ... | @@ -1296,6 +1335,18 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc |
| 1296 | 1335 | ||
| 1297 | log.debug("ensureNavTypeUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)}); | 1336 | log.debug("ensureNavTypeUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)}); |
| 1298 | 1337 | ||
| 1338 | const type_resolved_by_value: bool = from_val: { | ||
| 1339 | const analysis = nav.analysis orelse break :from_val false; | ||
| 1340 | const inst_resolved = analysis.zir_index.resolveFull(ip) orelse break :from_val false; | ||
| 1341 | const file = zcu.fileByIndex(inst_resolved.file); | ||
| 1342 | const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst); | ||
| 1343 | break :from_val zir_decl.type_body == null; | ||
| 1344 | }; | ||
| 1345 | if (type_resolved_by_value) { | ||
| 1346 | // Logic at the end of `ensureNavValUpToDate` is directly responsible for populating our state. | ||
| 1347 | return pt.ensureNavValUpToDate(nav_id); | ||
| 1348 | } | ||
| 1349 | |||
| 1299 | // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the | 1350 | // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the |
| 1300 | // status is `.unresolved`, which indicates that the value is outdated because it has *never* | 1351 | // status is `.unresolved`, which indicates that the value is outdated because it has *never* |
| 1301 | // been analyzed so far. | 1352 | // been analyzed so far. |
| ... | @@ -1331,6 +1382,12 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc | ... | @@ -1331,6 +1382,12 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc |
| 1331 | } | 1382 | } |
| 1332 | } | 1383 | } |
| 1333 | 1384 | ||
| 1385 | if (zcu.comp.debugIncremental()) { | ||
| 1386 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); | ||
| 1387 | info.last_update_gen = zcu.generation; | ||
| 1388 | info.deps.clearRetainingCapacity(); | ||
| 1389 | } | ||
| 1390 | |||
| 1334 | const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0); | 1391 | const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0); |
| 1335 | defer unit_prog_node.end(); | 1392 | defer unit_prog_node.end(); |
| 1336 | 1393 | ||
| ... | @@ -1397,6 +1454,10 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr | ... | @@ -1397,6 +1454,10 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr |
| 1397 | try zcu.analysis_in_progress.put(gpa, anal_unit, {}); | 1454 | try zcu.analysis_in_progress.put(gpa, anal_unit, {}); |
| 1398 | defer _ = zcu.analysis_in_progress.swapRemove(anal_unit); | 1455 | defer _ = zcu.analysis_in_progress.swapRemove(anal_unit); |
| 1399 | 1456 | ||
| 1457 | const zir_decl = zir.getDeclaration(inst_resolved.inst); | ||
| 1458 | assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace")); | ||
| 1459 | const type_body = zir_decl.type_body.?; | ||
| 1460 | |||
| 1400 | var analysis_arena: std.heap.ArenaAllocator = .init(gpa); | 1461 | var analysis_arena: std.heap.ArenaAllocator = .init(gpa); |
| 1401 | defer analysis_arena.deinit(); | 1462 | defer analysis_arena.deinit(); |
| 1402 | 1463 | ||
| ... | @@ -1436,9 +1497,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr | ... | @@ -1436,9 +1497,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr |
| 1436 | }; | 1497 | }; |
| 1437 | defer block.instructions.deinit(gpa); | 1498 | defer block.instructions.deinit(gpa); |
| 1438 | 1499 | ||
| 1439 | const zir_decl = zir.getDeclaration(inst_resolved.inst); | ||
| 1440 | assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace")); | ||
| 1441 | |||
| 1442 | const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); | 1500 | const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); |
| 1443 | 1501 | ||
| 1444 | block.comptime_reason = .{ .reason = .{ | 1502 | block.comptime_reason = .{ .reason = .{ |
| ... | @@ -1446,23 +1504,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr | ... | @@ -1446,23 +1504,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr |
| 1446 | .r = .{ .simple = .type }, | 1504 | .r = .{ .simple = .type }, |
| 1447 | } }; | 1505 | } }; |
| 1448 | 1506 | ||
| 1449 | const type_body = zir_decl.type_body orelse { | ||
| 1450 | // The type of this `Nav` is inferred from the value. | ||
| 1451 | // In other words, this `nav_ty` depends on the corresponding `nav_val`. | ||
| 1452 | try sema.declareDependency(.{ .nav_val = nav_id }); | ||
| 1453 | try pt.ensureNavValUpToDate(nav_id); | ||
| 1454 | // Note that the above call, if it did any work, has removed our `analysis_in_progress` entry for us. | ||
| 1455 | // (Our `defer` will run anyway, but it does nothing in this case.) | ||
| 1456 | |||
| 1457 | // There's not a great way for us to know whether the type actually changed. | ||
| 1458 | // For instance, perhaps the `nav_val` was already up-to-date, but this `nav_ty` is being | ||
| 1459 | // analyzed because this declaration had a type annotation on the *previous* update. | ||
| 1460 | // However, such cases are rare, and it's not unreasonable to re-analyze in them; and in | ||
| 1461 | // other cases where we get here, it's because the `nav_val` was already re-analyzed and | ||
| 1462 | // is outdated. | ||
| 1463 | return .{ .type_changed = true }; | ||
| 1464 | }; | ||
| 1465 | |||
| 1466 | const resolved_ty: Type = ty: { | 1507 | const resolved_ty: Type = ty: { |
| 1467 | const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst); | 1508 | const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst); |
| 1468 | const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src); | 1509 | const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src); |
| ... | @@ -1564,6 +1605,12 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter | ... | @@ -1564,6 +1605,12 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 1564 | if (func.analysisUnordered(ip).is_analyzed) return; | 1605 | if (func.analysisUnordered(ip).is_analyzed) return; |
| 1565 | } | 1606 | } |
| 1566 | 1607 | ||
| 1608 | if (zcu.comp.debugIncremental()) { | ||
| 1609 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); | ||
| 1610 | info.last_update_gen = zcu.generation; | ||
| 1611 | info.deps.clearRetainingCapacity(); | ||
| 1612 | } | ||
| 1613 | |||
| 1567 | const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0); | 1614 | const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0); |
| 1568 | defer func_prog_node.end(); | 1615 | defer func_prog_node.end(); |
| 1569 | 1616 | ||
| ... | @@ -1816,11 +1863,7 @@ fn createFileRootStruct( | ... | @@ -1816,11 +1863,7 @@ fn createFileRootStruct( |
| 1816 | ip.namespacePtr(namespace_index).owner_type = wip_ty.index; | 1863 | ip.namespacePtr(namespace_index).owner_type = wip_ty.index; |
| 1817 | 1864 | ||
| 1818 | if (zcu.comp.incremental) { | 1865 | if (zcu.comp.incremental) { |
| 1819 | try ip.addDependency( | 1866 | try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); |
| 1820 | gpa, | ||
| 1821 | .wrap(.{ .type = wip_ty.index }), | ||
| 1822 | .{ .src_hash = tracked_inst }, | ||
| 1823 | ); | ||
| 1824 | } | 1867 | } |
| 1825 | 1868 | ||
| 1826 | try pt.scanNamespace(namespace_index, decls); | 1869 | try pt.scanNamespace(namespace_index, decls); |
| ... | @@ -1832,6 +1875,7 @@ fn createFileRootStruct( | ... | @@ -1832,6 +1875,7 @@ fn createFileRootStruct( |
| 1832 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); | 1875 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 1833 | } | 1876 | } |
| 1834 | zcu.setFileRootType(file_index, wip_ty.index); | 1877 | zcu.setFileRootType(file_index, wip_ty.index); |
| 1878 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 1835 | return wip_ty.finish(ip, namespace_index); | 1879 | return wip_ty.finish(ip, namespace_index); |
| 1836 | } | 1880 | } |
| 1837 | 1881 | ||
| ... | @@ -2734,10 +2778,11 @@ const ScanDeclIter = struct { | ... | @@ -2734,10 +2778,11 @@ const ScanDeclIter = struct { |
| 2734 | else => unit: { | 2778 | else => unit: { |
| 2735 | const name = maybe_name.unwrap().?; | 2779 | const name = maybe_name.unwrap().?; |
| 2736 | const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name); | 2780 | const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name); |
| 2737 | const nav = if (existing_unit) |eu| | 2781 | const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: { |
| 2738 | eu.unwrap().nav_val | 2782 | const nav = try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, decl.kind == .@"usingnamespace"); |
| 2739 | else | 2783 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); |
| 2740 | try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, decl.kind == .@"usingnamespace"); | 2784 | break :nav nav; |
| 2785 | }; | ||
| 2741 | 2786 | ||
| 2742 | const unit: AnalUnit = .wrap(.{ .nav_val = nav }); | 2787 | const unit: AnalUnit = .wrap(.{ .nav_val = nav }); |
| 2743 | 2788 | ||
| ... | @@ -3911,6 +3956,7 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error! | ... | @@ -3911,6 +3956,7 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error! |
| 3911 | if (result.new_nav.unwrap()) |nav| { | 3956 | if (result.new_nav.unwrap()) |nav| { |
| 3912 | // This job depends on any resolve_type_fully jobs queued up before it. | 3957 | // This job depends on any resolve_type_fully jobs queued up before it. |
| 3913 | try pt.zcu.comp.queueJob(.{ .codegen_nav = nav }); | 3958 | try pt.zcu.comp.queueJob(.{ .codegen_nav = nav }); |
| 3959 | if (pt.zcu.comp.debugIncremental()) try pt.zcu.incremental_debug_state.newNav(pt.zcu, nav); | ||
| 3914 | } | 3960 | } |
| 3915 | return result.index; | 3961 | return result.index; |
| 3916 | } | 3962 | } |
| ... | @@ -3979,6 +4025,12 @@ pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError | ... | @@ -3979,6 +4025,12 @@ pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError |
| 3979 | _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); | 4025 | _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); |
| 3980 | zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); | 4026 | zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); |
| 3981 | 4027 | ||
| 4028 | if (zcu.comp.debugIncremental()) { | ||
| 4029 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); | ||
| 4030 | info.last_update_gen = zcu.generation; | ||
| 4031 | info.deps.clearRetainingCapacity(); | ||
| 4032 | } | ||
| 4033 | |||
| 3982 | switch (ip.indexToKey(ty)) { | 4034 | switch (ip.indexToKey(ty)) { |
| 3983 | .struct_type => return pt.recreateStructType(ty, declared_ty_key), | 4035 | .struct_type => return pt.recreateStructType(ty, declared_ty_key), |
| 3984 | .union_type => return pt.recreateUnionType(ty, declared_ty_key), | 4036 | .union_type => return pt.recreateUnionType(ty, declared_ty_key), |
| ... | @@ -4042,11 +4094,7 @@ fn recreateStructType( | ... | @@ -4042,11 +4094,7 @@ fn recreateStructType( |
| 4042 | errdefer wip_ty.cancel(ip, pt.tid); | 4094 | errdefer wip_ty.cancel(ip, pt.tid); |
| 4043 | 4095 | ||
| 4044 | wip_ty.setName(ip, struct_obj.name); | 4096 | wip_ty.setName(ip, struct_obj.name); |
| 4045 | try ip.addDependency( | 4097 | try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); |
| 4046 | gpa, | ||
| 4047 | .wrap(.{ .type = wip_ty.index }), | ||
| 4048 | .{ .src_hash = key.zir_index }, | ||
| 4049 | ); | ||
| 4050 | zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index; | 4098 | zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index; |
| 4051 | // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive. | 4099 | // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive. |
| 4052 | try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); | 4100 | try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); |
| ... | @@ -4058,6 +4106,7 @@ fn recreateStructType( | ... | @@ -4058,6 +4106,7 @@ fn recreateStructType( |
| 4058 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); | 4106 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 4059 | } | 4107 | } |
| 4060 | 4108 | ||
| 4109 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 4061 | const new_ty = wip_ty.finish(ip, struct_obj.namespace); | 4110 | const new_ty = wip_ty.finish(ip, struct_obj.namespace); |
| 4062 | if (inst_info.inst == .main_struct_inst) { | 4111 | if (inst_info.inst == .main_struct_inst) { |
| 4063 | // This is the root type of a file! Update the reference. | 4112 | // This is the root type of a file! Update the reference. |
| ... | @@ -4138,11 +4187,7 @@ fn recreateUnionType( | ... | @@ -4138,11 +4187,7 @@ fn recreateUnionType( |
| 4138 | errdefer wip_ty.cancel(ip, pt.tid); | 4187 | errdefer wip_ty.cancel(ip, pt.tid); |
| 4139 | 4188 | ||
| 4140 | wip_ty.setName(ip, union_obj.name); | 4189 | wip_ty.setName(ip, union_obj.name); |
| 4141 | try ip.addDependency( | 4190 | try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); |
| 4142 | gpa, | ||
| 4143 | .wrap(.{ .type = wip_ty.index }), | ||
| 4144 | .{ .src_hash = key.zir_index }, | ||
| 4145 | ); | ||
| 4146 | zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; | 4191 | zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; |
| 4147 | // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive. | 4192 | // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive. |
| 4148 | try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); | 4193 | try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); |
| ... | @@ -4154,6 +4199,7 @@ fn recreateUnionType( | ... | @@ -4154,6 +4199,7 @@ fn recreateUnionType( |
| 4154 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); | 4199 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 4155 | } | 4200 | } |
| 4156 | 4201 | ||
| 4202 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 4157 | return wip_ty.finish(ip, namespace_index); | 4203 | return wip_ty.finish(ip, namespace_index); |
| 4158 | } | 4204 | } |
| 4159 | 4205 | ||
| ... | @@ -4255,6 +4301,7 @@ fn recreateEnumType( | ... | @@ -4255,6 +4301,7 @@ fn recreateEnumType( |
| 4255 | zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; | 4301 | zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; |
| 4256 | // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive. | 4302 | // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive. |
| 4257 | 4303 | ||
| 4304 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); | ||
| 4258 | wip_ty.prepare(ip, namespace_index); | 4305 | wip_ty.prepare(ip, namespace_index); |
| 4259 | done = true; | 4306 | done = true; |
| 4260 | 4307 | ||
| ... | @@ -4432,3 +4479,13 @@ pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPo | ... | @@ -4432,3 +4479,13 @@ pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPo |
| 4432 | .byte_offset = 0, | 4479 | .byte_offset = 0, |
| 4433 | } }); | 4480 | } }); |
| 4434 | } | 4481 | } |
| 4482 | |||
| 4483 | pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dependee) Allocator.Error!void { | ||
| 4484 | const zcu = pt.zcu; | ||
| 4485 | const gpa = zcu.gpa; | ||
| 4486 | try zcu.intern_pool.addDependency(gpa, unit, dependee); | ||
| 4487 | if (zcu.comp.debugIncremental()) { | ||
| 4488 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, unit); | ||
| 4489 | try info.deps.append(gpa, dependee); | ||
| 4490 | } | ||
| 4491 | } |
src/main.zig+28| ... | @@ -677,6 +677,7 @@ const usage_build_generic = | ... | @@ -677,6 +677,7 @@ const usage_build_generic = |
| 677 | \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error | 677 | \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error |
| 678 | \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format | 678 | \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format |
| 679 | \\ --debug-rt Debug compiler runtime libraries | 679 | \\ --debug-rt Debug compiler runtime libraries |
| 680 | \\ --debug-incremental Enable incremental compilation debug features | ||
| 680 | \\ | 681 | \\ |
| 681 | ; | 682 | ; |
| 682 | 683 | ||
| ... | @@ -832,6 +833,7 @@ fn buildOutputType( | ... | @@ -832,6 +833,7 @@ fn buildOutputType( |
| 832 | var data_sections = false; | 833 | var data_sections = false; |
| 833 | var listen: Listen = .none; | 834 | var listen: Listen = .none; |
| 834 | var debug_compile_errors = false; | 835 | var debug_compile_errors = false; |
| 836 | var debug_incremental = false; | ||
| 835 | var verbose_link = (native_os != .wasi or builtin.link_libc) and | 837 | var verbose_link = (native_os != .wasi or builtin.link_libc) and |
| 836 | EnvVar.ZIG_VERBOSE_LINK.isSet(); | 838 | EnvVar.ZIG_VERBOSE_LINK.isSet(); |
| 837 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and | 839 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and |
| ... | @@ -1383,6 +1385,12 @@ fn buildOutputType( | ... | @@ -1383,6 +1385,12 @@ fn buildOutputType( |
| 1383 | } | 1385 | } |
| 1384 | } else if (mem.eql(u8, arg, "--debug-rt")) { | 1386 | } else if (mem.eql(u8, arg, "--debug-rt")) { |
| 1385 | debug_compiler_runtime_libs = true; | 1387 | debug_compiler_runtime_libs = true; |
| 1388 | } else if (mem.eql(u8, arg, "--debug-incremental")) { | ||
| 1389 | if (build_options.enable_debug_extensions) { | ||
| 1390 | debug_incremental = true; | ||
| 1391 | } else { | ||
| 1392 | warn("Zig was compiled without debug extensions. --debug-incremental has no effect.", .{}); | ||
| 1393 | } | ||
| 1386 | } else if (mem.eql(u8, arg, "-fincremental")) { | 1394 | } else if (mem.eql(u8, arg, "-fincremental")) { |
| 1387 | dev.check(.incremental); | 1395 | dev.check(.incremental); |
| 1388 | opt_incremental = true; | 1396 | opt_incremental = true; |
| ... | @@ -3460,6 +3468,9 @@ fn buildOutputType( | ... | @@ -3460,6 +3468,9 @@ fn buildOutputType( |
| 3460 | }; | 3468 | }; |
| 3461 | 3469 | ||
| 3462 | const incremental = opt_incremental orelse false; | 3470 | const incremental = opt_incremental orelse false; |
| 3471 | if (debug_incremental and !incremental) { | ||
| 3472 | fatal("--debug-incremental requires -fincremental", .{}); | ||
| 3473 | } | ||
| 3463 | 3474 | ||
| 3464 | const disable_lld_caching = !output_to_cache; | 3475 | const disable_lld_caching = !output_to_cache; |
| 3465 | 3476 | ||
| ... | @@ -3592,6 +3603,7 @@ fn buildOutputType( | ... | @@ -3592,6 +3603,7 @@ fn buildOutputType( |
| 3592 | .cache_mode = cache_mode, | 3603 | .cache_mode = cache_mode, |
| 3593 | .subsystem = subsystem, | 3604 | .subsystem = subsystem, |
| 3594 | .debug_compile_errors = debug_compile_errors, | 3605 | .debug_compile_errors = debug_compile_errors, |
| 3606 | .debug_incremental = debug_incremental, | ||
| 3595 | .incremental = incremental, | 3607 | .incremental = incremental, |
| 3596 | .enable_link_snapshots = enable_link_snapshots, | 3608 | .enable_link_snapshots = enable_link_snapshots, |
| 3597 | .install_name = install_name, | 3609 | .install_name = install_name, |
| ... | @@ -4195,9 +4207,25 @@ fn serve( | ... | @@ -4195,9 +4207,25 @@ fn serve( |
| 4195 | const main_progress_node = std.Progress.start(.{}); | 4207 | const main_progress_node = std.Progress.start(.{}); |
| 4196 | const file_system_inputs = comp.file_system_inputs.?; | 4208 | const file_system_inputs = comp.file_system_inputs.?; |
| 4197 | 4209 | ||
| 4210 | const IncrementalDebugServer = if (build_options.enable_debug_extensions) | ||
| 4211 | @import("IncrementalDebugServer.zig") | ||
| 4212 | else | ||
| 4213 | void; | ||
| 4214 | |||
| 4215 | var ids: IncrementalDebugServer = if (comp.debugIncremental()) ids: { | ||
| 4216 | break :ids .init(comp.zcu orelse @panic("--debug-incremental requires a ZCU")); | ||
| 4217 | } else undefined; | ||
| 4218 | defer if (comp.debugIncremental()) ids.deinit(); | ||
| 4219 | |||
| 4220 | if (comp.debugIncremental()) ids.spawn(); | ||
| 4221 | |||
| 4198 | while (true) { | 4222 | while (true) { |
| 4199 | const hdr = try server.receiveMessage(); | 4223 | const hdr = try server.receiveMessage(); |
| 4200 | 4224 | ||
| 4225 | // Lock the debug server while hanling the message. | ||
| 4226 | if (comp.debugIncremental()) ids.mutex.lock(); | ||
| 4227 | defer if (comp.debugIncremental()) ids.mutex.unlock(); | ||
| 4228 | |||
| 4201 | switch (hdr.tag) { | 4229 | switch (hdr.tag) { |
| 4202 | .exit => return cleanExit(), | 4230 | .exit => return cleanExit(), |
| 4203 | .update => { | 4231 | .update => { |
test/incremental/dependency_on_type_of_inferred_global created+30| ... | @@ -0,0 +1,30 @@ | ||
| 1 | #target=x86_64-linux-selfhosted | ||
| 2 | #target=x86_64-linux-cbe | ||
| 3 | #target=x86_64-windows-cbe | ||
| 4 | #target=wasm32-wasi-selfhosted | ||
| 5 | #update=initial version | ||
| 6 | #file=main.zig | ||
| 7 | const foo = @as(u8, 123); | ||
| 8 | comptime { | ||
| 9 | // depends on value of `foo` | ||
| 10 | if (foo != 123) unreachable; | ||
| 11 | } | ||
| 12 | comptime { | ||
| 13 | // depends on type of `foo` | ||
| 14 | if (@TypeOf(&foo) != *const u8) unreachable; | ||
| 15 | } | ||
| 16 | pub fn main() void {} | ||
| 17 | #expect_stdout="" | ||
| 18 | #update=change the type | ||
| 19 | #file=main.zig | ||
| 20 | const foo = @as(u16, 123); | ||
| 21 | comptime { | ||
| 22 | // depends on value of `foo` | ||
| 23 | if (foo != 123) unreachable; | ||
| 24 | } | ||
| 25 | comptime { | ||
| 26 | // depends on type of `foo` | ||
| 27 | if (@TypeOf(&foo) != *const u8) unreachable; | ||
| 28 | } | ||
| 29 | pub fn main() void {} | ||
| 30 | #expect_error=main.zig:8:37: error: reached unreachable code | ||