| 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 and !@import("builtin").single_threaded); |
| 14 | } |
| 15 | |
| 16 | zcu: *Zcu, |
| 17 | future: ?Io.Future(void), |
| 18 | /// Held by our owner when an update is in-progress, and held by us when responding to a command. |
| 19 | /// So, essentially guards all access to `Compilation`, including `Zcu`. |
| 20 | mutex: std.Io.Mutex, |
| 21 | |
| 22 | pub fn init(zcu: *Zcu) IncrementalDebugServer { |
| 23 | return .{ |
| 24 | .zcu = zcu, |
| 25 | .future = null, |
| 26 | .mutex = .init, |
| 27 | }; |
| 28 | } |
| 29 | |
| 30 | pub fn deinit(ids: *IncrementalDebugServer) void { |
| 31 | const io = ids.zcu.comp.io; |
| 32 | if (ids.future) |*f| f.cancel(io); |
| 33 | } |
| 34 | |
| 35 | const port = 7623; |
| 36 | pub fn spawn(ids: *IncrementalDebugServer) void { |
| 37 | const io = ids.zcu.comp.io; |
| 38 | std.debug.print("spawning incremental debug server on port {d}\n", .{port}); |
| 39 | ids.future = io.concurrent(runServer, .{ids}) catch |err| |
| 40 | std.process.fatal("failed to start incremental debug server: {s}", .{@errorName(err)}); |
| 41 | } |
| 42 | fn runServer(ids: *IncrementalDebugServer) void { |
| 43 | const io = ids.zcu.comp.io; |
| 44 | |
| 45 | const addr: Io.net.IpAddress = .{ .ip6 = .loopback(port) }; |
| 46 | var server = addr.listen(io, .{}) catch |err| switch (err) { |
| 47 | error.Canceled => return, |
| 48 | else => |e| { |
| 49 | log.err("listen failed ({t}); closing server", .{e}); |
| 50 | return; |
| 51 | }, |
| 52 | }; |
| 53 | defer server.deinit(io); |
| 54 | |
| 55 | while (true) { |
| 56 | var stream = server.accept(io) catch |err| switch (err) { |
| 57 | error.Canceled => return, |
| 58 | error.ConnectionAborted => { |
| 59 | log.warn("client disconnected during accept", .{}); |
| 60 | continue; |
| 61 | }, |
| 62 | else => |e| { |
| 63 | log.err("accept failed ({t})", .{e}); |
| 64 | return; |
| 65 | }, |
| 66 | }; |
| 67 | defer stream.close(io); |
| 68 | log.info("client '{f}' connected", .{stream.socket.address}); |
| 69 | var cmd_buf: [1024]u8 = undefined; |
| 70 | var reader = stream.reader(io, &cmd_buf); |
| 71 | var writer = stream.writer(io, &.{}); |
| 72 | ids.serveStream(&reader.interface, &writer.interface) catch |orig_err| { |
| 73 | const actual_err = switch (orig_err) { |
| 74 | error.Canceled, |
| 75 | error.OutOfMemory, |
| 76 | error.EndOfStream, |
| 77 | error.StreamTooLong, |
| 78 | => |e| e, |
| 79 | |
| 80 | error.ReadFailed => reader.err.?, |
| 81 | error.WriteFailed => writer.err.?, |
| 82 | }; |
| 83 | switch (actual_err) { |
| 84 | error.Canceled => return, |
| 85 | |
| 86 | error.OutOfMemory, |
| 87 | error.Unexpected, |
| 88 | error.SystemResources, |
| 89 | error.NetworkDown, |
| 90 | error.NetworkUnreachable, |
| 91 | error.HostUnreachable, |
| 92 | error.FastOpenAlreadyInProgress, |
| 93 | error.ConnectionRefused, |
| 94 | error.StreamTooLong, |
| 95 | => |e| log.err("failed to serve '{f}' ({t})", .{ stream.socket.address, e }), |
| 96 | |
| 97 | error.EndOfStream, |
| 98 | error.ConnectionTimedOut, |
| 99 | error.ConnectionResetByPeer, |
| 100 | => log.info("client '{f}' disconnected", .{stream.socket.address}), |
| 101 | |
| 102 | error.AddressFamilyUnsupported, |
| 103 | error.SocketUnconnected, |
| 104 | error.SocketNotBound, |
| 105 | error.AccessDenied, |
| 106 | => unreachable, |
| 107 | } |
| 108 | }; |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | fn serveStream( |
| 113 | ids: *IncrementalDebugServer, |
| 114 | stream_reader: *Io.Reader, |
| 115 | stream_writer: *Io.Writer, |
| 116 | ) error{ |
| 117 | Canceled, |
| 118 | OutOfMemory, |
| 119 | EndOfStream, |
| 120 | StreamTooLong, |
| 121 | ReadFailed, |
| 122 | WriteFailed, |
| 123 | }!noreturn { |
| 124 | const gpa = ids.zcu.gpa; |
| 125 | const io = ids.zcu.comp.io; |
| 126 | |
| 127 | var text_out: std.ArrayList(u8) = .empty; |
| 128 | defer text_out.deinit(gpa); |
| 129 | |
| 130 | while (true) { |
| 131 | try stream_writer.writeAll("zig> "); |
| 132 | const untrimmed = try stream_reader.takeSentinel('\n'); |
| 133 | const cmd_and_arg = std.mem.trim(u8, untrimmed, " \t\r\n"); |
| 134 | const cmd: []const u8, const arg: []const u8 = if (std.mem.findScalar(u8, cmd_and_arg, ' ')) |i| |
| 135 | .{ cmd_and_arg[0..i], cmd_and_arg[i + 1 ..] } |
| 136 | else |
| 137 | .{ cmd_and_arg, "" }; |
| 138 | |
| 139 | text_out.clearRetainingCapacity(); |
| 140 | { |
| 141 | if (!ids.mutex.tryLock()) { |
| 142 | try stream_writer.writeAll("waiting for in-progress update to finish...\n"); |
| 143 | try ids.mutex.lock(io); |
| 144 | } |
| 145 | defer ids.mutex.unlock(io); |
| 146 | var allocating: Io.Writer.Allocating = .fromArrayList(gpa, &text_out); |
| 147 | defer text_out = allocating.toArrayList(); |
| 148 | handleCommand(ids.zcu, &allocating.writer, cmd, arg) catch |err| switch (err) { |
| 149 | error.OutOfMemory, |
| 150 | error.WriteFailed, |
| 151 | => return error.OutOfMemory, |
| 152 | }; |
| 153 | } |
| 154 | try text_out.append(gpa, '\n'); |
| 155 | try stream_writer.writeAll(text_out.items); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | const help_str: []const u8 = |
| 160 | \\[str] arguments are any string. |
| 161 | \\[id] arguments are a numeric ID/index, like an InternPool index. |
| 162 | \\[unit] arguments are strings like 'func 1234' where '1234' is the relevant index (in this case an InternPool index). |
| 163 | \\ |
| 164 | \\MISC |
| 165 | \\ summary |
| 166 | \\ Dump some information about the whole ZCU. |
| 167 | \\ nav_info [id] |
| 168 | \\ Dump basic info about a NAV. |
| 169 | \\ |
| 170 | \\SEARCHING |
| 171 | \\ find_type [str] |
| 172 | \\ Find types (including dead ones) whose names contain the given substring. |
| 173 | \\ Starting with '^' or ending with '$' anchors to the start/end of the name. |
| 174 | \\ find_nav [str] |
| 175 | \\ Find NAVs (including dead ones) whose names contain the given substring. |
| 176 | \\ Starting with '^' or ending with '$' anchors to the start/end of the name. |
| 177 | \\ |
| 178 | \\UNITS |
| 179 | \\ unit_info [unit] |
| 180 | \\ Dump basic info about an analysis unit. |
| 181 | \\ unit_dependencies [unit] |
| 182 | \\ List all units which an analysis unit depends on. |
| 183 | \\ unit_trace [unit] |
| 184 | \\ Dump the current reference trace of an analysis unit. |
| 185 | \\ |
| 186 | \\TYPES |
| 187 | \\ type_info [id] |
| 188 | \\ Dump basic info about a type. |
| 189 | \\ type_namespace [id] |
| 190 | \\ List all declarations in the namespace of a type. |
| 191 | \\ |
| 192 | ; |
| 193 | |
| 194 | fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const u8) error{ WriteFailed, OutOfMemory }!void { |
| 195 | const ip = &zcu.intern_pool; |
| 196 | if (std.mem.eql(u8, cmd_str, "help")) { |
| 197 | try w.writeAll(help_str); |
| 198 | } else if (std.mem.eql(u8, cmd_str, "summary")) { |
| 199 | try w.print( |
| 200 | \\last generation: {d} |
| 201 | \\total container types: {d} |
| 202 | \\total NAVs: {d} |
| 203 | \\total units: {d} |
| 204 | \\ |
| 205 | , .{ |
| 206 | zcu.generation - 1, |
| 207 | zcu.incremental_debug_state.types.count(), |
| 208 | zcu.incremental_debug_state.navs.count(), |
| 209 | zcu.incremental_debug_state.units.count(), |
| 210 | }); |
| 211 | } else if (std.mem.eql(u8, cmd_str, "nav_info")) { |
| 212 | const nav_index: InternPool.Nav.Index = @fromBackingInt(@intCast(parseIndex(arg_str) orelse return w.writeAll("malformed nav index"))); |
| 213 | const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index"); |
| 214 | const nav = ip.getNav(nav_index); |
| 215 | try w.print( |
| 216 | \\name: '{f}' |
| 217 | \\fqn: '{f}' |
| 218 | \\created on generation: {d} |
| 219 | \\ |
| 220 | , .{ |
| 221 | nav.name.fmt(ip), |
| 222 | nav.fqn.fmt(ip), |
| 223 | create_gen, |
| 224 | }); |
| 225 | if (nav.resolved) |r| { |
| 226 | try w.writeAll("status: resolved\n type: "); |
| 227 | try printType(.fromInterned(r.type), zcu, w); |
| 228 | try w.writeAll("\n value: "); |
| 229 | if (r.value == .none) { |
| 230 | try w.writeAll("(unresolved)"); |
| 231 | } else { |
| 232 | try printType(.fromInterned(r.type), zcu, w); |
| 233 | } |
| 234 | try w.writeByte('\n'); |
| 235 | } else { |
| 236 | try w.writeAll("status: unresolved\n"); |
| 237 | } |
| 238 | } else if (std.mem.eql(u8, cmd_str, "find_type")) { |
| 239 | if (arg_str.len == 0) return w.writeAll("bad usage"); |
| 240 | const anchor_start = arg_str[0] == '^'; |
| 241 | const anchor_end = arg_str[arg_str.len - 1] == '$'; |
| 242 | const query = arg_str[@intFromBool(anchor_start) .. arg_str.len - @intFromBool(anchor_end)]; |
| 243 | var num_results: usize = 0; |
| 244 | for (zcu.incremental_debug_state.types.keys()) |type_ip_index| { |
| 245 | const ty: Type = .fromInterned(type_ip_index); |
| 246 | const ty_name = ty.containerTypeName(ip).toSlice(ip); |
| 247 | const success = switch (@as(u2, @intFromBool(anchor_start)) << 1 | @intFromBool(anchor_end)) { |
| 248 | 0b00 => std.mem.find(u8, ty_name, query) != null, |
| 249 | 0b01 => std.mem.endsWith(u8, ty_name, query), |
| 250 | 0b10 => std.mem.startsWith(u8, ty_name, query), |
| 251 | 0b11 => std.mem.eql(u8, ty_name, query), |
| 252 | }; |
| 253 | if (success) { |
| 254 | num_results += 1; |
| 255 | try w.print("* type {d} ('{s}')\n", .{ @backingInt(type_ip_index), ty_name }); |
| 256 | } |
| 257 | } |
| 258 | try w.print("Found {d} results\n", .{num_results}); |
| 259 | } else if (std.mem.eql(u8, cmd_str, "find_nav")) { |
| 260 | if (arg_str.len == 0) return w.writeAll("bad usage"); |
| 261 | const anchor_start = arg_str[0] == '^'; |
| 262 | const anchor_end = arg_str[arg_str.len - 1] == '$'; |
| 263 | const query = arg_str[@intFromBool(anchor_start) .. arg_str.len - @intFromBool(anchor_end)]; |
| 264 | var num_results: usize = 0; |
| 265 | for (zcu.incremental_debug_state.navs.keys()) |nav_index| { |
| 266 | const nav = ip.getNav(nav_index); |
| 267 | const nav_fqn = nav.fqn.toSlice(ip); |
| 268 | const success = switch (@as(u2, @intFromBool(anchor_start)) << 1 | @intFromBool(anchor_end)) { |
| 269 | 0b00 => std.mem.find(u8, nav_fqn, query) != null, |
| 270 | 0b01 => std.mem.endsWith(u8, nav_fqn, query), |
| 271 | 0b10 => std.mem.startsWith(u8, nav_fqn, query), |
| 272 | 0b11 => std.mem.eql(u8, nav_fqn, query), |
| 273 | }; |
| 274 | if (success) { |
| 275 | num_results += 1; |
| 276 | try w.print("* nav {d} ('{s}')\n", .{ @backingInt(nav_index), nav_fqn }); |
| 277 | } |
| 278 | } |
| 279 | try w.print("Found {d} results\n", .{num_results}); |
| 280 | } else if (std.mem.eql(u8, cmd_str, "unit_info")) { |
| 281 | const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); |
| 282 | const unit_info = zcu.incremental_debug_state.units.get(unit) orelse return w.writeAll("unknown anal unit"); |
| 283 | var ref_str_buf: [32]u8 = undefined; |
| 284 | const ref_str: []const u8 = ref: { |
| 285 | const refs = try zcu.resolveReferences(); |
| 286 | const ref = refs.get(unit) orelse break :ref "<unreferenced>"; |
| 287 | const referencer = (ref orelse break :ref "<analysis root>").referencer; |
| 288 | break :ref printAnalUnit(referencer, &ref_str_buf); |
| 289 | }; |
| 290 | try w.print( |
| 291 | \\last update generation: {d} |
| 292 | \\current referencer: {s} |
| 293 | \\ |
| 294 | , .{ |
| 295 | unit_info.last_update_gen, |
| 296 | ref_str, |
| 297 | }); |
| 298 | if (zcu.failed_analysis.get(unit)) |err_msg| { |
| 299 | try w.print("analysis result: failure ({q})\n", .{err_msg.msg}); |
| 300 | } else if (zcu.transitive_failed_analysis.get(unit)) |reason| { |
| 301 | switch (reason) { |
| 302 | .astgen_error => try w.writeAll("analysis result: transitive failure (astgen error)\n"), |
| 303 | .dependency_loop => try w.writeAll("analysis result: transitive failure (dependency loop)\n"), |
| 304 | .lost_tracking => try w.writeAll("analysis result: transitive failure (lost tracking for zir inst)\n"), |
| 305 | .failed_unit => |other_unit| { |
| 306 | var buf: [32]u8 = undefined; |
| 307 | try w.print("analysis result: transitive failure (failed unit: {s})\n", .{printAnalUnit(other_unit, &buf)}); |
| 308 | }, |
| 309 | .func_nav_val_changed => |func_index| try w.print("analysis result: transitive failure (owner nav of func '{d}' changed value)\n", .{@backingInt(func_index)}), |
| 310 | } |
| 311 | } else { |
| 312 | try w.writeAll("analysis result: success\n"); |
| 313 | } |
| 314 | if (unit.unwrap() == .func) { |
| 315 | const nav_id = zcu.intern_pool.indexToKey(unit.unwrap().func).func.owner_nav; |
| 316 | try w.print("owner nav: {d}\n", .{@backingInt(nav_id)}); |
| 317 | } |
| 318 | } else if (std.mem.eql(u8, cmd_str, "unit_dependencies")) { |
| 319 | const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); |
| 320 | const unit_info = zcu.incremental_debug_state.units.get(unit) orelse return w.writeAll("unknown anal unit"); |
| 321 | for (unit_info.deps.items, 0..) |dependee, i| { |
| 322 | try w.print("[{d}] ", .{i}); |
| 323 | switch (dependee) { |
| 324 | .src_hash, .namespace, .namespace_name, .source_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}), |
| 325 | .nav_val, .nav_ty => |nav| try w.print("{t} {d}", .{ dependee, @backingInt(nav) }), |
| 326 | .type_layout, .struct_defaults, .func_ies => |ip_index| try w.print("{t} {d}", .{ dependee, @backingInt(ip_index) }), |
| 327 | .memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}), |
| 328 | } |
| 329 | try w.writeByte('\n'); |
| 330 | } |
| 331 | } else if (std.mem.eql(u8, cmd_str, "unit_trace")) { |
| 332 | const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); |
| 333 | if (!zcu.incremental_debug_state.units.contains(unit)) return w.writeAll("unknown anal unit"); |
| 334 | const refs = try zcu.resolveReferences(); |
| 335 | if (!refs.contains(unit)) return w.writeAll("not referenced"); |
| 336 | var opt_cur: ?AnalUnit = unit; |
| 337 | while (opt_cur) |cur| { |
| 338 | var buf: [32]u8 = undefined; |
| 339 | try w.print("* {s}\n", .{printAnalUnit(cur, &buf)}); |
| 340 | opt_cur = if (refs.get(cur).?) |ref| ref.referencer else null; |
| 341 | } |
| 342 | } else if (std.mem.eql(u8, cmd_str, "type_info")) { |
| 343 | const ip_index: InternPool.Index = @fromBackingInt(@intCast(parseIndex(arg_str) orelse return w.writeAll("malformed ip index"))); |
| 344 | const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type"); |
| 345 | try w.print( |
| 346 | \\name: '{f}' |
| 347 | \\created on generation: {d} |
| 348 | \\ |
| 349 | , .{ |
| 350 | Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip), |
| 351 | create_gen, |
| 352 | }); |
| 353 | } else if (std.mem.eql(u8, cmd_str, "type_namespace")) { |
| 354 | const ip_index: InternPool.Index = @fromBackingInt(@intCast(parseIndex(arg_str) orelse return w.writeAll("malformed ip index"))); |
| 355 | if (!zcu.incremental_debug_state.types.contains(ip_index)) return w.writeAll("unknown type"); |
| 356 | const ns = zcu.namespacePtr(Type.fromInterned(ip_index).getNamespaceIndex(zcu)); |
| 357 | try w.print("{d} pub decls:\n", .{ns.pub_decls.count()}); |
| 358 | for (ns.pub_decls.keys()) |nav| { |
| 359 | try w.print("* nav {d}\n", .{@backingInt(nav)}); |
| 360 | } |
| 361 | try w.print("{d} non-pub decls:\n", .{ns.priv_decls.count()}); |
| 362 | for (ns.priv_decls.keys()) |nav| { |
| 363 | try w.print("* nav {d}\n", .{@backingInt(nav)}); |
| 364 | } |
| 365 | try w.print("{d} comptime decls:\n", .{ns.comptime_decls.items.len}); |
| 366 | for (ns.comptime_decls.items) |id| { |
| 367 | try w.print("* comptime {d}\n", .{@backingInt(id)}); |
| 368 | } |
| 369 | try w.print("{d} tests:\n", .{ns.test_decls.items.len}); |
| 370 | for (ns.test_decls.items) |nav| { |
| 371 | try w.print("* nav {d}\n", .{@backingInt(nav)}); |
| 372 | } |
| 373 | } else { |
| 374 | try w.writeAll("command not found; run 'help' for a command list"); |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | fn parseIndex(str: []const u8) ?u32 { |
| 379 | return std.fmt.parseInt(u32, str, 10) catch null; |
| 380 | } |
| 381 | fn parseAnalUnit(str: []const u8) ?AnalUnit { |
| 382 | const split_idx = std.mem.findScalar(u8, str, ' ') orelse return null; |
| 383 | const kind = str[0..split_idx]; |
| 384 | const idx_str = str[split_idx + 1 ..]; |
| 385 | if (std.mem.eql(u8, kind, "comptime")) { |
| 386 | return .wrap(.{ .@"comptime" = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); |
| 387 | } else if (std.mem.eql(u8, kind, "nav_val")) { |
| 388 | return .wrap(.{ .nav_val = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); |
| 389 | } else if (std.mem.eql(u8, kind, "nav_ty")) { |
| 390 | return .wrap(.{ .nav_ty = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); |
| 391 | } else if (std.mem.eql(u8, kind, "type_layout")) { |
| 392 | return .wrap(.{ .type_layout = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); |
| 393 | } else if (std.mem.eql(u8, kind, "struct_defaults")) { |
| 394 | return .wrap(.{ .struct_defaults = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); |
| 395 | } else if (std.mem.eql(u8, kind, "func")) { |
| 396 | return .wrap(.{ .func = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); |
| 397 | } else if (std.mem.eql(u8, kind, "memoized_state")) { |
| 398 | return .wrap(.{ .memoized_state = std.meta.stringToEnum( |
| 399 | InternPool.MemoizedStateStage, |
| 400 | idx_str, |
| 401 | ) orelse return null }); |
| 402 | } else { |
| 403 | return null; |
| 404 | } |
| 405 | } |
| 406 | fn printAnalUnit(unit: AnalUnit, buf: *[32]u8) []const u8 { |
| 407 | const idx: u32 = switch (unit.unwrap()) { |
| 408 | .memoized_state => |stage| return std.mem.print(buf, "memoized_state {s}", .{@tagName(stage)}) catch unreachable, |
| 409 | inline else => |i| @backingInt(i), |
| 410 | }; |
| 411 | return std.mem.print(buf, "{s} {d}", .{ @tagName(unit.unwrap()), idx }) catch unreachable; |
| 412 | } |
| 413 | |
| 414 | fn printType(ty: Type, zcu: *const Zcu, w: *Io.Writer) Io.Writer.Error!void { |
| 415 | const ip = &zcu.intern_pool; |
| 416 | switch (ip.indexToKey(ty.toIntern())) { |
| 417 | .int_type => |int| try w.print("{c}{d}", .{ |
| 418 | @as(u8, if (int.signedness == .unsigned) 'u' else 'i'), |
| 419 | int.bits, |
| 420 | }), |
| 421 | .tuple_type => try w.writeAll("(tuple)"), |
| 422 | .error_set_type => try w.writeAll("(error set)"), |
| 423 | .inferred_error_set_type => try w.writeAll("(inferred error set)"), |
| 424 | .func_type => try w.writeAll("(function)"), |
| 425 | .anyframe_type => try w.writeAll("(anyframe)"), |
| 426 | .vector_type => { |
| 427 | try w.print("@Vector({d}, ", .{ty.vectorLen(zcu)}); |
| 428 | try printType(ty.childType(zcu), zcu, w); |
| 429 | try w.writeByte(')'); |
| 430 | }, |
| 431 | .array_type => { |
| 432 | try w.print("[{d}]", .{ty.arrayLen(zcu)}); |
| 433 | try printType(ty.childType(zcu), zcu, w); |
| 434 | }, |
| 435 | .opt_type => { |
| 436 | try w.writeByte('?'); |
| 437 | try printType(ty.optionalChild(zcu), zcu, w); |
| 438 | }, |
| 439 | .error_union_type => { |
| 440 | try printType(ty.errorUnionSet(zcu), zcu, w); |
| 441 | try w.writeByte('!'); |
| 442 | try printType(ty.errorUnionPayload(zcu), zcu, w); |
| 443 | }, |
| 444 | .ptr_type => { |
| 445 | try w.writeAll("*(attrs) "); |
| 446 | try printType(ty.childType(zcu), zcu, w); |
| 447 | }, |
| 448 | .simple_type => |simple| try w.writeAll(@tagName(simple)), |
| 449 | |
| 450 | .struct_type, |
| 451 | .union_type, |
| 452 | .enum_type, |
| 453 | .opaque_type, |
| 454 | => try w.print("{f}[{d}]", .{ ty.containerTypeName(ip).fmt(ip), @backingInt(ty.toIntern()) }), |
| 455 | |
| 456 | else => unreachable, |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | const std = @import("std"); |
| 461 | const Io = std.Io; |
| 462 | const Allocator = std.mem.Allocator; |
| 463 | const log = std.log.scoped(.incremental_debug_server); |
| 464 | |
| 465 | const Compilation = @import("Compilation.zig"); |
| 466 | const Zcu = @import("Zcu.zig"); |
| 467 | const InternPool = @import("InternPool.zig"); |
| 468 | const Type = @import("Type.zig"); |
| 469 | const AnalUnit = InternPool.AnalUnit; |
| 470 | |
| 471 | const IncrementalDebugServer = @This(); |