From 2768b7a704977ffc4c1eefaafddc548b42aec0e4 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Sat, 27 Jul 2024 05:08:05 -0700 Subject: [PATCH] feat: allow dumping compiler state --- CHANGELOG.md | 1 + build.zig | 10 ++- src/core/Air.Dump.zig | 2 +- src/core/Air.zig | 10 +-- src/core/Comp.zig | 150 +++++++++++++++++++++++++++++++++++----- src/core/InternPool.zig | 105 ++++++++++++++++++++++++---- src/core/Sema.zig | 19 +++-- src/core/array.zig | 4 ++ src/core/js/Link.zig | 4 +- src/core/js/NodeGen.zig | 4 +- src/core/js/Tree.zig | 4 +- src/core/main.zig | 26 +++++++ src/core/mod.zig | 13 ++++ 13 files changed, 297 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 203b85d4b1f119d04100705c5470f32f9d610dec..a80bd28811771521440c4af557e0907df2af967d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - feat: clarify "defined runtime representation" and "can be represented at runtime" - feat: implement inferred structs + result type semantics +- feat: add `--dump-state=./filename.json` to dump compiler state as JSON - chore: remove group expressions, use single expression blocks instead - fix(sema): fix passing structs as function params - fix: diagnostic box drawing characters line up after line 9 diff --git a/build.zig b/build.zig index 54c19b08bf70601da6aa4e9b8020b6b13dc4da79..d84424fa26dfbaa7f6adc78c82b4c30b3295d731 100644 --- a/build.zig +++ b/build.zig @@ -92,7 +92,6 @@ pub fn build(b: *std.Build) !void { .root_source_file = b.path("src/manual/generate-man.zig"), .target = native_target, .optimize = .Debug, - .link_libc = true, }); manual_generator.root_module.addImport("build_options", native_debug.opts_mod); manual_generator.root_module.addImport("core", native_debug.core); @@ -204,10 +203,11 @@ fn buildWithTarget( const opts_mod = opts.createModule(); // Core - const core = b.addModule("compiler", .{ + const core = b.addModule("core", .{ .root_source_file = b.path("src/core/mod.zig"), .optimize = optimize, .target = target, + .strip = false, }); core.addImport("build_options", opts_mod); core.addImport("tracer", tracer.module("tracer")); @@ -220,6 +220,7 @@ fn buildWithTarget( .optimize = optimize, .use_lld = use_lld, .use_llvm = use_llvm, + .strip = false, }); core_tests.root_module.addImport("build_options", opts_mod); core_tests.root_module.addImport("tracer", tracer.module("tracer")); @@ -233,6 +234,8 @@ fn buildWithTarget( .optimize = optimize, .use_lld = use_lld, .use_llvm = use_llvm, + .strip = false, + .omit_frame_pointer = false, }); compiler_exe.root_module.addImport("build_options", opts_mod); compiler_exe.root_module.addImport("core", core); @@ -246,6 +249,9 @@ fn buildWithTarget( .optimize = optimize, .use_lld = use_lld, .use_llvm = use_llvm, + .strip = false, + .omit_frame_pointer = false, + .unwind_tables = false, }); lsp_exe.root_module.addImport("build_options", opts_mod); lsp_exe.root_module.addImport("core", core); diff --git a/src/core/Air.Dump.zig b/src/core/Air.Dump.zig index ef0df4fdcdf60a66b665fdf3e18c38024b3e0643..61a9a365ec0242e9849f5aede17665d11af4dcc6 100644 --- a/src/core/Air.Dump.zig +++ b/src/core/Air.Dump.zig @@ -143,7 +143,7 @@ pub const State = struct { try tty.setColor(w, .reset); try w.writeAll(")"); }, - inline .call, .new => |t| { + inline .call, .js_new => |t| { const call: Air.Call = air.instData(t, inst); const call_data = call.call_data.get(air); try w.writeAll("("); diff --git a/src/core/Air.zig b/src/core/Air.zig index 7705135ad1eb129d2722dad7e7f55f4fcba4378a..add87c643192ec31ead80b408ecdcc4544e81c73 100644 --- a/src/core/Air.zig +++ b/src/core/Air.zig @@ -16,7 +16,7 @@ pub const Inst = struct { block, js_global_ref, call, - new, + js_new, @"break", @"if", arg, @@ -53,7 +53,7 @@ pub const Inst = struct { .js_global_ref => InternPool.String.WithLen, .@"break" => Break, .@"if" => If, - .call, .new => Call, + .call, .js_new => Call, .arg => Arg, .memory => Memory, .store => Store, @@ -323,7 +323,7 @@ pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *const InternPool) InternPool. pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) InternPool.Type { return switch (air.instTag(index)) { // These values are by definition, opaque - .js_global_ref, .new => .@"opaque", + .js_global_ref, .js_new => .@"opaque", // These operations always produce known types .lt, .gt, .eq, .bool_not => .bool, @@ -375,7 +375,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In .field_struct => { const inst = air.instData(.field_struct, index); const base_type = air.typeOfIndex(inst.base, ip) - .toStructType(ip) orelse unreachable; // not a struct + .toStructType(ip) orelse unreachable; // field_struct demands struct type return base_type.field_types[inst.field_index]; }, @@ -383,7 +383,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In .index => { const inst = air.instData(.index, index); const base_type = air.typeOf(inst.lhs, ip) - .toArrayType(ip) orelse unreachable; // not an array + .toArrayType(ip) orelse unreachable; // index demands array type return base_type.item_type; }, }; diff --git a/src/core/Comp.zig b/src/core/Comp.zig index edeba0893f87cf182eeab20e16122341cb64a4ff..6b231aeeebd4353d7b81f39fa3d92a7e90a9751e 100644 --- a/src/core/Comp.zig +++ b/src/core/Comp.zig @@ -8,7 +8,8 @@ gpa: Allocator, /// Stores all Type and Value objects. intern_pool: InternPool = .{}, /// All files in the compilation -files: std.StringArrayHashMapUnmanaged(*File) = .{}, +/// TODO: turn this into a HashMap(*File, void) as File owns it's key +files: std.StringHashMapUnmanaged(*File) = .{}, options: Options = .{}, @@ -20,8 +21,9 @@ pub fn init(comp: *Comp) !void { pub fn deinit(comp: *Comp) void { comp.intern_pool.deinit(comp.gpa); - for (comp.files.values()) |file| { - file.deinit(comp.gpa); + var it = comp.files.valueIterator(); + while (it.next()) |file| { + file.*.deinit(comp.gpa); } comp.files.deinit(comp.gpa); comp.backend.deinit(comp); @@ -96,7 +98,7 @@ pub const File = struct { /// TODO: change this to a URL file_path: []const u8, - root_namespace: Decl.Index.Optional, + root_namespace: Namespace.Index.Optional, status: enum { never_loaded, /// A failure occurred while loading the file. Retrying might succeed. @@ -249,6 +251,36 @@ pub const File = struct { file.unloadSource(gpa); gpa.destroy(file); } + + pub fn fmtJson(file: *const File, comp: *const Comp) JsonFormatter { + return .{ .file = file, .comp = comp }; + } + + const JsonFormatter = struct { + file: *const File, + comp: *const Comp, + + pub fn jsonStringify(ctx: JsonFormatter, json_writer: anytype) !void { + // file path is already written + const jw = core.jsonWriterCast(json_writer); + try jw.beginObject(); + try jw.objectField("path_digest"); + try if (ctx.file.path_digest) |digest| + jw.write(std.fmt.bytesToHex(digest, .lower)) + else + jw.write(null); + try jw.objectField("status"); + try jw.write(ctx.file.status); + try jw.objectField("root_namespace"); + if (ctx.file.root_namespace.unwrap()) |ns_index| { + const ns = ctx.comp.intern_pool.namespacePtrConst(ns_index); + try jw.write(ns.fmtJson(ctx.comp)); + } else { + try jw.write(null); + } + try jw.endObject(); + } + }; }; /// A declaration is a named value which also represents the smallest unit of @@ -328,12 +360,43 @@ pub const Decl = struct { }; } - pub fn deinit(decl: *Decl, gpa: Allocator) void { - _ = gpa; // autofix - defer decl.* = undefined; - // if (decl.codegen) |*cg| cg.deinit(gpa); + pub fn fmtJson(decl: *const Decl, comp: *const Comp) JsonFormatter { + return .{ .decl = decl, .comp = comp }; } + const JsonFormatter = struct { + decl: *const Decl, + comp: *const Comp, + + pub fn jsonStringify(ctx: JsonFormatter, json_writer: anytype) !void { + const decl = ctx.decl; + const comp = ctx.comp; + + const jw = core.jsonWriterCast(json_writer); + try jw.beginObject(); + + try jw.objectField("name"); + try jw.write(decl.name.toSlice(&comp.intern_pool)); + try jw.objectField("analysis"); + try jw.write(decl.analysis); + try jw.objectField("teg_decl_index"); + try jw.write(decl.teg_decl_index.get(&comp.intern_pool)); + try jw.objectField("value"); + if (decl.raw_value) |val| { + var buf: [8192]u8 = undefined; + try jw.print("", .{}); + try jw.stream.print("[{d}, ", .{@intFromEnum(val)}); + try std.json.encodeJsonString( + std.fmt.bufPrint(&buf, "{}", .{val.dump(&comp.intern_pool)}) catch &buf, + .{}, + jw.stream, + ); + try jw.stream.print("]", .{}); + } + try jw.endObject(); + } + }; + pub const Index = InternPool.DeclIndex; }; @@ -348,8 +411,6 @@ pub const Namespace = struct { file_scope: *File, /// Direct children of the namespace. /// Declaration order is preserved via entry order. - /// These are only declarations named directly by the AST; anonymous - /// declarations are not stored here. decls: std.ArrayHashMapUnmanaged(Decl.Index, void, DeclContext, true) = .{}, pub const DeclContext = struct { @@ -400,6 +461,34 @@ pub const Namespace = struct { return ns.decls.getKeyAdapted(name, Namespace.DeclNameAdapter{ .comp = comp }); } + pub fn fmtJson(ns: *const Namespace, comp: *const Comp) JsonFormatter { + return .{ .ns = ns, .comp = comp }; + } + + const JsonFormatter = struct { + ns: *const Namespace, + comp: *const Comp, + + pub fn jsonStringify(ctx: JsonFormatter, json_writer: anytype) !void { + const ns = ctx.ns; + const comp = ctx.comp; + + const jw = core.jsonWriterCast(json_writer); + try jw.beginObject(); + // try jw.objectField("parent"); + // try if (ns.parent.unwrap()) |p| jw.write(@intFromEnum(p)) else jw.write(null); + try jw.objectField("decls"); + try jw.beginObject(); + for (ns.decls.keys()) |decl_index| { + const decl = comp.intern_pool.declPtrConst(decl_index); + try jw.objectField(decl.name.toSlice(&comp.intern_pool)); + try jw.write(decl.fmtJson(comp)); + } + try jw.endObject(); + try jw.endObject(); + } + }; + pub const Index = InternPool.NamespaceIndex; }; @@ -408,10 +497,13 @@ pub const Namespace = struct { pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !Namespace.Index { const teg = file.teg.?; + assert(file.root_namespace == .none); + const ns = try comp.intern_pool.createNamespace(comp.gpa, .{ .parent = .none, .file_scope = file, }); + file.root_namespace = ns.toOptional(); const ns_ptr = comp.intern_pool.namespacePtr(ns); errdefer comp.intern_pool.destroyNamespace(comp.gpa, ns); @@ -450,6 +542,8 @@ const Options = struct { dump_ir: bool = false, }; +/// The compiler backend is stored separately from the compilation, and +/// interaction is done via this static dispatch contract. pub const Backend = union(enum) { js: *@import("js/Js.zig"), @@ -485,6 +579,23 @@ pub const Backend = union(enum) { pub const Tag = std.meta.FieldEnum(Backend); }; +pub fn jsonStringify(comp: *const Comp, json_writer: anytype) !void { + const jw = core.jsonWriterCast(json_writer); + + try jw.beginObject(); + try jw.objectField("files"); + try jw.beginObject(); + var it = comp.files.valueIterator(); + while (it.next()) |entry| { + try jw.objectField(entry.*.file_path); + try jw.write(entry.*.fmtJson(comp)); + } + try jw.endObject(); + try jw.objectField("intern_pool"); + try jw.write(comp.intern_pool); + try jw.endObject(); +} + const Comp = @This(); const std = @import("std"); @@ -493,15 +604,16 @@ const assert = std.debug.assert; const Stat = std.Build.Cache.File.Stat; const log = std.log.scoped(.comp); -const InternPool = @import("InternPool.zig"); +const core = @import("mod.zig"); +const InternPool = core.InternPool; -const Ast = @import("Ast.zig"); -const Air = @import("Air.zig"); -const Teg = @import("Teg.zig"); -const Cache = @import("Cache.zig"); -const Sema = @import("Sema.zig"); -const Transform = @import("Transform.zig"); -const Report = @import("Report.zig"); -const Parse = @import("Parse.zig"); +const Ast = core.Ast; +const Air = core.Air; +const Teg = core.Teg; +const Cache = core.Cache; +const Sema = core.Sema; +const Transform = core.Transform; +const Report = core.Report; +const Parse = core.Parse; const tracer = @import("tracer"); diff --git a/src/core/InternPool.zig b/src/core/InternPool.zig index a4160990fffaf692b393c63017e29c0b5ec8c048..2dface82fb4b12aaaef9a302d3d7e70d09bc4dd4 100644 --- a/src/core/InternPool.zig +++ b/src/core/InternPool.zig @@ -714,14 +714,21 @@ pub const TrackedInst = extern struct { pub const Index = enum(u32) { _, pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Teg.Inst.Index { - return ip.tracked_insts.keys()[@intFromEnum(i)].inst; + return i.get(ip).inst; } + + pub fn get(i: TrackedInst.Index, ip: *const InternPool) TrackedInst { + return ip.tracked_insts.keys()[@intFromEnum(i)]; + } + pub fn toOptional(i: TrackedInst.Index) Optional { return @enumFromInt(@intFromEnum(i)); } + pub const Optional = enum(u32) { none = std.math.maxInt(u32), _, + pub fn unwrap(opt: Optional) ?TrackedInst.Index { return switch (opt) { .none => null, @@ -730,6 +737,16 @@ pub const TrackedInst = extern struct { } }; }; + + pub fn jsonStringify(i: *const TrackedInst, json_writer: anytype) !void { + const jw = core.jsonWriterCast(json_writer); + try jw.beginObject(); + try jw.objectField("path_digest"); + try jw.write(std.fmt.bytesToHex(i.path_digest, .lower)); + try jw.objectField("inst"); + try jw.write(@intFromEnum(i.inst)); + try jw.endObject(); + } }; pub const DeclIndex = enum(u32) { @@ -757,7 +774,7 @@ pub const DeclIndex = enum(u32) { pub const NamespaceIndex = enum(u32) { _, - pub fn toOptional(i: DeclIndex) Optional { + pub fn toOptional(i: NamespaceIndex) Optional { return @enumFromInt(@intFromEnum(i)); } @@ -1096,11 +1113,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { // ip.limbs.deinit(gpa); ip.string_bytes.deinit(gpa); - var decls = ip.allocated_decls.iterator(0); - while (decls.next()) |decl| { - decl.deinit(gpa); - } - ip.allocated_decls.deinit(gpa); ip.decls_free_list.deinit(gpa); @@ -1346,11 +1358,12 @@ fn getOrPutTrailingString( return @enumFromInt(str_index); }, } - const gop = try ip.string_table.getOrPutContextAdapted(gpa, key, std.hash_map.StringIndexAdapter{ - .bytes = string_bytes, - }, std.hash_map.StringIndexContext{ - .bytes = string_bytes, - }); + const gop = try ip.string_table.getOrPutContextAdapted( + gpa, + key, + std.hash_map.StringIndexAdapter{ .bytes = string_bytes }, + std.hash_map.StringIndexContext{ .bytes = string_bytes }, + ); if (gop.found_existing) { string_bytes.shrinkRetainingCapacity(str_index); return @enumFromInt(gop.key_ptr.*); @@ -1445,6 +1458,74 @@ pub fn destroyNamespace(ip: *InternPool, gpa: Allocator, index: NamespaceIndex) }; } +pub fn jsonStringify(ip: *const InternPool, json_writer: anytype) !void { + const jw = core.jsonWriterCast(json_writer); + try jw.beginObject(); + { + try jw.objectField("values"); + try jw.beginArray(); + const hashes: []u32 = ip.map.entries.items(.hash); + const values = ip.items.slice(); + for ( + @as([]Tag, values.items(.tag)), + @as([]EncodedData, values.items(.data)), + hashes, + 0.., + ) |tag, encoded_data, hash, i| { + if (tag == .removed) { + try jw.write(null); + continue; + } + try jw.beginObject(); + try jw.objectField("pretty"); + { + var buf: [8192]u8 = undefined; + try jw.print("", .{}); + try jw.stream.print("[{d}, ", .{i}); + try std.json.encodeJsonString(std.fmt.bufPrint( + &buf, + "{}", + .{@as(Value, @enumFromInt(i)).dump(ip)}, + ) catch &buf, .{}, jw.stream); + try jw.stream.print("]", .{}); + } + try jw.objectField("tag"); + try jw.write(@tagName(tag)); + switch (tag) { + .removed => unreachable, + inline else => |comptime_tag| { + const data = encoded_data.decode(comptime_tag, tag); + _ = data; + // try jw.objectField("data"); + // try jw.write(data); + }, + } + try jw.objectField("hash"); + try jw.write(hash); + try jw.endObject(); + } + try jw.endArray(); + } + { + try jw.objectField("string_bytes"); + try jw.write(ip.string_bytes.items); + } + { + try jw.objectField("string_table"); + try jw.beginArray(); + var it = ip.string_table.keyIterator(); + while (it.next()) |k| { + try jw.write(k.*); + } + try jw.endArray(); + } + { + try jw.objectField("tracked_insts"); + try jw.write(ip.tracked_insts.entries.items(.key)); + } + try jw.endObject(); +} + const InternPool = @This(); const std = @import("std"); diff --git a/src/core/Sema.zig b/src/core/Sema.zig index 4a3922ab18ee7b83a2b3510e5da7552429f15199..47f151799a1232a592c63ddce6bee213e2b3e2c1 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -622,7 +622,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { const start = s.air_extra.list.items.len; try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch)); - return s.addInstRef(.new, .{ + return s.addInstRef(.js_new, .{ .base = base, .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{ .args = .{ .start = @intCast(start), @@ -774,13 +774,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { // Ensure we have a valid struct to operate on const ty = try s.resolveType(in.op(.operand), node.base_type); - const struct_type = ty.toStructType(s.ip) orelse return s.fail( - .struct_init_invalid_type, - .{ + const struct_type = ty.toStructType(s.ip) orelse { + return s.fail(.struct_init_invalid_type, .{ .src = in.op(.operand).lazyNode(s.decl_index), .ty = ty, - }, - ); + }); + }; var has_errors = false; var is_constant = true; @@ -957,7 +956,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { const operand = try s.resolveValue(result.type); const ty = operand.toType(s.ip) orelse - return Value.opaque_type; + return Value.opaque_type.toAir(); const array_type = ty.toArrayType(s.ip) orelse return Value.opaque_type.toAir(); @@ -966,11 +965,11 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { }, .result_type_tuple_field => { - const result: Teg.ResultTypeTupleField = s.code.instData(.result_type_array_value, in); + const result: Teg.ResultTypeTupleField = s.code.instData(.result_type_tuple_field, in); const operand = try s.resolveValue(result.type); const ty = operand.toType(s.ip) orelse - return Value.opaque_type; + return Value.opaque_type.toAir(); _ = ty; @@ -982,7 +981,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { const operand = try s.resolveValue(result.type); const ty = operand.toType(s.ip) orelse - return Value.opaque_type; + return Value.opaque_type.toAir(); const struct_type = ty.toStructType(s.ip) orelse return Value.opaque_type.toAir(); diff --git a/src/core/array.zig b/src/core/array.zig index a9bad97d30df002bf493ea02aeac1e6570ff50f5..e7d984f5302de89b33184a2cb735e5ac636b62fb 100644 --- a/src/core/array.zig +++ b/src/core/array.zig @@ -133,6 +133,10 @@ fn ExtraWrap(comptime kind: StoreKind) type { .{ .index = @intFromEnum(index) + advance }, }; } + + pub fn jsonStringify(index: @This(), jw: anytype) !void { + try jw.write(@intFromEnum(index)); + } }; } }; diff --git a/src/core/js/Link.zig b/src/core/js/Link.zig index be43e842e1deb0c2d507dfc42662b09c6b9130c6..62044ce809f85556460c97e5678017f9c0c1ffa9 100644 --- a/src/core/js/Link.zig +++ b/src/core/js/Link.zig @@ -475,8 +475,8 @@ fn printExprLevel( try writer.writeAll(")"); }, - .new => { - const call: Tree.Call = tree.nodeData(.new, node); + .js_new => { + const call: Tree.Call = tree.nodeData(.js_new, node); const args = call.args.get(tree); if (Level.new.needsParens(level)) diff --git a/src/core/js/NodeGen.zig b/src/core/js/NodeGen.zig index 5a49dc016c7f0d23d5ab2194e599b8c011855c25..88f0e2bdb3f920ac45fd0f92fd697039bace90ef 100644 --- a/src/core/js/NodeGen.zig +++ b/src/core/js/NodeGen.zig @@ -273,7 +273,7 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { try c.markExpression(inst, node); }, - inline .call, .new => |tag| { + inline .call, .js_new => |tag| { const data: Air.Call = c.air.instData(tag, inst); const call_data = data.call_data.get(c.air); @@ -653,7 +653,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool { .index => |index| switch (c.air.instTag(index)) { .js_global_ref, .call, - .new, + .js_new, .arg, .field_opaque, .field_struct, diff --git a/src/core/js/Tree.zig b/src/core/js/Tree.zig index 1ca7092263a8e84d42004f21940fbcb60265feae..b37281f792af1c0f46c4ef87a33699d370febff8 100644 --- a/src/core/js/Tree.zig +++ b/src/core/js/Tree.zig @@ -41,7 +41,7 @@ pub const Tag = enum { gt, // Exprs call, - new, + js_new, field, ternary, object_literal, @@ -93,7 +93,7 @@ pub const Tag = enum { => Un, .object_literal => ObjectLiteral, .array => IndexList, - .call, .new => Call, + .call, .js_new => Call, .field => Field, .stmt_if => StmtIf, .stmt_break => StmtBreak, diff --git a/src/core/main.zig b/src/core/main.zig index c7bb7ce0fc99035cc82189db440f4a8e6890f409..2a97bbca84e5d13e7ee564a9f758f3d60818da79 100644 --- a/src/core/main.zig +++ b/src/core/main.zig @@ -40,6 +40,7 @@ pub fn main() !void { var exe_name: []const u8 = undefined; var dump_ir = false; + var dump_state: ?[]const u8 = null; { var args = try std.process.argsWithAllocator(alloc); @@ -58,6 +59,10 @@ pub fn main() !void { dump_ir = true; continue; } + if (std.mem.startsWith(u8, arg, "--dump-state=")) { + dump_state = arg["--dump-state=".len..]; + continue; + } std.debug.print("error: unknown flag '{s}'\n", .{arg}); maybe_file_path = null; break; @@ -107,6 +112,13 @@ pub fn main() !void { }; try comp.init(); defer comp.deinit(); + + defer if (dump_state) |path| { + dumpState(&comp, path) catch |err| { + std.debug.print("Failed to dump compilation state: {}", .{err}); + }; + }; + const main_file = try comp.getFile(file_path); var fail_report: Report = undefined; @@ -141,3 +153,17 @@ pub fn main() !void { std.process.cleanExit(); } + +fn dumpState(comp: *const Comp, dump_path: []const u8) !void { + const file = try std.fs.cwd().createFile(dump_path, .{}); + defer file.close(); + + var bufw = std.io.bufferedWriter(file.writer()); + var jw = std.json.writeStream(bufw.writer(), .{ + .whitespace = .indent_2, + }); + + try comp.jsonStringify(&jw); + try bufw.writer().writeAll("\n"); + try bufw.flush(); +} diff --git a/src/core/mod.zig b/src/core/mod.zig index a7c78c625208ae3f521622d5c35a1598945bc9fa..cf33cf9bef67090e3daf1e01ae16c2edf203af33 100644 --- a/src/core/mod.zig +++ b/src/core/mod.zig @@ -101,6 +101,19 @@ pub fn detectTTYFromWriter(writer: std.io.AnyWriter) std.io.tty.Config { return .no_color; } +/// This function is entirely because ZLS cannot infer `anytype` -> `std.json.WriteStream` +/// It should be removed once ZLS or the Zig language fixes this. +pub fn jsonWriterCast(json_writer: anytype) *std.json.WriteStream( + @TypeOf(json_writer.*).Stream, + switch (@TypeOf(json_writer.nesting_stack)) { + void => .assumed_correct, + std.BitStack => .checked_to_arbitrary_depth, + else => .{ .checked_to_fixed_depth = 256 }, + }, +) { + return json_writer; +} + const std = @import("std"); const assert = std.debug.assert; -- 2.54.0