authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-05-25 18:02:16+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-05-25 18:02:16+01:00
logef35c3d5fefb8c14e17f3c7036bb21e808ee59be
treeb2178084647ef4ac98bf4d45273b4708ff0b7607
parentdc6ffc28b57a96fd03f62bc665b6ed28b8e9e67b
parent3d8e760552bc60d2c7f1f4df9c8a05c8aae2b769
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #23986 from mlugg/incremental-stuff

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 parameter242 // The usage text shows another argument after this parameter
241 // but it is handled by the parent process. The build runner243 // 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,
59args: ?[]const []const u8 = null,59args: ?[]const []const u8 = null,
60debug_log_scopes: []const []const u8 = &.{},60debug_log_scopes: []const []const u8 = &.{},
61debug_compile_errors: bool = false,61debug_compile_errors: bool = false,
62debug_incremental: bool = false,
62debug_pkg_config: bool = false,63debug_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 }
14491449
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,
190stack_report: bool,190stack_report: bool,
191debug_compiler_runtime_libs: bool,191debug_compiler_runtime_libs: bool,
192debug_compile_errors: bool,192debug_compile_errors: bool,
193/// Do not check this field directly. Instead, use the `debugIncremental` wrapper function.
194debug_incremental: bool,
193incremental: bool,195incremental: bool,
194alloc_failure_occurred: bool = false,196alloc_failure_occurred: bool = false,
195last_update_was_cache_hit: bool = false,197last_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};
770772
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.
776pub inline fn debugIncremental(comp: *const Compilation) bool {
777 if (!build_options.enable_debug_extensions) return false;
778 return comp.debug_incremental;
779}
780
771pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;781pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
772pub const SemaError = Zcu.SemaError;782pub const SemaError = Zcu.SemaError;
773783
...@@ -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 build1613 /// Normally when you create a `Compilation`, Zig will automatically build
1603 /// and link in required dependencies, such as compiler-rt and libc. When1614 /// 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
11comptime {
12 // This file should only be referenced when debug extensions are enabled.
13 std.debug.assert(@import("build_options").enable_debug_extensions);
14}
15
16zcu: *Zcu,
17thread: ?std.Thread,
18running: 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`.
21mutex: std.Thread.Mutex,
22
23pub fn init(zcu: *Zcu) IncrementalDebugServer {
24 return .{
25 .zcu = zcu,
26 .thread = null,
27 .running = .init(true),
28 .mutex = .{},
29 };
30}
31
32pub fn deinit(ids: *IncrementalDebugServer) void {
33 if (ids.thread) |t| {
34 ids.running.store(false, .monotonic);
35 t.join();
36 }
37}
38
39const port = 7623;
40pub 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}
45fn 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
86const 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
121fn 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
295fn parseIndex(str: []const u8) ?u32 {
296 return std.fmt.parseInt(u32, str, 10) catch null;
297}
298fn 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}
321fn 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}
328fn 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
374const std = @import("std");
375const Allocator = std.mem.Allocator;
376
377const Compilation = @import("Compilation.zig");
378const Zcu = @import("Zcu.zig");
379const InternPool = @import("InternPool.zig");
380const Type = @import("Type.zig");
381const AnalUnit = InternPool.AnalUnit;
382
383const 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);
29992999
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 }
30073003
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}
30223019
...@@ -3247,6 +3244,7 @@ fn zirEnumDecl(...@@ -3247,6 +3244,7 @@ fn zirEnumDecl(
32473244
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;
32523250
...@@ -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);
33783376
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 }
33863380
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}
34013396
...@@ -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}
34863482
...@@ -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 }
80298030
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 });
2140721409
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(
2161121614
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}
2192521930
...@@ -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}
2227822284
...@@ -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}
3748637492
37487pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {37493pub 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;
3749037496
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 }
3751037516
37511 try zcu.intern_pool.addDependency(sema.gpa, sema.owner, dependee);37517 try pt.addDependency(sema.owner, dependee);
37512}37518}
3751337519
37514fn isComptimeMutablePtr(sema: *Sema, val: Value) bool {37520fn 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();
3790737913
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 });
3790937920
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 }
37993799
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();
38023807
...@@ -3851,6 +3856,11 @@ fn resolveUnionInner(...@@ -3851,6 +3856,11 @@ fn resolveUnionInner(
3851 return error.AnalysisFail;3856 return error.AnalysisFail;
3852 }3857 }
38533858
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();
38563866
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.
309builtin_decl_values: BuiltinDecl.Memoized = .initFill(.none),309builtin_decl_values: BuiltinDecl.Memoized = .initFill(.none),
310310
311incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void =
312 if (build_options.enable_debug_extensions) .init else {},
313
311generation: u32 = 0,314generation: u32 = 0,
312315
316pub 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
313pub const PerThread = @import("Zcu/PerThread.zig");361pub const PerThread = @import("Zcu/PerThread.zig");
314362
315pub const ImportTableAdapter = struct {363pub 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);
27472795
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 }
637637
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 }
786792
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();
789801
...@@ -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 }
960972
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();
963981
...@@ -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 }
10061024
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}
10091056
...@@ -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));
12501297
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
12961335
1297 log.debug("ensureNavTypeUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});1336 log.debug("ensureNavTypeUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
12981337
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 the1350 // 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 }
13331384
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();
13361393
...@@ -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);
13991456
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();
14021463
...@@ -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);
14381499
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 });
14431501
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 } };
14481506
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 }
15661607
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();
15691616
...@@ -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;
18171864
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 }
18251868
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}
18371881
...@@ -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_val2782 const nav = try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, decl.kind == .@"usingnamespace");
2739 else2783 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 };
27412786
2742 const unit: AnalUnit = .wrap(.{ .nav_val = nav });2787 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
27432788
...@@ -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);
39814027
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);
40434095
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 }
40604108
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);
41394188
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 }
41564201
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}
41594205
...@@ -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.
42574303
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;
42604307
...@@ -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
4483pub 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 error677 \\ --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 format678 \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format
679 \\ --debug-rt Debug compiler runtime libraries679 \\ --debug-rt Debug compiler runtime libraries
680 \\ --debug-incremental Enable incremental compilation debug features
680 \\681 \\
681;682;
682683
...@@ -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) and837 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) and839 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 };
34613469
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 }
34633474
3464 const disable_lld_caching = !output_to_cache;3475 const disable_lld_caching = !output_to_cache;
34653476
...@@ -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.?;
41974209
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();
42004224
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
7const foo = @as(u8, 123);
8comptime {
9 // depends on value of `foo`
10 if (foo != 123) unreachable;
11}
12comptime {
13 // depends on type of `foo`
14 if (@TypeOf(&foo) != *const u8) unreachable;
15}
16pub fn main() void {}
17#expect_stdout=""
18#update=change the type
19#file=main.zig
20const foo = @as(u16, 123);
21comptime {
22 // depends on value of `foo`
23 if (foo != 123) unreachable;
24}
25comptime {
26 // depends on type of `foo`
27 if (@TypeOf(&foo) != *const u8) unreachable;
28}
29pub fn main() void {}
30#expect_error=main.zig:8:37: error: reached unreachable code