diff --git a/README.md b/README.md index c36e87250efe7a2648ecb5858ea9c7cdd919f603..0aa742511ebc6dd1a609cf6988331aa4890286e0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Ix -Ix (**I**nferable E**x**pression) is a high-level, general purpose programming -language. It features a mix of Rust, Zig, and JavaScript's great points. +Ix (***I***nferable E***x***pression) is a high-level, general purpose +programming language. It features a mix of Rust, Zig, and JavaScript's great +points. For a guide on how to use Ix, see the manual included in the binary distribution. Additionally, the latest commit of the manual is hosted online at diff --git a/doc/architecture.md b/doc/architecture.md index de45d0b46a315d16d7b5ebeb7fba01e5f3a11762..9413bc82c1623f2b93c0cba8435a798989ae0d6a 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -34,13 +34,13 @@ compiler tests into these categories: runtime, testing both the semantic analysis pass as well as the code generation backend. - **Transform Error Tests** (`test/transform_error`) confirm that the transform - pass errors are correct. A passing `Teg` fails the test. + pass errors are correct. A passing `Hir` fails the test. -All compiler tests can be run with `zig build test`, or the test suite can be -filtered using flags, see `zig build test --help` +All compiler tests can be run with `./build test`, or the test suite can be +filtered using flags, see `./build test -- --help` -To run Zig tests for an associated module, use `zig build test-{name}`, or run -`zig build test-all` to run all tests in the codebase. +To run Zig tests for an associated module, use `./build test-{name}`, or run +`./build test-all` to run all tests in the codebase. ## Core @@ -48,7 +48,7 @@ The `core` module contains most of the compiler: - Tokenizing and Parsing files. (`Tokenizer.zig`, `Parse.zig`, `Ast.zig`) - Transform Pass, moving from Ast to an untyped intermediate representation - (`Transform.zig`) + (`Transform.zig`, `Hir.zig`) - Semantic Analysis, resolving all types and comptime values. (`Sema.zig`) - Intern Pool, storing and de-duplicating objects in a compilation. (`InternPool.zig`) @@ -56,7 +56,7 @@ The `core` module contains most of the compiler: Note that components like the formatter and module fetching code are not here. Keeping this module as small as possible is important for keeping compile times -low. Using `zig build compiler -- --help` instead of the fully featured binary +low. Using `./build compiler -- --help` instead of the fully featured binary will allow for faster and more focused iteration. ## Manual diff --git a/doc/language-manual.md b/doc/language-manual.md index ffca26d424414617bbe800242efbf1d5dc49b9b3..b6dde2e8da040ddafad1af0001c509e367235fbf 100644 --- a/doc/language-manual.md +++ b/doc/language-manual.md @@ -1,6 +1,6 @@ # Ix Programming Language -Ix (**I**nferable E**x**pression) is a high-level programming language for +Ix (***I***nferable E***x***pression) is a high-level programming language for developing web applications and games. Key goals of this project are: diff --git a/src/cli_compiler.zig b/src/cli_compiler.zig index a13fe48184ffa3dfd1e3aa9fb9c06940645e9de3..43c866cdc0fef3d9b7dcf390119a547541a5c157 100644 --- a/src/cli_compiler.zig +++ b/src/cli_compiler.zig @@ -81,7 +81,7 @@ pub fn main() !u8 { const main_file = try comp.getFile(file_path); var fail_report: Report = undefined; - const teg = main_file.getTeg(&comp, &fail_report) catch |err| switch (err) { + const hir = main_file.getHir(&comp, &fail_report) catch |err| switch (err) { error.TransformFail => { std.debug.print("Transform failed:\n", .{}); try fail_report.dump(.initComp(&comp, main_file), comp.out); @@ -97,7 +97,7 @@ pub fn main() !u8 { if (comp.options.dump_ir) { const stderr = std.io.getStdErr().writer(); - try teg.dumpToWriter(stderr, null); + try hir.dumpToWriter(stderr, null); } const entry_point = try comp.enqueueEntryModule(main_file); diff --git a/src/cli_transform.zig b/src/cli_transform.zig index 501353608d31b9e0e2523c4a608263a56929a8f9..b687ddf5e857e3b1d04343cab96bfe319a8facfd 100644 --- a/src/cli_transform.zig +++ b/src/cli_transform.zig @@ -66,7 +66,7 @@ pub fn main() !u8 { } var teg_report: core.Report = undefined; - var teg = core.Transform.transform(ast, alloc, &teg_report) catch |err| switch (err) { + var hir = core.Transform.transform(ast, alloc, &teg_report) catch |err| switch (err) { error.OutOfMemory => return err, error.TransformFail => { defer teg_report.deinit(alloc); @@ -81,13 +81,13 @@ pub fn main() !u8 { return 1; }, }; - defer teg.deinit(alloc); + defer hir.deinit(alloc); - // const file = try std.fs.cwd().createFile("serialized.teg", .{}); + // const file = try std.fs.cwd().createFile("serialized.hir", .{}); // defer file.close(); - // try teg.serialize(.{ .file = file }); + // try hir.serialize(.{ .file = file }); - try teg.dumpToWriter(out.writer, out.tty); + try hir.dumpToWriter(out.writer, out.tty); return 0; } diff --git a/src/core/Air.Dump.zig b/src/core/Air.Dump.zig index 03f29ece0026eeec8b67714a9531b6b2ed612b33..d95fe6e89ff03e21ffc1188369aefe606239aed4 100644 --- a/src/core/Air.Dump.zig +++ b/src/core/Air.Dump.zig @@ -1,4 +1,4 @@ -//! This implements a debug formatter for Teg +//! This implements a debug formatter for Air air: Air, ip: *const InternPool, tty: std.io.tty.Config, diff --git a/src/core/Ast.zig b/src/core/Ast.zig index 18a10acf8d28760a9e03c5cabcb4abd1dfa83dac..5fbfba34a7793845b15fbc35223e064d6455b46d 100644 --- a/src/core/Ast.zig +++ b/src/core/Ast.zig @@ -1,6 +1,6 @@ //! The Abstract Syntax Tree preserves most details of the source code, //! as it is used for formatting. Before semantic analysis, this -//! structure is normalized into `Teg`, which removes constructs like +//! structure is normalized into `Hir`, which removes constructs like //! whitespace, unneeded let bindings, and other transforms. //! //! `ast.nodes` contains nodes stored as three parts: diff --git a/src/core/Comp.zig b/src/core/Comp.zig index 118b19df45534443da4ddd7fd4b786b25d8d1e41..667560a920c7971bbd8db7b51ecdf90e4ce6041b 100644 --- a/src/core/Comp.zig +++ b/src/core/Comp.zig @@ -110,14 +110,14 @@ const Job = union(enum) { pub fn enqueueEntryModule(comp: *Comp, file: *File) !Decl.Index { var report: Report = undefined; - const teg = try file.getTeg(comp, &report); + const hir = try file.getHir(comp, &report); // TODO, move this elsewhere. we should print all errors and warnings at once - if (teg.report.warnings.len > 0) { - try teg.report.dump(.initComp(comp, file), comp.out); + if (hir.report.warnings.len > 0) { + try hir.report.dump(.initComp(comp, file), comp.out); } - const ns = try comp.indexTegModule(file, .main_module_inst); + const ns = try comp.indexHirModule(file, .main_module_inst); const main_str = try comp.intern_pool.getOrPutString(comp.gpa, "main", .no_embedded_nulls); const decl_index = comp.intern_pool.namespacePtr(ns).lookupDirect(comp, main_str) orelse { // TODO: add a helper function into Report to render a single error @@ -168,12 +168,12 @@ fn runAnalysisJob(comp: *Comp, unit: InternPool.AnalysisUnit, first_blocked_sema }; const decl = comp.intern_pool.declPtr(decl_index); const file = comp.intern_pool.namespacePtr(decl.src_namespace).file_scope; - const teg = file.teg orelse @panic("TODO: make sure teg is loaded to avoid annoying error set?"); + const hir = file.hir orelse @panic("TODO: make sure hir is loaded to avoid annoying error set?"); try sema.init( comp, file, - teg, + hir, unit, decl.src_namespace, std.heap.ArenaAllocator.init(comp.gpa), @@ -266,7 +266,7 @@ pub fn getFile(comp: *Comp, absolute_file_path: []const u8) !*File { .root_namespace = .none, .source = null, .ast = null, - .teg = null, + .hir = null, }; gop.value_ptr.* = file; return file; @@ -277,7 +277,7 @@ pub const Source = struct { stat: Stat, }; -/// A file in the compilation and it's associated data structures. Ast and Teg +/// A file in the compilation and it's associated data structures. Ast and Hir /// are not guaranteed to be loaded, even if it's `status` says `success`. pub const File = struct { /// TODO: change this to a URL @@ -287,7 +287,7 @@ pub const File = struct { source: ?Source, ast: ?Ast, - teg: ?Teg, + hir: ?Hir, path_digest: ?Cache.BinDigest, @@ -323,7 +323,7 @@ pub const File = struct { // Here we do not modify stat fields because this function is the one // used for error reporting. We need to keep the stat fields stale so that - // transformFile can know to regenerate Teg. + // transformFile can know to regenerate Hir. const loaded_source: Source = .{ .code = source, @@ -370,11 +370,11 @@ pub const File = struct { return &(file.ast.?); } - pub const GetTegError = GetAstError || Transform.Error; + pub const GetHirError = GetAstError || Transform.Error; - pub fn getTeg(file: *File, comp: *Comp, fail_report: *Report) GetTegError!Teg { - if (file.teg) |teg| { - return teg; + pub fn getHir(file: *File, comp: *Comp, fail_report: *Report) GetHirError!Hir { + if (file.hir) |hir| { + return hir; } const bin_digest = hash: { @@ -387,10 +387,10 @@ pub const File = struct { }; file.path_digest = bin_digest; - const teg = try Transform.transform((try file.getAst(comp)).*, comp.gpa, fail_report); + const hir = try Transform.transform((try file.getAst(comp)).*, comp.gpa, fail_report); - file.teg = teg; - return teg; + file.hir = hir; + return hir; } pub fn unloadSource(file: *File, gpa: Allocator) void { @@ -407,15 +407,15 @@ pub const File = struct { } } - pub fn unloadTeg(file: *File, gpa: Allocator) void { - if (file.teg) |*teg| { - teg.deinit(gpa); - file.teg = null; + pub fn unloadHir(file: *File, gpa: Allocator) void { + if (file.hir) |*hir| { + hir.deinit(gpa); + file.hir = null; } } pub fn deinit(file: *File, gpa: Allocator) void { - file.unloadTeg(gpa); + file.unloadHir(gpa); file.unloadAst(gpa); file.unloadSource(gpa); gpa.destroy(file); @@ -476,7 +476,7 @@ pub const Decl = struct { // /// This value is absolute. // src_line: u32, - /// Index of the Teg `declaration` instruction from which this `Decl` was created. + /// Index of the Hir `declaration` instruction from which this `Decl` was created. teg_decl_index: InternPool.TrackedInst.Index, /// Represents the "shallow" analysis status. For example, for decls that are functions, @@ -620,10 +620,10 @@ pub const Namespace = struct { pub const Index = InternPool.NamespaceIndex; }; -/// Index a Teg's module scope as a namespace. This prepares `Decl` objects for +/// Index a Hir's module scope as a namespace. This prepares `Decl` objects for /// every declaration, but does not begin analysis of them. -pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !Namespace.Index { - const teg = file.teg.?; +pub fn indexHirModule(comp: *Comp, file: *File, module_index: Hir.Inst.Index) !Namespace.Index { + const hir = file.hir.?; assert(file.root_namespace == .none); @@ -635,12 +635,12 @@ pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !N const ns_ptr = comp.intern_pool.namespacePtr(ns); errdefer comp.intern_pool.destroyNamespace(comp.gpa, ns); - for (teg.moduleDeclarations(module_index)) |decl_index| { - switch (teg.instTag(decl_index)) { + for (hir.moduleDeclarations(module_index)) |decl_index| { + switch (hir.instTag(decl_index)) { .declaration => { const name = try comp.intern_pool.getOrPutString( comp.gpa, - teg.declName(decl_index), + hir.declName(decl_index), .no_embedded_nulls, ); const tracked_inst = try comp.intern_pool.trackInst(comp.gpa, file, decl_index); @@ -736,7 +736,7 @@ const InternPool = core.InternPool; const Ast = core.Ast; const Air = core.Air; -const Teg = core.Teg; +const Hir = core.Hir; const Cache = core.Cache; const Sema = core.Sema; const Transform = core.Transform; diff --git a/src/core/Hir.Dump.zig b/src/core/Hir.Dump.zig new file mode 100644 index 0000000000000000000000000000000000000000..53ee3b62b2379aa76b05b38af5132b0e6af6ebe7 --- /dev/null +++ b/src/core/Hir.Dump.zig @@ -0,0 +1,522 @@ +//! This implements a debug formatter for Hir +hir: Hir, +tty: ?std.io.tty.Config = null, + +const State = struct { + arena: std.mem.Allocator, + hir: *const Hir, + indent: usize = 0, + tty: std.io.tty.Config, + visited: std.AutoHashMapUnmanaged(Hir.Inst.Index, void) = .{}, + + fn newline(state: State, writer: anytype) !void { + try writer.writeByte('\n'); + if (state.indent > 0) + try writer.writeByteNTimes(' ', state.indent * 2); + } + + fn dumpInst(s: *State, writer: anytype, inst: Hir.Inst.Index) @TypeOf(writer).Error!void { + const hir = s.hir; + const tty = s.tty; + + try writer.print("%{} = ", .{@intFromEnum(inst)}); + + const tag = s.hir.instTag(inst); + + try tty.setColor(writer, .bright_black); + const node_offset = hir.instNodeOffset(inst); + try writer.print("{c}{d} ", .{ + @as(u8, if (node_offset >= 0) '+' else '-'), + @abs(node_offset), + }); + try tty.setColor(writer, .reset); + + try tty.setColor(writer, .cyan); + try writer.print("{s}", .{@tagName(tag)}); + try tty.setColor(writer, .reset); + + if ((try s.visited.fetchPut(s.arena, inst, {})) != null) { + try tty.setColor(writer, .red); + try writer.writeAll(" [error: recursive tree]"); + try tty.setColor(writer, .reset); + return; + } + + switch (tag) { + .module => { + try s.dumpInstList(writer, hir.moduleDeclarations(inst), '{', '}'); + }, + .declaration, .variable_declaration, .test_declaration => { + const decl = hir.fullDecl(inst); + + try writer.writeAll(" "); + const name = decl.name.slice(hir.string_bytes); + if (name.len == 0) { + try tty.setColor(writer, .bold); + try writer.writeAll("[no name]"); + } else { + try tty.setColor(writer, .magenta); + try writer.writeAll(name); + } + try tty.setColor(writer, .reset); + try writer.writeAll(" ("); + try writer.print("{d}", .{decl.instruction_count}); + try writer.writeAll(")"); + try tty.setColor(writer, .bright_black); + try writer.writeAll(" ["); + try writer.writeAll(&std.fmt.bytesToHex(decl.hash, .lower)); + try writer.writeAll("]"); + try tty.setColor(writer, .reset); + try writer.writeAll(" ("); + s.indent += 1; + try s.newline(writer); + try s.dumpRef(writer, decl.body); + s.indent -= 1; + try s.newline(writer); + try writer.writeAll(")"); + }, + .func => { + const func: Hir.Func = hir.instPayload(.func, inst); + try writer.writeAll(" {"); + s.indent += 1; + try s.newline(writer); + try writer.writeAll("params: "); + try s.dumpOptionalRefList(writer, func.params.slice(hir), '[', ']'); + try s.newline(writer); + try writer.writeAll("return_type: "); + if (func.return_type.unwrap()) |ret| + try s.dumpRef(writer, ret) + else { + try tty.setColor(writer, .bright_black); + try writer.writeAll(""); + try tty.setColor(writer, .reset); + } + try s.newline(writer); + try writer.writeAll("captures: "); + try s.dumpInstList2(writer, func.captures.slice(hir), '[', ']'); + try s.newline(writer); + try writer.writeAll("body: "); + try s.dumpInst(writer, func.body); + s.indent -= 1; + try s.newline(writer); + try writer.writeAll("}"); + }, + .param => { + const data = hir.instData(.param, inst); + try writer.writeAll("("); + if (data.data == std.math.maxInt(u32)) { + try tty.setColor(writer, .bright_black); + try writer.writeAll(""); + } else { + try tty.setColor(writer, .yellow); + try writer.print("{d}", .{data.data}); + } + try tty.setColor(writer, .reset); + try writer.writeAll(")"); + }, + inline .block, .@"comptime" => |t| { + const block = hir.instData(t, inst); + const index_list = block.extra_index.get(hir); + try s.dumpInstList(writer, index_list.slice(hir), '[', ']'); + }, + .number => { + const number = hir.instData(.number, inst); + const float = number.extra_index.get(hir).decode(); + try tty.setColor(writer, .yellow); + try writer.print(" {d}", .{float}); + try tty.setColor(writer, .reset); + }, + inline .field, + .index, + .add, + .sub, + .mul, + .div, + .rem, + .pow, + .@"and", + .@"or", + .bit_and, + .bit_or, + .bit_xor, + .shl, + .shr, + .eq, + .ne, + .lt, + .le, + .gt, + .ge, + => |comptime_tag| { + const data = hir.instData(comptime_tag, inst).extra_index.get(hir); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.lhs); + try writer.writeAll(", "); + try s.dumpRefMarker(writer, data.rhs); + try writer.writeAll(")"); + }, + .coercion => { + const data = hir.instData(.coercion, inst).extra_index.get(hir); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.type); + try writer.writeAll(", "); + try s.dumpRefMarker(writer, data.operand); + try writer.writeAll(")"); + }, + .@"break" => { + const data: Hir.Break = hir.instData(.@"break", inst).extra_index.get(hir); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.block.toRef()); + try writer.writeAll(", "); + try s.dumpRefMarker(writer, data.operand); + try writer.writeAll(")"); + }, + .store => { + const data: Hir.Store = hir.instData(.store, inst).extra_index.get(hir); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.memory.toRef()); + try writer.writeAll(", "); + try s.dumpRefMarker(writer, data.operand); + try writer.writeAll(")"); + }, + .store_field => { + const data: Hir.StoreField = hir.instData(.store_field, inst).extra_index.get(hir); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.base); + try writer.writeAll(", "); + try s.dumpRefMarker(writer, data.field); + try writer.writeAll(", "); + try s.dumpRefMarker(writer, data.operand); + try writer.writeAll(")"); + }, + .store_decl => { + const data: Hir.StoreVarDecl = hir.instData(.store_decl, inst).extra_index.get(hir); + try writer.writeAll("("); + try tty.setColor(writer, .magenta); + try writer.writeAll(data.var_decl.slice(hir.string_bytes)); + try tty.setColor(writer, .reset); + try writer.writeAll(", "); + try s.dumpRefMarker(writer, data.operand); + try writer.writeAll(")"); + }, + .string => { + const data: Hir.SlicedString = hir.instData(.string, inst).extra_index.get(hir); + try tty.setColor(writer, .green); + try writer.writeAll(" \""); + try writer.writeAll(data.slice(hir.string_bytes)); + try writer.writeAll("\""); + try tty.setColor(writer, .reset); + }, + inline .memory, + .discard, + .ensure_void, + .negate, + .bool_not, + .bit_not, + .@"extern", + .import, + .fetch, + .compile_error, + .type_of, + .array_type, + .assert, + => |comptime_tag| { + const data = hir.instData(comptime_tag, inst); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.operand); + try writer.writeAll(")"); + }, + .call => { + const call: Hir.Call = hir.instData(.call, inst).extra_index.get(hir); + try writer.writeAll("("); + try s.dumpRefMarker(writer, call.base); + try writer.writeAll(", "); + try s.dumpRefMarkerList(writer, call.args.slice(hir), '[', ']'); + try writer.writeAll(")"); + }, + .js_new => { + const call: Hir.JsNew = hir.instData(.js_new, inst).extra_index.get(hir); + try writer.writeAll("("); + try s.dumpRefMarker(writer, call.base); + try writer.writeAll(", "); + try s.dumpRefMarkerList(writer, call.args.slice(hir), '[', ']'); + try writer.writeAll(")"); + }, + .load_decl => { + const call: Hir.StrNode = hir.instData(.load_decl, inst); + + try tty.setColor(writer, .red); + try writer.print(" {s}", .{call.string.slice(hir.string_bytes)}); + try tty.setColor(writer, .reset); + }, + .@"if" => { + const data: Hir.If = hir.instPayload(.@"if", inst); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.cond); + try writer.writeAll(") {"); + s.indent += 1; + try s.newline(writer); + try writer.writeAll("yes: "); + try s.dumpRef(writer, data.yes); + try s.newline(writer); + try writer.writeAll("no: "); + try s.dumpRef(writer, data.no); + s.indent -= 1; + try s.newline(writer); + try writer.writeAll("}"); + }, + .struct_decl => { + const data: Hir.StructDecl = hir.instPayload(.struct_decl, inst); + try writer.writeAll(" {"); + s.indent += 1; + try s.newline(writer); + for (data.keys(hir), data.types(hir), 0..) |key, value, i| { + if (i != 0) + try s.newline(writer); + try writer.print("{s}: ", .{key.slice(hir.string_bytes)}); + try s.dumpRefMarker(writer, value); + } + s.indent -= 1; + try s.newline(writer); + try writer.writeAll("}"); + }, + .struct_init => { + const data: Hir.StructInit = hir.instPayload(.struct_init, inst); + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.base_type); + try writer.writeAll(") {"); + s.indent += 1; + try s.newline(writer); + for (data.keys(hir), data.inits(hir), 0..) |key, value, i| { + if (i != 0) + try s.newline(writer); + try writer.print("{s}: ", .{key.slice(hir.string_bytes)}); + try s.dumpRefMarker(writer, value); + } + s.indent -= 1; + try s.newline(writer); + try writer.writeAll("}"); + }, + .array_init => { + const data: Hir.ArrayInit = hir.instPayload(.array_init, inst); + + if (data.type_hint.unwrap()) |type_hint| { + try writer.writeAll("("); + try s.dumpRef(writer, type_hint); + try writer.writeAll(", "); + } + + try writer.writeAll("["); + + s.indent += 1; + + for (data.list.slice(hir)) |item| { + try s.newline(writer); + try s.dumpRefMarker(writer, item); + try writer.writeAll(","); + } + + s.indent -= 1; + try s.newline(writer); + + try writer.writeAll("]"); + + if (data.type_hint != .none) + try writer.writeAll(")"); + }, + .tuple_type => { + const data: Hir.TupleType = hir.instPayload(.tuple_type, inst); + + try writer.writeAll("("); + try s.dumpRefMarkerList(writer, data.items.slice(hir), '[', ']'); + try writer.writeAll(")"); + }, + .tuple_init => { + const data: Hir.TupleInit = hir.instPayload(.tuple_init, inst); + + try writer.writeAll("("); + if (data.type_hint.unwrap()) |type_hint| { + try s.dumpRef(writer, type_hint); + try writer.writeAll(", "); + } + try s.dumpRefMarkerList(writer, data.items.slice(hir), '[', ']'); + try writer.writeAll(")"); + }, + .result_type_argument => { + const data: Hir.ResultTypeArgument = hir.instData(.result_type_argument, inst); + + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.call_base); + try writer.print(", {d}", .{data.argument}); + try writer.writeAll(")"); + }, + .result_type_struct_field => { + const data: Hir.ResultTypeStructField = hir.instData(.result_type_struct_field, inst); + + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.type); + try writer.print(", {s}", .{data.field.slice(s.hir.string_bytes)}); + try writer.writeAll(")"); + }, + .result_type_tuple_field => { + const data: Hir.ResultTypeTupleField = hir.instData(.result_type_tuple_field, inst); + + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.type); + try writer.print(", {d}", .{data.item}); + try writer.writeAll(")"); + }, + .result_type_array_value => { + const data: Hir.ResultTypeArrayValue = hir.instData(.result_type_array_value, inst); + + try writer.writeAll("("); + try s.dumpRefMarker(writer, data.type); + try writer.writeAll(")"); + }, + } + } + + fn dumpRef(s: *State, writer: anytype, ref: Hir.Inst.Ref) !void { + switch (ref.decode()) { + .index => |index| try s.dumpInst(writer, index), + .value => { + try s.tty.setColor(writer, .blue); + try writer.print("%{s}", .{@tagName(ref)}); + try s.tty.setColor(writer, .reset); + }, + } + } + + fn dumpRefMarker(s: *State, writer: anytype, ref: Hir.Inst.Ref) !void { + switch (ref.decode()) { + .index => |index| { + try s.tty.setColor(writer, .blue); + try writer.print("%{d}", .{@intFromEnum(index)}); + try s.tty.setColor(writer, .reset); + }, + .value => { + try s.tty.setColor(writer, .blue); + try writer.print("%{s}", .{@tagName(ref)}); + try s.tty.setColor(writer, .reset); + }, + } + } + + fn dumpInstList(state: *State, writer: anytype, list: []const Hir.Inst.Index, l: u8, r: u8) !void { + try writer.print(" {c}", .{l}); + if (list.len > 0) { + state.indent += 1; + + for (list) |inst| { + try state.newline(writer); + try state.dumpInst(writer, inst); + } + + state.indent -= 1; + try state.newline(writer); + } + try writer.print("{c}", .{r}); + } + + fn dumpInstList2(state: *State, writer: anytype, list: []const Hir.Inst.Index, l: u8, r: u8) !void { + try writer.print(" {c}", .{l}); + if (list.len > 0) { + state.indent += 1; + + for (list) |inst| { + try state.newline(writer); + try state.dumpRefMarker(writer, inst.toRef()); + } + + state.indent -= 1; + try state.newline(writer); + } + try writer.print("{c}", .{r}); + } + + fn dumpRefList(state: *State, writer: anytype, list: []const Hir.Inst.Ref, l: u8, r: u8) !void { + try writer.print(" {c}", .{l}); + if (list.len > 0) { + state.indent += 1; + + for (list) |ref| { + try state.newline(writer); + try state.dumpRef(writer, ref); + } + + state.indent -= 1; + + try state.newline(writer); + } + try writer.print("{c}", .{r}); + } + + fn dumpOptionalRefList(state: *State, writer: anytype, list: []const Hir.Inst.Ref.Optional, l: u8, r: u8) !void { + try writer.print(" {c}", .{l}); + if (list.len > 0) { + state.indent += 1; + + for (list) |optional_ref| { + try state.newline(writer); + if (optional_ref.unwrap()) |ref| + try state.dumpRef(writer, ref) + else { + try state.tty.setColor(writer, .bright_black); + try writer.writeAll(""); + try state.tty.setColor(writer, .reset); + } + } + + state.indent -= 1; + + try state.newline(writer); + } + try writer.print("{c}", .{r}); + } + + fn dumpRefMarkerList(state: *State, writer: anytype, list: []const Hir.Inst.Ref, l: u8, r: u8) !void { + try writer.print("{c}", .{l}); + if (list.len > 0) { + state.indent += 1; + + for (list) |ref| { + try state.newline(writer); + try state.dumpRefMarker(writer, ref); + } + + state.indent -= 1; + + try state.newline(writer); + } + try writer.print("{c}", .{r}); + } +}; + +pub fn format( + dump: @This(), + comptime unused_fmt: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, +) !void { + comptime assert(unused_fmt.len == 0); + + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + + const hir = dump.hir; + var state = State{ + .hir = &hir, + .tty = dump.tty orelse detectTTYFromWriter(writer), + .arena = arena.allocator(), + }; + try state.dumpInst(writer, .main_module_inst); + try writer.writeAll("\n"); + assert(state.indent == 0); +} + +const std = @import("std"); +const assert = std.debug.assert; + +const core = @import("mod.zig"); +const Hir = core.Hir; +const detectTTYFromWriter = core.detectTTYFromWriter; diff --git a/src/core/Hir.zig b/src/core/Hir.zig new file mode 100644 index 0000000000000000000000000000000000000000..9225070af8bc6b57955d667d7f732c9cdcc68d27 --- /dev/null +++ b/src/core/Hir.zig @@ -0,0 +1,1017 @@ +//! High-level Intermediate Representation is an instruction list format that +//! describes a source file in a manner effective for Semantic Analysis to +//! process. The root instruction of every Hir is a `.module` that contains the +//! root declarations. Each declaration's value contains a list of instructions. +//! In contrast to the nested nature of syntax trees, Hir represents nested +//! expressions as flat instruction sequences. +//! +//! Hir for a given file has no dependencies on it's imports, meaning it can be +//! computed in parallel and cached when not changed. +//! +//! A helper program exists to visualize Hir: './build transform -- ./file.ix' +//! Alternatively, `ix build` has the `--dump-ir` option. + +insts: Inst.List.Slice, +string_bytes: []const u8, +extra: ExtraStore = .{}, +report: Report, + +pub const ExtraStore = dod.ExtraStore(.hir, .@"const"); +pub const EncodedData = dod.EncodedData(Tag, u64); +pub const ExtraIndex = ExtraStore.Index; +pub const Dump = @import("Hir.Dump.zig"); + +pub const Tag = enum(u8) { + /// Either the top level module or a `module` keyword. Contains a list of `.declarations` + /// Every analyzed module gets a `Comp.Namespace` + module, + /// Smallest unit of re-compilation. Every analyzed declaration gets a `Comp.Decl` + declaration, + /// Top-level memory location + variable_declaration, + /// Test declaration + test_declaration, + /// Retrieves a declaration value via it's name. Since declarations are + /// analyzed in isolation of each other, all references to instructions + /// outside of the current declaration must be in the form of this. + load_decl, + /// Blocks are a list of instructions. The block will end with a `.break`, + /// which will assign the block a return value. + block, + /// Ends a block (current or any parent) with a value. + /// `return` expressions are lowered to this. + @"break", + /// Creates a memory cell in the current scope with a starting value. + memory, + /// Re-assign a memory cell with a new ref. + store, + /// Re-assign a global variable decl with a new ref. + store_decl, + /// Re-assign a field with a new ref + store_field, + /// Performs a type coercion, such as in variable declarations. + /// The resulting Air Ref will be guaranteed to be the coerced type. + coercion, + /// Load a parameter + /// if data value is maxInt(u32), the function return type is used + param, + /// Asserts the operand is not void. Used for discard statements. + discard, + /// Asserts the operand is `void`. Used for undiscarded expressions. + ensure_void, + + /// Branch takes a boolean condition and takes either a yes or no path. + @"if", + /// Given a block instruction + @"comptime", + + /// A function expression, with or without captures + func, + + // The following instructions are used to implement `Transform.ResultType` + result_type_argument, + result_type_struct_field, + result_type_tuple_field, + result_type_array_value, + + /// Numeric constant. + number, + /// String literal constant. + string, + + /// Constructs a unique struct type. + struct_decl, + /// Constructs an array type + array_type, + + struct_init, + array_init, + + tuple_init, + tuple_type, + + add, + sub, + mul, + div, + rem, + pow, + @"and", + @"or", + bit_and, + bit_or, + bit_xor, + shl, + shr, + eq, + ne, + lt, + le, + gt, + ge, + + negate, + bool_not, + bit_not, + assert, + field, + index, + call, + type_of, + /// Implements the `@extern` builtin + @"extern", + + /// Implements the `@jsNew` builtin + js_new, + /// Implements the `@import` builtin + import, + /// Implements the `@fetch` builtin + fetch, + /// Implements the `@compileError` builtin + compile_error, + + pub fn Data(comptime tag: Tag) type { + return switch (tag) { + .module, .block, .@"comptime" => PlNode(IndexList), + + .declaration, + .variable_declaration, + .test_declaration, + => PlNode(Decl), + + .coercion => PlNode(Coercion), + .@"break" => PlNode(Break), + .number => PlNode(Float), + .string => PlNode(SlicedString), + .call => PlNode(Call), + .@"if" => PlNode(If), + .store => PlNode(Store), + .store_decl => PlNode(StoreVarDecl), + .store_field => PlNode(StoreField), + .js_new => PlNode(JsNew), + .struct_decl => PlNode(StructDecl), + .struct_init => PlNode(StructInit), + .array_init => PlNode(ArrayInit), + .tuple_type => PlNode(TupleType), + .tuple_init => PlNode(TupleInit), + .func => PlNode(Func), + + .load_decl => StrNode, + .param => DataNode, + + .result_type_argument => ResultTypeArgument, + .result_type_struct_field => ResultTypeStructField, + .result_type_tuple_field => ResultTypeTupleField, + .result_type_array_value => ResultTypeArrayValue, + + // One operand instructions + .memory, + .discard, + .ensure_void, + .negate, + .bool_not, + .bit_not, + .@"extern", + .import, + .fetch, + .compile_error, + .type_of, + .array_type, + .assert, + => RefNode, + + // Two operand instructions + .field, + .index, + .add, + .sub, + .mul, + .div, + .rem, + .pow, + .@"and", + .@"or", + .bit_and, + .bit_or, + .bit_xor, + .shl, + .shr, + .eq, + .ne, + .lt, + .le, + .gt, + .ge, + => PlNode(Bin), + }; + } + + pub fn airTag(comptime tag: Tag) Air.Inst.Tag { + return @field(Air.Inst.Tag, @tagName(tag)); + } +}; + +pub fn PlNode(comptime Payload: type) type { + return struct { + pub const T = Payload; + + src_node: NodeOffset, + extra_index: ExtraIndex(Payload), + }; +} + +pub const RefNode = struct { + src_node: NodeOffset, + operand: Inst.Ref, +}; + +pub const StrNode = struct { + src_node: NodeOffset, + string: NullTerminatedString, +}; + +pub const DataNode = struct { + src_node: NodeOffset, + data: u32, +}; + +pub const Inst = struct { + pub const List = std.MultiArrayList(Inst); + + tag: Tag, + data: EncodedData, + + /// The position of a Hir instruction within the `Hir` instructions array. + pub const Index = enum(u32) { + /// Hir is structured so that the outermost "main" struct of any file + /// is always at index 0. + main_module_inst = 0, + _, + + pub const static_len = @intFromEnum(Ref.empty_tuple) + 1; + + pub fn toRef(i: Index) Inst.Ref { + return @enumFromInt(static_len + @intFromEnum(i)); + } + + pub fn toOptional(i: Index) Optional { + return @enumFromInt(@intFromEnum(i)); + } + + pub fn op(i: Index, operand: Report.Operand) InstOp { + return .{ .index = i, .op = operand }; + } + + pub const Optional = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn unwrap(oi: Optional) ?Index { + return if (oi == .none) null else @enumFromInt(@intFromEnum(oi)); + } + }; + }; + + /// A reference to an instruction, or to a static InternPool value. + /// + /// If the integer tag value is < InternPool.static_len, then it + /// corresponds to a Value. Otherwise, it is an instruction index. + pub const Ref = enum(u32) { + opaque_type, + bool_type, + int32_type, + float_type, + string_type, + never_type, + type_type, + numeric_literal_type, + numeric_literal_array_type, + never_array_type, + empty_tuple_type, + + void, + bool_true, + bool_false, + int32_zero, + float_zero, + nan, + empty_string, + empty_array, + empty_tuple, + _, + + pub const Decoded = union(enum) { + value: InternPool.Value, + index: Hir.Inst.Index, + }; + + pub fn decode(ref: Ref) Decoded { + const ref_int = @intFromEnum(ref); + return if (ref_int >= Index.static_len) + .{ .index = @enumFromInt(ref_int - Index.static_len) } + else + .{ .value = @enumFromInt(ref_int) }; + } + + pub fn index(ref: Ref) ?Index { + const ref_int = @intFromEnum(ref); + if (ref_int >= Index.static_len) { + return @enumFromInt(ref_int - Index.static_len); + } else { + return null; + } + } + + pub fn value(inst: Ref) ?InternPool.Value { + const ref_int = @intFromEnum(inst); + + // TODO: is this > or >= + if (ref_int > Index.static_len) + return null; + + return @enumFromInt(ref_int); + } + + pub fn toOptional(ref: Ref) Optional { + return @enumFromInt(@intFromEnum(ref)); + } + + pub const Optional = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn unwrap(opt: Optional) ?Ref { + return if (opt != .none) @enumFromInt(@intFromEnum(opt)) else null; + } + }; + }; +}; + +pub const Bin = struct { + lhs: Inst.Ref, + rhs: Inst.Ref, +}; + +pub const Store = struct { + memory: Inst.Index, + operand: Inst.Ref, +}; + +pub const StoreVarDecl = struct { + var_decl: NullTerminatedString, + operand: Inst.Ref, +}; + +pub const StoreField = struct { + base: Inst.Ref, + field: Inst.Ref, + operand: Inst.Ref, +}; + +pub const Coercion = struct { + type: Inst.Ref, + operand: Inst.Ref, +}; + +pub const Break = struct { + block: Inst.Index, + operand: Inst.Ref, +}; + +pub const Call = struct { + base: Inst.Ref, + args: RefList, +}; + +pub const If = struct { + cond: Inst.Ref, + yes: Inst.Ref, + no: Inst.Ref, +}; + +pub const JsNew = struct { + base: Inst.Ref, + args: RefList, +}; + +/// Index into extra which contains `field_len * 2` u32s. The first half +/// are the keys (NullTerminatedString) and the second half are field types +/// (Ref). This basically is like a specialized MultiArrayList. +pub const StructDecl = struct { + payload_index: u32, + field_len: u32, + + pub fn keys(decl: StructDecl, hir: *const Hir) []const NullTerminatedString { + return @ptrCast( + hir.extra.list.items[decl.payload_index..][0..decl.field_len], + ); + } + + pub fn types(decl: StructDecl, hir: *const Hir) []const Inst.Ref { + return @ptrCast( + hir.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len], + ); + } +}; + +/// This memory is laid out with the same strategy described in StructDecl +pub const StructInit = struct { + base_type: Inst.Ref, + payload_index: u32, + field_len: u32, + + pub fn keys(decl: StructInit, hir: *const Hir) []const NullTerminatedString { + return @ptrCast( + hir.extra.list.items[decl.payload_index..][0..decl.field_len], + ); + } + + pub fn inits(decl: StructInit, hir: *const Hir) []const Inst.Ref { + return @ptrCast( + hir.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len], + ); + } +}; + +pub const ArrayInit = struct { + /// Optionally provide a result type for more reliable type coercion. + /// If specified, this must be an array type. Otherwise, the first + /// element's type is used. + type_hint: Inst.Ref.Optional, + /// The items in the array + list: RefList, +}; + +pub const TupleType = struct { + /// The items in the tuple + items: RefList, +}; + +pub const TupleInit = struct { + /// Optionally provide a result type for more reliable type coercion. + /// If specified, this must be an array type. Otherwise, the first + /// element's type is used. + type_hint: Inst.Ref.Optional, + /// The items in the tuple + items: RefList, +}; + +pub const Decl = struct { + name: NullTerminatedString, + hash: Cache.BinDigest, + instruction_count: u32, + body: Inst.Ref, +}; + +pub const Func = struct { + params: OptionalRefList, + return_type: Inst.Ref.Optional, + body: Inst.Index, + captures: IndexList, + instruction_count: u32, +}; + +pub const ResultTypeArgument = struct { + /// The VALUE that is going to be called. + call_base: Inst.Ref, + /// Which argument + argument: u32, +}; + +pub const ResultTypeStructField = struct { + /// The type of the struct itself, + type: Inst.Ref, + /// Which field + field: NullTerminatedString, +}; + +pub const ResultTypeTupleField = struct { + /// The type of the tuple itself, + type: Inst.Ref, + /// Which item + item: u32, +}; + +pub const ResultTypeArrayValue = struct { + /// The type of the array itself, + type: Inst.Ref, + unused: u32 = 0, +}; + +// TODO: make this an enum +const NodeOffset = i32; + +// TODO: remove InstOp. LazyNode should not have a decl +pub const InstOp = struct { + index: Inst.Index, + op: Report.Operand, + + pub fn lazyNode(inst: InstOp, decl: Comp.Decl.Index) LazyNode { + return .{ + .inst = inst.index, + .operand = inst.op, + .decl = decl, + }; + } +}; + +pub const NullTerminatedString = enum(u32) { + empty = 0, + _, + + pub fn slice(str: NullTerminatedString, string_bytes: []const u8) [:0]const u8 { + if (str == .empty) return ""; + const sub = string_bytes[@intFromEnum(str)..]; + const i = mem.indexOfScalar(u8, sub, 0) orelse unreachable; + + return sub[0..i :0]; + } +}; + +pub const SlicedString = struct { + start: u32, + len: u32, + + pub fn slice(str: SlicedString, string_bytes: []const u8) []const u8 { + return string_bytes[str.start..][0..str.len]; + } +}; + +// TODO: Make the next three structs using a generic comptime thing + +pub const RefList = struct { + start: u32, + len: u32, + + pub fn slice(self: RefList, hir: *const Hir) []const Inst.Ref { + return @ptrCast(hir.extra.list.items[self.start..][0..self.len]); + } +}; + +pub const OptionalRefList = struct { + start: u32, + len: u32, + + pub fn slice(self: OptionalRefList, hir: *const Hir) []const Inst.Ref.Optional { + return @ptrCast(hir.extra.list.items[self.start..][0..self.len]); + } +}; + +pub const IndexList = struct { + start: u32, + len: u32, + + pub fn slice(self: IndexList, hir: *const Hir) []const Inst.Index { + return @ptrCast(hir.extra.list.items[self.start..][0..self.len]); + } +}; + +pub const NullTermStringList = struct { + start: u32, + len: u32, + + pub fn slice(self: IndexList, hir: *const Hir) []const NullTerminatedString { + return @ptrCast(hir.extra.list.items[self.start..][0..self.len]); + } +}; + +pub fn deinit(hir: *Hir, gpa: std.mem.Allocator) void { + gpa.free(hir.string_bytes); + hir.extra.deinit(gpa); + hir.insts.deinit(gpa); + hir.report.deinit(gpa); + hir.* = undefined; +} + +pub fn instTag(hir: *const Hir, index: Inst.Index) Hir.Tag { + return hir.insts.items(.tag)[@intFromEnum(index)]; +} + +pub fn instEncodedData(hir: *const Hir, index: Inst.Index) EncodedData { + return hir.insts.items(.data)[@intFromEnum(index)]; +} + +pub fn instData(hir: *const Hir, comptime tag: Hir.Tag, index: Inst.Index) tag.Data() { + return hir.instEncodedData(index).decode(tag, hir.instTag(index)); +} + +pub fn instDataUnsafe(hir: *const Hir, comptime T: type, index: Inst.Index) T { + return hir.instEncodedData(index).decodeUnsafe(T, hir.instTag(index)); +} + +pub fn instPayload(hir: *const Hir, comptime tag: Hir.Tag, index: Inst.Index) tag.Data().T { + return hir.instData(tag, index).extra_index.get(hir); +} + +pub fn instNodeOffset(hir: *const Hir, index: Inst.Index) i32 { + return switch (hir.instTag(index)) { + // these nodes never get errors, but Hir.Dump will print these regardless + // TODO: + .declaration, + .result_type_argument, + .result_type_struct_field, + .result_type_tuple_field, + .result_type_array_value, + => 0, + + inline else => |tag| hir.instData(tag, index).src_node, + }; +} + +pub fn blockInstructions(hir: *const Hir, index: Inst.Index) []const Inst.Index { + return hir.instData(.block, index).extra_index.get(hir).slice(hir); +} + +pub fn fullFuncDecl(hir: *const Hir, index: Inst.Index) struct { Hir.Decl, Hir.DeclFn } { + const decl, var trail = hir.instData(.declaration, index).extra_index.getTrail(hir); + assert(decl.kind == .function); + const decl_fn = trail.read(&hir.extra, Hir.DeclFn); + return .{ decl, decl_fn }; +} + +pub fn fullDecl(hir: *const Hir, index: Inst.Index) Decl { + const decl = hir.instEncodedData(index).decodeUnsafe(PlNode(Decl), hir.instTag(index)).extra_index.get(hir); + return .{ + .name = decl.name, + .hash = decl.hash, + .instruction_count = decl.instruction_count, + .body = decl.body, + }; +} + +pub fn declData(hir: *const Hir, index: Inst.Index) Hir.Decl { + return hir.instDataUnsafe(PlNode(Decl), index).extra_index.get(hir); +} + +pub fn moduleDeclarations(hir: *const Hir, module_index: Inst.Index) []const Inst.Index { + const module = hir.instData(.module, module_index); + const module_data: IndexList = module.extra_index.get(hir); + const slice = module_data.slice(hir); + if (core.runtime_safety) { + for (slice) |item| { + switch (hir.instTag(item)) { + .declaration, .variable_declaration, .test_declaration => {}, + else => { + std.debug.panic("\ngot unknown tag {s}\n", .{@tagName(hir.instTag(item))}); + }, + } + } + } + return slice; +} + +pub fn rootFunction(hir: *const Hir) Inst.Index { + const decls = hir.moduleDeclarations(.main_module_inst); + const root_fn = decls[decls.len - 1]; + assert(hir.declName(root_fn).len == 0); + return root_fn; +} + +pub fn declName(hir: *const Hir, decl_index: Inst.Index) []const u8 { + const decl = hir.instDataUnsafe(PlNode(Decl), decl_index); + const decl_data: Decl = decl.extra_index.get(hir); + return decl_data.name.slice(hir.string_bytes); +} + +pub fn dumpToWriter(hir: Hir, writer: anytype, tty: ?std.io.tty.Config) !void { + try writer.print("{}", .{Dump{ .hir = hir, .tty = tty }}); +} + +/// Used for generating decl hashes +fn isolatedRefValue(decl_index: Inst.Index, ref_to_hash: Inst.Ref) i32 { + return switch (ref_to_hash.decode()) { + .value => |value| -@as(i32, @intCast(@intFromEnum(value))), + .index => |index| @intCast(@intFromEnum(index) - @intFromEnum(decl_index)), + }; +} + +/// Used for generating decl hashes +fn isolatedIndexValue(decl_index: Inst.Index, index_to_hash: Inst.Index) u32 { + return @intFromEnum(index_to_hash) - @intFromEnum(decl_index); +} + +pub fn writeToHasher( + hir: *const Hir, + hasher: *HashHelper, + tag_list: []Tag, + data_list: []EncodedData, + decl_index: Inst.Index, +) void { + // Add all of the tags + hasher.addBytes(std.mem.sliceAsBytes(tag_list)); + + // Add all of the data + for (tag_list, data_list) |tag, data| { + + // This should roughly line up with Hir.Tag.Data + switch (tag) { + // PlNode(IndexList), + .module, + .block, + .@"comptime", + => { + const pl_node = data.decodeUnsafe(PlNode(IndexList), tag); + hasher.add(pl_node.src_node); + writeIndexListToHasher(hasher, pl_node.extra_index.get(hir).slice(hir), decl_index); + }, + + // PlNode + inline .declaration, + .variable_declaration, + .test_declaration, + .coercion, + .@"break", + .number, + .string, + .call, + .@"if", + .store, + .js_new, + .struct_decl, + .struct_init, + .array_init, + .tuple_init, + .tuple_type, + .func, + .store_decl, + .store_field, + => |pl_node_tag| { + const pl_node = data.decode(pl_node_tag, tag); + hasher.add(pl_node.src_node); + hir.writePlNodeInnerToHasher(hasher, pl_node_tag.Data().T, pl_node.extra_index.get(hir), decl_index); + }, + // StrNode + .load_decl => { + const str_node = data.decodeUnsafe(StrNode, tag); + hasher.add(str_node.src_node); + hasher.addBytes(str_node.string.slice(hir.string_bytes)); + }, + // DataNode + .param => { + const data_node = data.decodeUnsafe(DataNode, tag); + hasher.add(data_node.src_node); + hasher.add(data_node.data); + }, + // RefNode + .memory, + .discard, + .ensure_void, + .negate, + .bool_not, + .bit_not, + .@"extern", + .import, + .fetch, + .compile_error, + .type_of, + .array_type, + .assert, + => { + const ref_node = data.decodeUnsafe(RefNode, tag); + hasher.add(ref_node.src_node); + hasher.add(isolatedRefValue(decl_index, ref_node.operand)); + }, + // PlNode(Bin) + .field, + .index, + .add, + .sub, + .mul, + .div, + .rem, + .pow, + .@"and", + .@"or", + .bit_and, + .bit_or, + .bit_xor, + .shl, + .shr, + .eq, + .ne, + .lt, + .le, + .gt, + .ge, + => { + const pl_node = data.decodeUnsafe(PlNode(Bin), tag); + const bin = pl_node.extra_index.get(hir); + hasher.add(isolatedRefValue(decl_index, bin.lhs)); + hasher.add(isolatedRefValue(decl_index, bin.rhs)); + }, + // Result Types + .result_type_argument => { + const arg = data.decodeUnsafe(ResultTypeArgument, tag); + hasher.add(isolatedRefValue(decl_index, arg.call_base)); + hasher.add(arg.argument); + }, + .result_type_struct_field => { + const struct_field = data.decodeUnsafe(ResultTypeStructField, tag); + hasher.add(isolatedRefValue(decl_index, struct_field.type)); + hasher.addBytes(struct_field.field.slice(hir.string_bytes)); + }, + .result_type_tuple_field => { + const tuple_field = data.decodeUnsafe(ResultTypeTupleField, tag); + hasher.add(isolatedRefValue(decl_index, tuple_field.type)); + hasher.add(tuple_field.item); + }, + .result_type_array_value => { + const array_value = data.decodeUnsafe(ResultTypeArrayValue, tag); + hasher.add(isolatedRefValue(decl_index, array_value.type)); + }, + } + } +} + +fn writeIndexListToHasher( + hasher: *HashHelper, + index_list: []const Inst.Index, + decl_index: Inst.Index, +) void { + hasher.add(index_list.len); + for (index_list) |idx| { + hasher.add(isolatedIndexValue(decl_index, idx)); + } +} + +fn writeRefListToHasher( + hasher: *HashHelper, + decl_index: Inst.Index, + index_list: []const Inst.Ref, +) void { + hasher.add(index_list.len); + for (index_list) |idx| { + hasher.add(isolatedRefValue(decl_index, idx)); + } +} + +fn writePlNodeInnerToHasher( + hir: *const Hir, + hasher: *HashHelper, + comptime T: type, + data: T, + decl_index: Inst.Index, +) void { + switch (T) { + Decl => { + hasher.addBytes(&data.hash); + hasher.addBytes(data.name.slice(hir.string_bytes)); + }, + Float => { + const as_bytes: [2]u32 = .{ + data.low, + data.high, + }; + hasher.add(@as(u64, @bitCast(as_bytes))); + }, + SlicedString => { + hasher.addBytes(data.slice(hir.string_bytes)); + }, + Coercion => { + hasher.add(isolatedRefValue(decl_index, data.type)); + hasher.add(isolatedRefValue(decl_index, data.operand)); + }, + Break => { + hasher.add(isolatedIndexValue(decl_index, data.block)); + hasher.add(isolatedRefValue(decl_index, data.operand)); + }, + Call => { + hasher.add(isolatedRefValue(decl_index, data.base)); + writeRefListToHasher(hasher, decl_index, data.args.slice(hir)); + }, + If => { + hasher.add(isolatedRefValue(decl_index, data.cond)); + hasher.add(isolatedRefValue(decl_index, data.yes)); + hasher.add(isolatedRefValue(decl_index, data.no)); + }, + Store => { + hasher.add(isolatedIndexValue(decl_index, data.memory)); + hasher.add(isolatedRefValue(decl_index, data.operand)); + }, + StoreField => { + hasher.add(isolatedRefValue(decl_index, data.base)); + hasher.add(isolatedRefValue(decl_index, data.field)); + hasher.add(isolatedRefValue(decl_index, data.operand)); + }, + StoreVarDecl => { + hasher.addBytes(data.var_decl.slice(hir.string_bytes)); + hasher.add(isolatedRefValue(decl_index, data.operand)); + }, + JsNew => { + hasher.add(isolatedRefValue(decl_index, data.base)); + writeRefListToHasher(hasher, decl_index, data.args.slice(hir)); + }, + StructDecl => { + hasher.add(data.field_len); + for (data.keys(hir), data.types(hir)) |key, ty| { + hasher.addBytes(key.slice(hir.string_bytes)); + hasher.add(isolatedRefValue(decl_index, ty)); + } + }, + StructInit => { + hasher.add(isolatedRefValue(decl_index, data.base_type)); + hasher.add(data.field_len); + for (data.keys(hir), data.inits(hir)) |key, init| { + hasher.addBytes(key.slice(hir.string_bytes)); + hasher.add(isolatedRefValue(decl_index, init)); + } + }, + ArrayInit => { + if (data.type_hint.unwrap()) |type_hint| + hasher.add(isolatedRefValue(decl_index, type_hint)) + else + hasher.add(@as(i32, std.math.maxInt(i32))); + writeRefListToHasher(hasher, decl_index, data.list.slice(hir)); + }, + Func => { + for (data.params.slice(hir)) |optional| { + if (optional.unwrap()) |item| + hasher.add(isolatedRefValue(decl_index, item)); + } + + if (data.return_type.unwrap()) |type_hint| + hasher.add(isolatedRefValue(decl_index, type_hint)) + else + hasher.add(@as(i32, std.math.maxInt(i32))); + + hasher.add(isolatedIndexValue(decl_index, data.body)); + hasher.add(data.instruction_count); + }, + TupleType => { + hasher.add(data.items.len); + for (data.items.slice(hir)) |item| { + hasher.add(isolatedRefValue(decl_index, item)); + } + }, + TupleInit => { + if (data.type_hint.unwrap()) |type_hint| + hasher.add(isolatedRefValue(decl_index, type_hint)) + else + hasher.add(@as(i32, std.math.maxInt(i32))); + writeRefListToHasher(hasher, decl_index, data.items.slice(hir)); + }, + else => @compileError("hash not implemented for " ++ @typeName(T)), + } +} + +pub const SerializedHeader = extern struct { + const version = @import("config").version_string ++ "\x00"; + + instruction_len: u32, + string_bytes: u32, + extra_len: u32, + report_err_len: u32, + report_warn_len: u32, +}; + +pub fn serialize(hir: Hir, target: core.SerializeTarget) !void { + const header: SerializedHeader = .{ + .instruction_len = @intCast(hir.insts.len), + .string_bytes = @intCast(hir.string_bytes.len), + .extra_len = @intCast(hir.extra.list.items.len), + .report_err_len = @intCast(hir.report.errors.len), + .report_warn_len = @intCast(hir.report.warnings.len), + }; + + var iovecs = [_]std.posix.iovec_const{ + core.iovecConst(SerializedHeader.version), + core.iovecConst(std.mem.asBytes(&header)), + core.iovecConst(std.mem.sliceAsBytes(hir.insts.items(.tag))), + core.iovecConst(std.mem.sliceAsBytes(hir.insts.items(.data))), + core.iovecConst(hir.string_bytes), + core.iovecConst(std.mem.sliceAsBytes(hir.extra.list.items)), + core.iovecConst(std.mem.sliceAsBytes(hir.report.errors)), + core.iovecConst(std.mem.sliceAsBytes(hir.report.warnings)), + }; + try target.writev(&iovecs); +} + +const Hir = @This(); + +const std = @import("std"); +const mem = std.mem; +const assert = std.debug.assert; +const HashHelper = std.Build.Cache.HashHelper; + +const core = @import("mod.zig"); +const Cache = core.Cache; + +const Ast = core.Ast; +const Unused = Ast.Unused; +const NodeIndex = Ast.NodeIndex; +const TokenIndex = Ast.TokenIndex; +const OptionalNodeIndex = Ast.OptionalNodeIndex; +const OptionalTokenIndex = Ast.OptionalTokenIndex; +const OptionalExtraIndex = Ast.OptionalExtraIndex; + +const dod = core.array; +const Float = dod.Float; + +const InternPool = core.InternPool; + +const Air = core.Air; + +const Comp = core.Comp; + +const Report = core.Report; +const LazyNode = Report.LazyNode; diff --git a/src/core/InternPool.zig b/src/core/InternPool.zig index 01aea56e759f7bded40c5df6bb801c5c634ec0ca..5671940652839126f561c7ebdd219e84f02d8073 100644 --- a/src/core/InternPool.zig +++ b/src/core/InternPool.zig @@ -638,7 +638,7 @@ pub const ExtraIndex = ExtraStore.Index; /// This is specified with an integer literal and a corresponding comptime /// assert below to break an unfortunate and arguably incorrect dependency loop /// when compiling. -pub const static_len = Teg.Inst.Index.static_len; +pub const static_len = Hir.Inst.Index.static_len; comptime { // @compileLog(static_len, static_keys.len); assert(static_len == static_keys.len); @@ -805,16 +805,16 @@ pub const NullTerminatedString = enum(u32) { pub const TrackedInst = extern struct { path_digest: Cache.BinDigest, - inst: Teg.Inst.Index, + inst: Hir.Inst.Index, comptime { // The fields should be tightly packed. See also serialization logic in `Compilation.saveState`. - assert(@sizeOf(@This()) == Cache.bin_digest_len + @sizeOf(Teg.Inst.Index)); + assert(@sizeOf(@This()) == Cache.bin_digest_len + @sizeOf(Hir.Inst.Index)); } pub const Index = enum(u32) { _, - pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Teg.Inst.Index { + pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Hir.Inst.Index { return i.get(ip).inst; } @@ -1659,7 +1659,7 @@ fn getOrPutTrailingString( } } -pub fn trackInst(ip: *InternPool, gpa: Allocator, file: *Comp.File, inst: Teg.Inst.Index) Allocator.Error!TrackedInst.Index { +pub fn trackInst(ip: *InternPool, gpa: Allocator, file: *Comp.File, inst: Hir.Inst.Index) Allocator.Error!TrackedInst.Index { const t = tracer.trace(@src()); defer t.end(); @@ -1869,7 +1869,7 @@ const assert = std.debug.assert; const core = @import("mod.zig"); const Comp = core.Comp; const Cache = core.Cache; -const Teg = core.Teg; +const Hir = core.Hir; const Air = core.Air; const dod = core.array; const Float = dod.Float; diff --git a/src/core/Report.zig b/src/core/Report.zig index 014f534bbd9cd9075db29f53366b24aacf76f54d..8c485ec5132d66a1bb8cf08aad8be95eecf644fe 100644 --- a/src/core/Report.zig +++ b/src/core/Report.zig @@ -1093,7 +1093,7 @@ pub const MsgContext = struct { pub const LazyNode = struct { // TODO: remove this, it should be inferable off of the analysis unit that belongs to the report decl: Comp.Decl.Index, - inst: Teg.Inst.Index, + inst: Hir.Inst.Index, operand: Operand, pub fn get(lazy: LazyNode, ctx: *MsgContext) !NodeIndex { @@ -1111,15 +1111,15 @@ pub const LazyNode = struct { @panic("TODO: LazyNode.get when the decl is in a nested namespace"); } - const teg = &(ctx.file.?.teg orelse + const hir = &(ctx.file.?.hir orelse // this will be required once incremental compilation hits - @panic("TODO: teg was unloaded")); + @panic("TODO: hir was unloaded")); const decl_inst = decl.teg_decl_index; - const teg_decl = teg.instData(.declaration, decl_inst.resolve(ctx.ip())); + const teg_decl = hir.instData(.declaration, decl_inst.resolve(ctx.ip())); - const node_offset = teg.instNodeOffset(lazy.inst); + const node_offset = hir.instNodeOffset(lazy.inst); const base_node: NodeIndex = @intCast(teg_decl.src_node + node_offset); if (lazy.operand == .main_node) @@ -1129,7 +1129,7 @@ pub const LazyNode = struct { const node_tag = ctx.ast.nodeTag(base_node); - switch (teg.instTag(lazy.inst)) { + switch (hir.instTag(lazy.inst)) { .coercion => switch (node_tag) { .var_decl => switch (lazy.operand) { .lhs => return data.var_decl.type.unwrap().?, @@ -1213,7 +1213,7 @@ pub const LazyNode = struct { } std.debug.print("TODO: operand lookup: .{s} + .{s} + {}\n", .{ - @tagName(teg.instTag(lazy.inst)), + @tagName(hir.instTag(lazy.inst)), @tagName(ctx.ast.nodeTag(base_node)), lazy.operand, }); @@ -1467,7 +1467,7 @@ const TokenIndex = Ast.TokenIndex; const NodeIndex = Ast.NodeIndex; const Location = Ast.Location; -const Teg = core.Teg; +const Hir = core.Hir; const Comp = core.Comp; const File = Comp.File; diff --git a/src/core/Sema.zig b/src/core/Sema.zig index 1366aed00beae7d9b392bff8adddcefe7a381346..ce5efa254618074d6fb1c0b16e30df1372d4bd02 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -1,13 +1,9 @@ -//! Semantic Analysis converts `Teg` +//! Semantic Analysis converts `Hir` to `Air` by stepping over every instruction +//! and evaluating comptime values or emitting runtime instructions. //! -//! Designed as a nonrecursive state machine which can be paused when an -//! unfinished dependency is encountered, allowing the thread to work -//! on other analysis jobs. -//! -//! Sema's responsibilities include -//! - Operation validation and reporting of most compile errors. -//! - Evaluation of `comptime` code. -//! - Declaring dependencies on other analysis units. +//! Designed as a nonrecursive state machine, `Sema` can be paused when an +//! unfinished dependency is encountered, allowing the thread to work on other +//! analysis jobs. comp: *Comp, ip: *InternPool, @@ -20,13 +16,13 @@ owner: AnalysisUnit, /// The namespace index that is used for declaration lookups ns_index: Namespace.Index, /// Non-owning copy -teg: Teg, -/// The file that .teg is from +hir: Hir, +/// The file that .hir is from file: *File, /// All errors and warnings from this decl report: Report.Builder, -/// Map of `Teg` instructions to their resulting `Air` Ref +/// Map of `Hir` instructions to their resulting `Air` Ref inst_map: InstMap, /// `null` when not analyzing function bodies @@ -63,7 +59,7 @@ pub fn init( s: *Sema, comp: *Comp, file: *File, - teg: Teg, + hir: Hir, owner: AnalysisUnit, ns_index: Namespace.Index, arena_state: std.heap.ArenaAllocator, @@ -82,7 +78,7 @@ pub fn init( .arena_state = arena_state, .owner = owner, .ns_index = ns_index, - .teg = teg, + .hir = hir, .file = file, .report = .init(comp.gpa), @@ -102,12 +98,12 @@ pub fn init( const first_instruction, const count = switch (owner.unwrap()) { .decl => |decl| data: { const decl_inst = s.ip.declPtr(decl).teg_decl_index.resolve(s.ip); - const teg_decl: Teg.Decl = teg.instPayload(.declaration, decl_inst); + const teg_decl: Hir.Decl = hir.instPayload(.declaration, decl_inst); break :data .{ decl_inst, teg_decl.instruction_count }; }, .func => |value| data: { const func_inst = value.toKey(s.ip).func.teg_body_inst.get(s.ip); - const func: Teg.Func = teg.instPayload(.func, func_inst.inst); + const func: Hir.Func = hir.instPayload(.func, func_inst.inst); break :data .{ func.body, func.instruction_count }; }, }; @@ -115,7 +111,7 @@ pub fn init( s.inst_map = try .init(s.arena(), first_instruction, count); const block_index = switch (owner.unwrap()) { - .decl => switch (s.teg.declData(first_instruction).body.decode()) { + .decl => switch (s.hir.declData(first_instruction).body.decode()) { .index => |index| index, .value => unreachable, // Sema must not be created for a static value }, @@ -165,11 +161,11 @@ const AnalysisMode = enum { }; const Frame = struct { - ip: [*]const Teg.Inst.Index, + ip: [*]const Hir.Inst.Index, instructions_left: if (runtime_safety) usize else void, mode: AnalysisMode, - block_index: Teg.Inst.Index, + block_index: Hir.Inst.Index, mark_index: u32, data: Data, @@ -192,15 +188,15 @@ const Frame = struct { argument: u16, }; - fn initFromBlock(s: *Sema, block_index: Teg.Inst.Index, mode: AnalysisMode, data: Data) Frame { - switch (s.teg.instTag(block_index)) { + fn initFromBlock(s: *Sema, block_index: Hir.Inst.Index, mode: AnalysisMode, data: Data) Frame { + switch (s.hir.instTag(block_index)) { else => unreachable, // not a block .block, .@"comptime" => {}, } - const list = s.teg.instEncodedData(block_index) - .decodeUnsafe(Teg.PlNode(Teg.IndexList), s.teg.instTag(block_index)) - .extra_index.get(&s.teg) - .slice(&s.teg); + const list = s.hir.instEncodedData(block_index) + .decodeUnsafe(Hir.PlNode(Hir.IndexList), s.hir.instTag(block_index)) + .extra_index.get(&s.hir) + .slice(&s.hir); return .{ .ip = list[0..].ptr, .instructions_left = if (runtime_safety) list.len, @@ -211,12 +207,12 @@ const Frame = struct { }; } - fn currentIndex(frame: *const Frame) Teg.Inst.Index { + fn currentIndex(frame: *const Frame) Hir.Inst.Index { // validate the pointer is correct if (runtime_safety) { const s: *const Sema = @fieldParentPtr("frame", frame); assert(s.magic == .valid); - const extra = s.teg.extra.list.items; + const extra = s.hir.extra.list.items; assert(@intFromPtr(frame.ip) > @intFromPtr(extra.ptr) and @intFromPtr(frame.ip) <= @intFromPtr(&extra.ptr[extra.len - 1])); } @@ -224,22 +220,22 @@ const Frame = struct { return frame.ip[0]; } - fn currentTag(frame: *const Frame) Teg.Tag { + fn currentTag(frame: *const Frame) Hir.Tag { const s: *const Sema = @fieldParentPtr("frame", frame); assert(s.magic == .valid); - return s.teg.instTag(frame.currentIndex()); + return s.hir.instTag(frame.currentIndex()); } - fn currentData(frame: *const Frame, comptime tag: Teg.Tag) tag.Data() { + fn currentData(frame: *const Frame, comptime tag: Hir.Tag) tag.Data() { const s: *const Sema = @fieldParentPtr("frame", frame); assert(s.magic == .valid); - return s.teg.instData(tag, frame.currentIndex()); + return s.hir.instData(tag, frame.currentIndex()); } - fn currentPayload(frame: *const Frame, comptime tag: Teg.Tag) tag.Data().T { + fn currentPayload(frame: *const Frame, comptime tag: Hir.Tag) tag.Data().T { const s: *const Sema = @fieldParentPtr("frame", frame); assert(s.magic == .valid); - return s.teg.instPayload(tag, frame.currentIndex()); + return s.hir.instPayload(tag, frame.currentIndex()); } fn advance(frame: *Frame) void { @@ -282,10 +278,10 @@ const Function = struct { /// instructions it has. const InstMap = struct { items: []Air.Inst.Ref.Optional, - start: Teg.Inst.Index, + start: Hir.Inst.Index, debug_traces: if (runtime_safety) []StoredTrace else void, - pub fn init(allocator: Allocator, inst_start: Teg.Inst.Index, inst_len: u32) !InstMap { + pub fn init(allocator: Allocator, inst_start: Hir.Inst.Index, inst_len: u32) !InstMap { const insts = try allocator.alloc(Air.Inst.Ref.Optional, inst_len); errdefer allocator.free(insts); @@ -309,7 +305,7 @@ const InstMap = struct { map.* = undefined; } - pub fn getOrNull(map: *const InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref { + pub fn getOrNull(map: *const InstMap, teg_code: Hir.Inst.Index) ?Air.Inst.Ref { const source_i = @intFromEnum(teg_code); const start_i = @intFromEnum(map.start); if (assertFailed(source_i >= start_i and source_i <= start_i + map.items.len)) { @@ -322,7 +318,7 @@ const InstMap = struct { return map.items[source_i - start_i].unwrap(); } - pub fn get(map: *const InstMap, teg_code: Teg.Inst.Index) Air.Inst.Ref { + pub fn get(map: *const InstMap, teg_code: Hir.Inst.Index) Air.Inst.Ref { return map.getOrNull(teg_code) orelse { if (runtime_safety) { std.debug.panic("did not analyze %{d}", .{@intFromEnum(teg_code)}); @@ -331,7 +327,7 @@ const InstMap = struct { }; } - pub fn add(map: *InstMap, from: Teg.Inst.Index, to: Air.Inst.Ref) void { + pub fn add(map: *InstMap, from: Hir.Inst.Index, to: Air.Inst.Ref) void { const source_i = @intFromEnum(from); const start_i = @intFromEnum(map.start); if (assertFailed(source_i >= start_i and source_i <= start_i + map.items.len)) { @@ -371,7 +367,7 @@ const DoMark = enum { marked, }; -fn getRef(s: *Sema, teg_ref: Teg.Inst.Ref, mark: DoMark) Air.Inst.Ref { +fn getRef(s: *Sema, teg_ref: Hir.Inst.Ref, mark: DoMark) Air.Inst.Ref { switch (teg_ref.decode()) { .value => |value| return value.toAir(), .index => |index| { @@ -389,7 +385,7 @@ fn getRef(s: *Sema, teg_ref: Teg.Inst.Ref, mark: DoMark) Air.Inst.Ref { } } -fn getValue(s: *Sema, src: LazyNode, teg_ref: Teg.Inst.Ref) !Value { +fn getValue(s: *Sema, src: LazyNode, teg_ref: Hir.Inst.Ref) !Value { switch (teg_ref.decode()) { .value => |value| return value, .index => |index| { @@ -399,17 +395,17 @@ fn getValue(s: *Sema, src: LazyNode, teg_ref: Teg.Inst.Ref) !Value { } } -fn getCoercedValue(s: *Sema, src: LazyNode, teg_ref: Teg.Inst.Ref, ty: Type) !Value { +fn getCoercedValue(s: *Sema, src: LazyNode, teg_ref: Hir.Inst.Ref, ty: Type) !Value { const any = try s.getValue(src, teg_ref); return try s.coerceValue(src, any, ty); } -fn getCoercedRef(s: *Sema, src: LazyNode, teg_ref: Teg.Inst.Ref, ty: Type, mark: DoMark) !Air.Inst.Ref { +fn getCoercedRef(s: *Sema, src: LazyNode, teg_ref: Hir.Inst.Ref, ty: Type, mark: DoMark) !Air.Inst.Ref { const any = s.getRef(teg_ref, mark); return try s.coerceRef(src, any, ty); } -fn getType(s: *Sema, src: LazyNode, teg_ref: Teg.Inst.Ref) !Type { +fn getType(s: *Sema, src: LazyNode, teg_ref: Hir.Inst.Ref) !Type { return (try s.getCoercedValue(src, teg_ref, .type)).toTypeUnsafe(s.ip); } @@ -491,7 +487,7 @@ const InstructionKind = enum { control_flow, pop_control_flow, - pub inline fn fromTag(tag: Teg.Tag) InstructionKind { + pub inline fn fromTag(tag: Hir.Tag) InstructionKind { return switch (tag) { // these push new frames, and frame end will assign its value .func, @@ -523,7 +519,7 @@ const InstructionKind = enum { const PopControlFlowState = enum { stop, @"continue" }; -fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag).Result() { +fn stepInner(s: *Sema, comptime tag: Hir.Tag) Error!InstructionKind.fromTag(tag).Result() { switch (tag) { .block => unreachable, // frame must already be pushed .declaration, @@ -534,7 +530,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) // TODO: pre-evaluate `.param` nodes, since they are always at the start of a function. // this way we can avoid repeating the non-trivial call into InternPool .param => { - const param: Teg.DataNode = s.frame.currentData(.param); + const param: Hir.DataNode = s.frame.currentData(.param); assert(s.frame.mode == .run_time); // never emitted in a comptime block assert(s.owner.kind == .func); // only emitted in function body const proto = s.owner.unwrap().func.toKey(s.ip).func.ty.toUnresolvedFunctionPrototype(s.ip).?; @@ -550,8 +546,8 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) } }, .string => { - const string: Teg.SlicedString = s.frame.currentPayload(.string); - return s.makeValueAir(.{ .string = string.slice(s.teg.string_bytes) }); + const string: Hir.SlicedString = s.frame.currentPayload(.string); + return s.makeValueAir(.{ .string = string.slice(s.hir.string_bytes) }); }, .number => { const number: store.Float = s.frame.currentPayload(.number); @@ -565,7 +561,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) .gt, .eq, => { - const bin: Teg.Bin = s.frame.currentPayload(tag); + const bin: Hir.Bin = s.frame.currentPayload(tag); const op = @field(BinaryOp, @tagName(tag)); const lhs = s.getRef(bin.lhs, .marked); @@ -600,7 +596,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }); }, .negate => { - const un: Teg.RefNode = s.frame.currentData(.negate); + const un: Hir.RefNode = s.frame.currentData(.negate); const operand = s.getRef(un.operand, .marked); @@ -622,7 +618,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) return s.addRef(.negate, .{ .operand = operand }); }, .bool_not => { - const un: Teg.RefNode = s.frame.currentData(.bool_not); + const un: Hir.RefNode = s.frame.currentData(.bool_not); const operand = try s.getCoercedRef(s.lazyNode(.operand), un.operand, .bool, .marked); @@ -639,7 +635,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) return s.addRef(.bool_not, .{ .operand = operand }); }, .struct_decl => { - const node: Teg.StructDecl = s.frame.currentPayload(.struct_decl); + const node: Hir.StructDecl = s.frame.currentPayload(.struct_decl); const scratch = try s.scratch.reserveMulti( s.arena(), @@ -649,12 +645,12 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) defer scratch.deinit(); // Fill the first half with keys - for (scratch.items(.keys), node.keys(&s.teg)) |*dest, src| { - dest.* = try s.ip.getOrPutString(s.gpa, src.slice(s.teg.string_bytes), .no_embedded_nulls); + for (scratch.items(.keys), node.keys(&s.hir)) |*dest, src| { + dest.* = try s.ip.getOrPutString(s.gpa, src.slice(s.hir.string_bytes), .no_embedded_nulls); } // Fill the second half with coerced values. - for (node.types(&s.teg), 0..) |ty, i| { + for (node.types(&s.hir), 0..) |ty, i| { scratch.at(.types, i).* = try s.getType(s.lazyNode(.argument(@intCast(i))), ty); } @@ -688,7 +684,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) } }); }, .field => { - const field: Teg.Bin = s.frame.currentPayload(.field); + const field: Hir.Bin = s.frame.currentPayload(.field); const lhs = s.getRef(field.lhs, .marked); const lhs_type = s.typeOfRef(lhs); @@ -733,7 +729,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }); }, .call => { - const call: Teg.Call = s.frame.currentPayload(.call); + const call: Hir.Call = s.frame.currentPayload(.call); const base = s.getRef(call.base, .marked); const base_type = s.typeOfRef(base); @@ -759,7 +755,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) } // validate function args - for (proto.param_types, call.args.slice(&s.teg), 0..) |param, arg, i| { + for (proto.param_types, call.args.slice(&s.hir), 0..) |param, arg, i| { const ref = s.getRef(arg, .marked); const coerced = try s.coerceRef( s.lazyNode(.argument(@intCast(i))), @@ -769,7 +765,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) scratch[i] = coerced; } } else { - for (call.args.slice(&s.teg), 0..) |arg, i| { + for (call.args.slice(&s.hir), 0..) |arg, i| { const ref = try s.ensureValueDefinedRepresentation( s.lazyNode(.argument(@intCast(i))), s.getRef(arg, .marked), @@ -792,7 +788,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }); }, .array_init => { - const node: Teg.ArrayInit = s.frame.currentPayload(.array_init); + const node: Hir.ArrayInit = s.frame.currentPayload(.array_init); assert(node.list.len > 0 or node.type_hint.unwrap() != null); @@ -806,7 +802,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) var is_constant = true; - for (node.list.slice(&s.teg), 0..) |item_teg_ref, i| { + for (node.list.slice(&s.hir), 0..) |item_teg_ref, i| { const item = s.getRef(item_teg_ref, .marked); const coerced = if (item_type) |ty| try s.coerceRef(s.lazyNode(.argument(@intCast(i))), item, ty) @@ -862,8 +858,8 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }); }, .store => { - const data: Teg.Store = s.frame.currentPayload(.store); - assert(s.teg.instTag(data.memory) == .memory); // corrupt store instruction + const data: Hir.Store = s.frame.currentPayload(.store); + assert(s.hir.instTag(data.memory) == .memory); // corrupt store instruction try s.ensureAtRuntime(s.lazyNode(.main_node)); // TODO @@ -885,7 +881,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .js_new => { - const call: Teg.JsNew = s.frame.currentPayload(.js_new); + const call: Hir.JsNew = s.frame.currentPayload(.js_new); try s.ensureAtRuntime(s.lazyNode(.main_node)); @@ -893,7 +889,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) const scratch = try s.arena().alloc(Air.Inst.Ref, call.args.len); defer s.arena().free(scratch); - for (call.args.slice(&s.teg), 0..) |arg, i| { + for (call.args.slice(&s.hir), 0..) |arg, i| { const ref = try s.ensureValueDefinedRepresentation( s.lazyNode(.argument(@intCast(i))), s.getRef(arg, .marked), @@ -924,7 +920,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .func => { - const data: Teg.Func = s.frame.currentPayload(.func); + const data: Hir.Func = s.frame.currentPayload(.func); const reserve = try s.scratch.reserveMulti( s.arena(), .{ .params = Type, .captures = Air.Inst.Ref }, @@ -937,7 +933,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) defer handled_reserve.assertUnlocked(); errdefer handled_reserve.unlock(); - for (data.params.slice(&s.teg), 0..) |param, i| { + for (data.params.slice(&s.hir), 0..) |param, i| { const src = s.lazyNode(.argument(@intCast(i))); const known = param.unwrap() orelse @panic("TODO: inferred param sets"); @@ -991,7 +987,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .@"break" => { - const brk: Teg.Break = s.frame.currentPayload(.@"break"); + const brk: Hir.Break = s.frame.currentPayload(.@"break"); const operand = s.getRef(brk.operand, .marked); assert(s.frame.block_index == brk.block); @@ -1036,7 +1032,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) s.frame.advance(); }, .@"if" => { - const if_data: Teg.If = s.frame.currentPayload(.@"if"); + const if_data: Hir.If = s.frame.currentPayload(.@"if"); switch (data.conditional) { .comptime_condition => { @@ -1093,7 +1089,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) s.frame.advance(); }, .func => { - const func_data: Teg.Func = s.frame.currentPayload(.func); + const func_data: Hir.Func = s.frame.currentPayload(.func); const reserve: FunctionReserve = .fromIndex( &s.scratch, @@ -1104,7 +1100,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) const return_type = switch (data.func) { .argument => |arg_num| ret: { for ( - func_data.params.slice(&s.teg)[arg_num + 1 ..], + func_data.params.slice(&s.hir)[arg_num + 1 ..], reserve.items(.params)[arg_num + 1 ..], arg_num + 1.., ) |param, *dst, i| { @@ -1172,10 +1168,10 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .load_decl => { - const request: Teg.StrNode = s.frame.currentData(.load_decl); + const request: Hir.StrNode = s.frame.currentData(.load_decl); const decl_name = try s.ip.getOrPutString( s.gpa, - request.string.slice(s.teg.string_bytes), + request.string.slice(s.hir.string_bytes), .no_embedded_nulls, ); const decl_index = s.lookupDecl(decl_name); @@ -1183,13 +1179,13 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .coercion => { - const as: Teg.Coercion = s.frame.currentPayload(.coercion); + const as: Hir.Coercion = s.frame.currentPayload(.coercion); const coerce_to = try s.getType(s.lazyNode(.operand), as.type); return try s.getCoercedRef(s.lazyNode(.operand), as.operand, coerce_to, .marked); }, .compile_error => { - const node: Teg.RefNode = s.frame.currentData(.compile_error); + const node: Hir.RefNode = s.frame.currentData(.compile_error); const value = try s.getCoercedValue(s.lazyNode(.operand), node.operand, .string); const str = value.asString(s.ip).toSlice(s.ip); return s.fail(.user_compile_error, .{ @@ -1199,7 +1195,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .@"if" => { - const data: Teg.If = s.frame.currentPayload(.@"if"); + const data: Hir.If = s.frame.currentPayload(.@"if"); const condition = try s.getCoercedRef(s.lazyNode(.operand), data.cond, .bool, .marked); switch (condition.decode()) { @@ -1270,7 +1266,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .struct_init => { - const node: Teg.StructInit = s.frame.currentPayload(.struct_init); + const node: Hir.StructInit = s.frame.currentPayload(.struct_init); // Ensure we have a valid struct to operate on const ty = try s.getType(s.lazyNode(.operand), node.base_type); @@ -1295,9 +1291,9 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) // The ordering of `storage` is defined by the struct's type, which // may be different than the instantiation. The fields are visited in // order of the instantiation. - for (node.keys(&s.teg), node.inits(&s.teg), 0..) |init_key, init_value, i| { + for (node.keys(&s.hir), node.inits(&s.hir), 0..) |init_key, init_value, i| { const found_index = index: for (struct_type.field_keys, 0..) |decl_key, j| { - if (decl_key.eqlSlice(init_key.slice(s.teg.string_bytes), s.ip)) + if (decl_key.eqlSlice(init_key.slice(s.hir.string_bytes), s.ip)) break :index j; } else { has_errors = true; @@ -1374,18 +1370,18 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .array_type => { - const un: Teg.RefNode = s.frame.currentData(.array_type); + const un: Hir.RefNode = s.frame.currentData(.array_type); const operand = try s.getType(s.lazyNode(.operand), un.operand); return s.makeValueAir(.{ .array_type = .{ .item_type = operand } }); }, .tuple_type => { - const data: Teg.TupleType = s.frame.currentPayload(.tuple_type); + const data: Hir.TupleType = s.frame.currentPayload(.tuple_type); const scratch = try s.scratch.reserve(s.arena(), Type, data.items.len); defer scratch.deinit(); - for (data.items.slice(&s.teg), scratch.slice(), 0..) |src, *dst, i| { + for (data.items.slice(&s.hir), scratch.slice(), 0..) |src, *dst, i| { dst.* = try s.getType(s.lazyNode(.argument(@intCast(i))), src); } @@ -1393,7 +1389,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .tuple_init => { - const data: Teg.TupleInit = s.frame.currentPayload(.tuple_init); + const data: Hir.TupleInit = s.frame.currentPayload(.tuple_init); _ = data.type_hint; // TODO: use type hint @@ -1401,7 +1397,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) defer scratch.deinit(); var is_const = true; - for (data.items.slice(&s.teg), scratch.slice()) |src, *dst| { + for (data.items.slice(&s.hir), scratch.slice()) |src, *dst| { const result = s.getRef(src, .marked); dst.* = result; if (result.kind == .index) is_const = false; @@ -1447,7 +1443,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .result_type_argument => { - const result: Teg.ResultTypeArgument = s.frame.currentData(.result_type_argument); + const result: Hir.ResultTypeArgument = s.frame.currentData(.result_type_argument); const operand = s.getRef(result.call_base, .no_mark); const ty = s.typeOfRef(operand); @@ -1462,7 +1458,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .result_type_array_value => { - const result: Teg.ResultTypeArrayValue = s.frame.currentData(.result_type_array_value); + const result: Hir.ResultTypeArrayValue = s.frame.currentData(.result_type_array_value); const operand = try s.getValue(s.lazyNode(.main_node), result.type); const ty = operand.toType(s.ip) orelse @@ -1475,7 +1471,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .result_type_tuple_field => { - const result: Teg.ResultTypeTupleField = s.frame.currentData(.result_type_tuple_field); + const result: Hir.ResultTypeTupleField = s.frame.currentData(.result_type_tuple_field); const operand = try s.getValue(s.lazyNode(.main_node), result.type); const ty = operand.toType(s.ip) orelse @@ -1487,7 +1483,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) }, .result_type_struct_field => { - const result: Teg.ResultTypeStructField = s.frame.currentData(.result_type_struct_field); + const result: Hir.ResultTypeStructField = s.frame.currentData(.result_type_struct_field); const operand = try s.getValue(s.lazyNode(.main_node), result.type); const ty = operand.toType(s.ip) orelse @@ -1496,7 +1492,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) const struct_type = ty.toStructType(s.ip) orelse return Value.opaque_type.toAir(); - const to_match = result.field.slice(s.teg.string_bytes); + const to_match = result.field.slice(s.hir.string_bytes); for (struct_type.field_keys, struct_type.field_types) |k, v| { if (k.eqlSlice(to_match, s.ip)) { return v.value().toAir(); @@ -1512,7 +1508,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) fn emitRuntimeBlock( s: *Sema, - from: Teg.Inst.Index, + from: Hir.Inst.Index, break_operand: Air.Inst.Ref, mark: core.U32Stack.Mark, ) !void { @@ -1553,7 +1549,7 @@ const BinaryOp = enum { eq, }; -fn binTypeResolution(s: *Sema, src: Teg.Inst.Index, op: BinaryOp, lhs: Type, rhs: Type) !Type { +fn binTypeResolution(s: *Sema, src: Hir.Inst.Index, op: BinaryOp, lhs: Type, rhs: Type) !Type { if (lhs == .@"opaque") { return if (rhs == .numeric_literal) .float else rhs; } @@ -2084,7 +2080,7 @@ fn fieldAccessStruct( lhs: Air.Inst.Ref, lhs_type: Type, struct_type: InternPool.Key.StructType, - field: Teg.Inst.Ref, + field: Hir.Inst.Ref, ) !Air.Inst.Ref { const rhs = try s.getCoercedValue(s.lazyNode(.rhs), field, .string); const string = rhs.asString(s.ip); @@ -2125,7 +2121,7 @@ fn fieldAccessStruct( } } -fn fieldAccessArray(s: *Sema, lhs: Air.Inst.Ref, lhs_type: Type, rhs_teg: Teg.Inst.Ref) !Air.Inst.Ref { +fn fieldAccessArray(s: *Sema, lhs: Air.Inst.Ref, lhs_type: Type, rhs_teg: Hir.Inst.Ref) !Air.Inst.Ref { const rhs = s.getRef(rhs_teg, .marked); switch (s.typeOfRef(rhs)) { @@ -2176,7 +2172,7 @@ fn fieldAccessTuple( lhs: Air.Inst.Ref, lhs_type: Type, tuple_type: InternPool.Key.TupleType, - field: Teg.Inst.Ref, + field: Hir.Inst.Ref, ) !Air.Inst.Ref { const index_value = try s.getCoercedValue(s.lazyNode(.rhs), field, .int32); const index_signed = index_value.toInt32(s.ip) orelse unreachable; @@ -2387,7 +2383,7 @@ const File = Comp.File; const Decl = Comp.Decl; const Namespace = Comp.Namespace; -const Teg = core.Teg; +const Hir = core.Hir; const Air = core.Air; const Ast = core.Ast; diff --git a/src/core/StoredTrace.zig b/src/core/StoredTrace.zig index 5508bbf120fd150745b664cacc56077efe169526..0ba9c60c33b5338c851ae15ea6373c4886ba9507 100644 --- a/src/core/StoredTrace.zig +++ b/src/core/StoredTrace.zig @@ -1,6 +1,6 @@ -//! A variant of std.builtin.StackTrace that stores its data within itself -//! instead of being a pointer. This allows capturing the stack trace and -//! saving it for later printing. +//! A variant of `std.builtin.StackTrace` that stores its data within itself +//! instead of being a pointer. This allows storing captured stack traces +//! for later printing. data: [31]usize, index: usize, diff --git a/src/core/Teg.Dump.zig b/src/core/Teg.Dump.zig deleted file mode 100644 index a244b796e151855de91e8d27322795120aec7a99..0000000000000000000000000000000000000000 --- a/src/core/Teg.Dump.zig +++ /dev/null @@ -1,522 +0,0 @@ -//! This implements a debug formatter for Teg -teg: Teg, -tty: ?std.io.tty.Config = null, - -const State = struct { - arena: std.mem.Allocator, - teg: *const Teg, - indent: usize = 0, - tty: std.io.tty.Config, - visited: std.AutoHashMapUnmanaged(Teg.Inst.Index, void) = .{}, - - fn newline(state: State, writer: anytype) !void { - try writer.writeByte('\n'); - if (state.indent > 0) - try writer.writeByteNTimes(' ', state.indent * 2); - } - - fn dumpInst(s: *State, writer: anytype, inst: Teg.Inst.Index) @TypeOf(writer).Error!void { - const teg = s.teg; - const tty = s.tty; - - try writer.print("%{} = ", .{@intFromEnum(inst)}); - - const tag = s.teg.instTag(inst); - - try tty.setColor(writer, .bright_black); - const node_offset = teg.instNodeOffset(inst); - try writer.print("{c}{d} ", .{ - @as(u8, if (node_offset >= 0) '+' else '-'), - @abs(node_offset), - }); - try tty.setColor(writer, .reset); - - try tty.setColor(writer, .cyan); - try writer.print("{s}", .{@tagName(tag)}); - try tty.setColor(writer, .reset); - - if ((try s.visited.fetchPut(s.arena, inst, {})) != null) { - try tty.setColor(writer, .red); - try writer.writeAll(" [error: recursive tree]"); - try tty.setColor(writer, .reset); - return; - } - - switch (tag) { - .module => { - try s.dumpInstList(writer, teg.moduleDeclarations(inst), '{', '}'); - }, - .declaration, .variable_declaration, .test_declaration => { - const decl = teg.fullDecl(inst); - - try writer.writeAll(" "); - const name = decl.name.slice(teg.string_bytes); - if (name.len == 0) { - try tty.setColor(writer, .bold); - try writer.writeAll("[no name]"); - } else { - try tty.setColor(writer, .magenta); - try writer.writeAll(name); - } - try tty.setColor(writer, .reset); - try writer.writeAll(" ("); - try writer.print("{d}", .{decl.instruction_count}); - try writer.writeAll(")"); - try tty.setColor(writer, .bright_black); - try writer.writeAll(" ["); - try writer.writeAll(&std.fmt.bytesToHex(decl.hash, .lower)); - try writer.writeAll("]"); - try tty.setColor(writer, .reset); - try writer.writeAll(" ("); - s.indent += 1; - try s.newline(writer); - try s.dumpRef(writer, decl.body); - s.indent -= 1; - try s.newline(writer); - try writer.writeAll(")"); - }, - .func => { - const func: Teg.Func = teg.instPayload(.func, inst); - try writer.writeAll(" {"); - s.indent += 1; - try s.newline(writer); - try writer.writeAll("params: "); - try s.dumpOptionalRefList(writer, func.params.slice(teg), '[', ']'); - try s.newline(writer); - try writer.writeAll("return_type: "); - if (func.return_type.unwrap()) |ret| - try s.dumpRef(writer, ret) - else { - try tty.setColor(writer, .bright_black); - try writer.writeAll(""); - try tty.setColor(writer, .reset); - } - try s.newline(writer); - try writer.writeAll("captures: "); - try s.dumpInstList2(writer, func.captures.slice(teg), '[', ']'); - try s.newline(writer); - try writer.writeAll("body: "); - try s.dumpInst(writer, func.body); - s.indent -= 1; - try s.newline(writer); - try writer.writeAll("}"); - }, - .param => { - const data = teg.instData(.param, inst); - try writer.writeAll("("); - if (data.data == std.math.maxInt(u32)) { - try tty.setColor(writer, .bright_black); - try writer.writeAll(""); - } else { - try tty.setColor(writer, .yellow); - try writer.print("{d}", .{data.data}); - } - try tty.setColor(writer, .reset); - try writer.writeAll(")"); - }, - inline .block, .@"comptime" => |t| { - const block = teg.instData(t, inst); - const index_list = block.extra_index.get(teg); - try s.dumpInstList(writer, index_list.slice(teg), '[', ']'); - }, - .number => { - const number = teg.instData(.number, inst); - const float = number.extra_index.get(teg).decode(); - try tty.setColor(writer, .yellow); - try writer.print(" {d}", .{float}); - try tty.setColor(writer, .reset); - }, - inline .field, - .index, - .add, - .sub, - .mul, - .div, - .rem, - .pow, - .@"and", - .@"or", - .bit_and, - .bit_or, - .bit_xor, - .shl, - .shr, - .eq, - .ne, - .lt, - .le, - .gt, - .ge, - => |comptime_tag| { - const data = teg.instData(comptime_tag, inst).extra_index.get(teg); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.lhs); - try writer.writeAll(", "); - try s.dumpRefMarker(writer, data.rhs); - try writer.writeAll(")"); - }, - .coercion => { - const data = teg.instData(.coercion, inst).extra_index.get(teg); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.type); - try writer.writeAll(", "); - try s.dumpRefMarker(writer, data.operand); - try writer.writeAll(")"); - }, - .@"break" => { - const data: Teg.Break = teg.instData(.@"break", inst).extra_index.get(teg); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.block.toRef()); - try writer.writeAll(", "); - try s.dumpRefMarker(writer, data.operand); - try writer.writeAll(")"); - }, - .store => { - const data: Teg.Store = teg.instData(.store, inst).extra_index.get(teg); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.memory.toRef()); - try writer.writeAll(", "); - try s.dumpRefMarker(writer, data.operand); - try writer.writeAll(")"); - }, - .store_field => { - const data: Teg.StoreField = teg.instData(.store_field, inst).extra_index.get(teg); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.base); - try writer.writeAll(", "); - try s.dumpRefMarker(writer, data.field); - try writer.writeAll(", "); - try s.dumpRefMarker(writer, data.operand); - try writer.writeAll(")"); - }, - .store_decl => { - const data: Teg.StoreVarDecl = teg.instData(.store_decl, inst).extra_index.get(teg); - try writer.writeAll("("); - try tty.setColor(writer, .magenta); - try writer.writeAll(data.var_decl.slice(teg.string_bytes)); - try tty.setColor(writer, .reset); - try writer.writeAll(", "); - try s.dumpRefMarker(writer, data.operand); - try writer.writeAll(")"); - }, - .string => { - const data: Teg.SlicedString = teg.instData(.string, inst).extra_index.get(teg); - try tty.setColor(writer, .green); - try writer.writeAll(" \""); - try writer.writeAll(data.slice(teg.string_bytes)); - try writer.writeAll("\""); - try tty.setColor(writer, .reset); - }, - inline .memory, - .discard, - .ensure_void, - .negate, - .bool_not, - .bit_not, - .@"extern", - .import, - .fetch, - .compile_error, - .type_of, - .array_type, - .assert, - => |comptime_tag| { - const data = teg.instData(comptime_tag, inst); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.operand); - try writer.writeAll(")"); - }, - .call => { - const call: Teg.Call = teg.instData(.call, inst).extra_index.get(teg); - try writer.writeAll("("); - try s.dumpRefMarker(writer, call.base); - try writer.writeAll(", "); - try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']'); - try writer.writeAll(")"); - }, - .js_new => { - const call: Teg.JsNew = teg.instData(.js_new, inst).extra_index.get(teg); - try writer.writeAll("("); - try s.dumpRefMarker(writer, call.base); - try writer.writeAll(", "); - try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']'); - try writer.writeAll(")"); - }, - .load_decl => { - const call: Teg.StrNode = teg.instData(.load_decl, inst); - - try tty.setColor(writer, .red); - try writer.print(" {s}", .{call.string.slice(teg.string_bytes)}); - try tty.setColor(writer, .reset); - }, - .@"if" => { - const data: Teg.If = teg.instPayload(.@"if", inst); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.cond); - try writer.writeAll(") {"); - s.indent += 1; - try s.newline(writer); - try writer.writeAll("yes: "); - try s.dumpRef(writer, data.yes); - try s.newline(writer); - try writer.writeAll("no: "); - try s.dumpRef(writer, data.no); - s.indent -= 1; - try s.newline(writer); - try writer.writeAll("}"); - }, - .struct_decl => { - const data: Teg.StructDecl = teg.instPayload(.struct_decl, inst); - try writer.writeAll(" {"); - s.indent += 1; - try s.newline(writer); - for (data.keys(teg), data.types(teg), 0..) |key, value, i| { - if (i != 0) - try s.newline(writer); - try writer.print("{s}: ", .{key.slice(teg.string_bytes)}); - try s.dumpRefMarker(writer, value); - } - s.indent -= 1; - try s.newline(writer); - try writer.writeAll("}"); - }, - .struct_init => { - const data: Teg.StructInit = teg.instPayload(.struct_init, inst); - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.base_type); - try writer.writeAll(") {"); - s.indent += 1; - try s.newline(writer); - for (data.keys(teg), data.inits(teg), 0..) |key, value, i| { - if (i != 0) - try s.newline(writer); - try writer.print("{s}: ", .{key.slice(teg.string_bytes)}); - try s.dumpRefMarker(writer, value); - } - s.indent -= 1; - try s.newline(writer); - try writer.writeAll("}"); - }, - .array_init => { - const data: Teg.ArrayInit = teg.instPayload(.array_init, inst); - - if (data.type_hint.unwrap()) |type_hint| { - try writer.writeAll("("); - try s.dumpRef(writer, type_hint); - try writer.writeAll(", "); - } - - try writer.writeAll("["); - - s.indent += 1; - - for (data.list.slice(teg)) |item| { - try s.newline(writer); - try s.dumpRefMarker(writer, item); - try writer.writeAll(","); - } - - s.indent -= 1; - try s.newline(writer); - - try writer.writeAll("]"); - - if (data.type_hint != .none) - try writer.writeAll(")"); - }, - .tuple_type => { - const data: Teg.TupleType = teg.instPayload(.tuple_type, inst); - - try writer.writeAll("("); - try s.dumpRefMarkerList(writer, data.items.slice(teg), '[', ']'); - try writer.writeAll(")"); - }, - .tuple_init => { - const data: Teg.TupleInit = teg.instPayload(.tuple_init, inst); - - try writer.writeAll("("); - if (data.type_hint.unwrap()) |type_hint| { - try s.dumpRef(writer, type_hint); - try writer.writeAll(", "); - } - try s.dumpRefMarkerList(writer, data.items.slice(teg), '[', ']'); - try writer.writeAll(")"); - }, - .result_type_argument => { - const data: Teg.ResultTypeArgument = teg.instData(.result_type_argument, inst); - - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.call_base); - try writer.print(", {d}", .{data.argument}); - try writer.writeAll(")"); - }, - .result_type_struct_field => { - const data: Teg.ResultTypeStructField = teg.instData(.result_type_struct_field, inst); - - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.type); - try writer.print(", {s}", .{data.field.slice(s.teg.string_bytes)}); - try writer.writeAll(")"); - }, - .result_type_tuple_field => { - const data: Teg.ResultTypeTupleField = teg.instData(.result_type_tuple_field, inst); - - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.type); - try writer.print(", {d}", .{data.item}); - try writer.writeAll(")"); - }, - .result_type_array_value => { - const data: Teg.ResultTypeArrayValue = teg.instData(.result_type_array_value, inst); - - try writer.writeAll("("); - try s.dumpRefMarker(writer, data.type); - try writer.writeAll(")"); - }, - } - } - - fn dumpRef(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { - switch (ref.decode()) { - .index => |index| try s.dumpInst(writer, index), - .value => { - try s.tty.setColor(writer, .blue); - try writer.print("%{s}", .{@tagName(ref)}); - try s.tty.setColor(writer, .reset); - }, - } - } - - fn dumpRefMarker(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { - switch (ref.decode()) { - .index => |index| { - try s.tty.setColor(writer, .blue); - try writer.print("%{d}", .{@intFromEnum(index)}); - try s.tty.setColor(writer, .reset); - }, - .value => { - try s.tty.setColor(writer, .blue); - try writer.print("%{s}", .{@tagName(ref)}); - try s.tty.setColor(writer, .reset); - }, - } - } - - fn dumpInstList(state: *State, writer: anytype, list: []const Teg.Inst.Index, l: u8, r: u8) !void { - try writer.print(" {c}", .{l}); - if (list.len > 0) { - state.indent += 1; - - for (list) |inst| { - try state.newline(writer); - try state.dumpInst(writer, inst); - } - - state.indent -= 1; - try state.newline(writer); - } - try writer.print("{c}", .{r}); - } - - fn dumpInstList2(state: *State, writer: anytype, list: []const Teg.Inst.Index, l: u8, r: u8) !void { - try writer.print(" {c}", .{l}); - if (list.len > 0) { - state.indent += 1; - - for (list) |inst| { - try state.newline(writer); - try state.dumpRefMarker(writer, inst.toRef()); - } - - state.indent -= 1; - try state.newline(writer); - } - try writer.print("{c}", .{r}); - } - - fn dumpRefList(state: *State, writer: anytype, list: []const Teg.Inst.Ref, l: u8, r: u8) !void { - try writer.print(" {c}", .{l}); - if (list.len > 0) { - state.indent += 1; - - for (list) |ref| { - try state.newline(writer); - try state.dumpRef(writer, ref); - } - - state.indent -= 1; - - try state.newline(writer); - } - try writer.print("{c}", .{r}); - } - - fn dumpOptionalRefList(state: *State, writer: anytype, list: []const Teg.Inst.Ref.Optional, l: u8, r: u8) !void { - try writer.print(" {c}", .{l}); - if (list.len > 0) { - state.indent += 1; - - for (list) |optional_ref| { - try state.newline(writer); - if (optional_ref.unwrap()) |ref| - try state.dumpRef(writer, ref) - else { - try state.tty.setColor(writer, .bright_black); - try writer.writeAll(""); - try state.tty.setColor(writer, .reset); - } - } - - state.indent -= 1; - - try state.newline(writer); - } - try writer.print("{c}", .{r}); - } - - fn dumpRefMarkerList(state: *State, writer: anytype, list: []const Teg.Inst.Ref, l: u8, r: u8) !void { - try writer.print("{c}", .{l}); - if (list.len > 0) { - state.indent += 1; - - for (list) |ref| { - try state.newline(writer); - try state.dumpRefMarker(writer, ref); - } - - state.indent -= 1; - - try state.newline(writer); - } - try writer.print("{c}", .{r}); - } -}; - -pub fn format( - dump: @This(), - comptime unused_fmt: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, -) !void { - comptime assert(unused_fmt.len == 0); - - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - - const teg = dump.teg; - var state = State{ - .teg = &teg, - .tty = dump.tty orelse detectTTYFromWriter(writer), - .arena = arena.allocator(), - }; - try state.dumpInst(writer, .main_module_inst); - try writer.writeAll("\n"); - assert(state.indent == 0); -} - -const std = @import("std"); -const assert = std.debug.assert; - -const core = @import("mod.zig"); -const Teg = core.Teg; -const detectTTYFromWriter = core.detectTTYFromWriter; diff --git a/src/core/Teg.zig b/src/core/Teg.zig deleted file mode 100644 index 11b844392eb6864598b6b79f087ba3af50f1b51e..0000000000000000000000000000000000000000 --- a/src/core/Teg.zig +++ /dev/null @@ -1,1006 +0,0 @@ -//! TODO: rename this to EIR (expression intermediate representation) - -insts: Inst.List.Slice, -string_bytes: []const u8, -extra: ExtraStore = .{}, -report: Report, - -pub const ExtraStore = dod.ExtraStore(.teg, .@"const"); -pub const EncodedData = dod.EncodedData(Tag, u64); -pub const ExtraIndex = ExtraStore.Index; -pub const Dump = @import("Teg.Dump.zig"); - -pub const Tag = enum(u8) { - /// Either the top level module or a `module` keyword. Contains a list of `.declarations` - /// Every analyzed module gets a `Comp.Namespace` - module, - /// Smallest unit of re-compilation. Every analyzed declaration gets a `Comp.Decl` - declaration, - /// Top-level memory location - variable_declaration, - /// Test declaration - test_declaration, - /// Retrieves a declaration value via it's name. Since declarations are - /// analyzed in isolation of each other, all references to instructions - /// outside of the current declaration must be in the form of this. - load_decl, - /// Blocks are a list of instructions. The block will end with a `.break`, - /// which will assign the block a return value. - block, - /// Ends a block (current or any parent) with a value. - /// `return` expressions are lowered to this. - @"break", - /// Creates a memory cell in the current scope with a starting value. - memory, - /// Re-assign a memory cell with a new ref. - store, - /// Re-assign a global variable decl with a new ref. - store_decl, - /// Re-assign a field with a new ref - store_field, - /// Performs a type coercion, such as in variable declarations. - /// The resulting Air Ref will be guaranteed to be the coerced type. - coercion, - /// Load a parameter - /// if data value is maxInt(u32), the function return type is used - param, - /// Asserts the operand is not void. Used for discard statements. - discard, - /// Asserts the operand is `void`. Used for undiscarded expressions. - ensure_void, - - /// Branch takes a boolean condition and takes either a yes or no path. - @"if", - /// Given a block instruction - @"comptime", - - /// A function expression, with or without captures - func, - - // The following instructions are used to implement `Transform.ResultType` - result_type_argument, - result_type_struct_field, - result_type_tuple_field, - result_type_array_value, - - /// Numeric constant. - number, - /// String literal constant. - string, - - /// Constructs a unique struct type. - struct_decl, - /// Constructs an array type - array_type, - - struct_init, - array_init, - - tuple_init, - tuple_type, - - add, - sub, - mul, - div, - rem, - pow, - @"and", - @"or", - bit_and, - bit_or, - bit_xor, - shl, - shr, - eq, - ne, - lt, - le, - gt, - ge, - - negate, - bool_not, - bit_not, - assert, - field, - index, - call, - type_of, - /// Implements the `@extern` builtin - @"extern", - - /// Implements the `@jsNew` builtin - js_new, - /// Implements the `@import` builtin - import, - /// Implements the `@fetch` builtin - fetch, - /// Implements the `@compileError` builtin - compile_error, - - pub fn Data(comptime tag: Tag) type { - return switch (tag) { - .module, .block, .@"comptime" => PlNode(IndexList), - - .declaration, - .variable_declaration, - .test_declaration, - => PlNode(Decl), - - .coercion => PlNode(Coercion), - .@"break" => PlNode(Break), - .number => PlNode(Float), - .string => PlNode(SlicedString), - .call => PlNode(Call), - .@"if" => PlNode(If), - .store => PlNode(Store), - .store_decl => PlNode(StoreVarDecl), - .store_field => PlNode(StoreField), - .js_new => PlNode(JsNew), - .struct_decl => PlNode(StructDecl), - .struct_init => PlNode(StructInit), - .array_init => PlNode(ArrayInit), - .tuple_type => PlNode(TupleType), - .tuple_init => PlNode(TupleInit), - .func => PlNode(Func), - - .load_decl => StrNode, - .param => DataNode, - - .result_type_argument => ResultTypeArgument, - .result_type_struct_field => ResultTypeStructField, - .result_type_tuple_field => ResultTypeTupleField, - .result_type_array_value => ResultTypeArrayValue, - - // One operand instructions - .memory, - .discard, - .ensure_void, - .negate, - .bool_not, - .bit_not, - .@"extern", - .import, - .fetch, - .compile_error, - .type_of, - .array_type, - .assert, - => RefNode, - - // Two operand instructions - .field, - .index, - .add, - .sub, - .mul, - .div, - .rem, - .pow, - .@"and", - .@"or", - .bit_and, - .bit_or, - .bit_xor, - .shl, - .shr, - .eq, - .ne, - .lt, - .le, - .gt, - .ge, - => PlNode(Bin), - }; - } - - pub fn airTag(comptime tag: Tag) Air.Inst.Tag { - return @field(Air.Inst.Tag, @tagName(tag)); - } -}; - -pub fn PlNode(comptime Payload: type) type { - return struct { - pub const T = Payload; - - src_node: NodeOffset, - extra_index: ExtraIndex(Payload), - }; -} - -pub const RefNode = struct { - src_node: NodeOffset, - operand: Inst.Ref, -}; - -pub const StrNode = struct { - src_node: NodeOffset, - string: NullTerminatedString, -}; - -pub const DataNode = struct { - src_node: NodeOffset, - data: u32, -}; - -pub const Inst = struct { - pub const List = std.MultiArrayList(Inst); - - tag: Tag, - data: EncodedData, - - /// The position of a Teg instruction within the `Teg` instructions array. - pub const Index = enum(u32) { - /// Teg is structured so that the outermost "main" struct of any file - /// is always at index 0. - main_module_inst = 0, - _, - - pub const static_len = @intFromEnum(Ref.empty_tuple) + 1; - - pub fn toRef(i: Index) Inst.Ref { - return @enumFromInt(static_len + @intFromEnum(i)); - } - - pub fn toOptional(i: Index) Optional { - return @enumFromInt(@intFromEnum(i)); - } - - pub fn op(i: Index, operand: Report.Operand) InstOp { - return .{ .index = i, .op = operand }; - } - - pub const Optional = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub fn unwrap(oi: Optional) ?Index { - return if (oi == .none) null else @enumFromInt(@intFromEnum(oi)); - } - }; - }; - - /// A reference to an instruction, or to a static InternPool value. - /// - /// If the integer tag value is < InternPool.static_len, then it - /// corresponds to a Value. Otherwise, it is an instruction index. - pub const Ref = enum(u32) { - opaque_type, - bool_type, - int32_type, - float_type, - string_type, - never_type, - type_type, - numeric_literal_type, - numeric_literal_array_type, - never_array_type, - empty_tuple_type, - - void, - bool_true, - bool_false, - int32_zero, - float_zero, - nan, - empty_string, - empty_array, - empty_tuple, - _, - - pub const Decoded = union(enum) { - value: InternPool.Value, - index: Teg.Inst.Index, - }; - - pub fn decode(ref: Ref) Decoded { - const ref_int = @intFromEnum(ref); - return if (ref_int >= Index.static_len) - .{ .index = @enumFromInt(ref_int - Index.static_len) } - else - .{ .value = @enumFromInt(ref_int) }; - } - - pub fn index(ref: Ref) ?Index { - const ref_int = @intFromEnum(ref); - if (ref_int >= Index.static_len) { - return @enumFromInt(ref_int - Index.static_len); - } else { - return null; - } - } - - pub fn value(inst: Ref) ?InternPool.Value { - const ref_int = @intFromEnum(inst); - - // TODO: is this > or >= - if (ref_int > Index.static_len) - return null; - - return @enumFromInt(ref_int); - } - - pub fn toOptional(ref: Ref) Optional { - return @enumFromInt(@intFromEnum(ref)); - } - - pub const Optional = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub fn unwrap(opt: Optional) ?Ref { - return if (opt != .none) @enumFromInt(@intFromEnum(opt)) else null; - } - }; - }; -}; - -pub const Bin = struct { - lhs: Inst.Ref, - rhs: Inst.Ref, -}; - -pub const Store = struct { - memory: Inst.Index, - operand: Inst.Ref, -}; - -pub const StoreVarDecl = struct { - var_decl: NullTerminatedString, - operand: Inst.Ref, -}; - -pub const StoreField = struct { - base: Inst.Ref, - field: Inst.Ref, - operand: Inst.Ref, -}; - -pub const Coercion = struct { - type: Inst.Ref, - operand: Inst.Ref, -}; - -pub const Break = struct { - block: Inst.Index, - operand: Inst.Ref, -}; - -pub const Call = struct { - base: Inst.Ref, - args: RefList, -}; - -pub const If = struct { - cond: Inst.Ref, - yes: Inst.Ref, - no: Inst.Ref, -}; - -pub const JsNew = struct { - base: Inst.Ref, - args: RefList, -}; - -/// Index into extra which contains `field_len * 2` u32s. The first half -/// are the keys (NullTerminatedString) and the second half are field types -/// (Ref). This basically is like a specialized MultiArrayList. -pub const StructDecl = struct { - payload_index: u32, - field_len: u32, - - pub fn keys(decl: StructDecl, teg: *const Teg) []const NullTerminatedString { - return @ptrCast( - teg.extra.list.items[decl.payload_index..][0..decl.field_len], - ); - } - - pub fn types(decl: StructDecl, teg: *const Teg) []const Inst.Ref { - return @ptrCast( - teg.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len], - ); - } -}; - -/// This memory is laid out with the same strategy described in StructDecl -pub const StructInit = struct { - base_type: Inst.Ref, - payload_index: u32, - field_len: u32, - - pub fn keys(decl: StructInit, teg: *const Teg) []const NullTerminatedString { - return @ptrCast( - teg.extra.list.items[decl.payload_index..][0..decl.field_len], - ); - } - - pub fn inits(decl: StructInit, teg: *const Teg) []const Inst.Ref { - return @ptrCast( - teg.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len], - ); - } -}; - -pub const ArrayInit = struct { - /// Optionally provide a result type for more reliable type coercion. - /// If specified, this must be an array type. Otherwise, the first - /// element's type is used. - type_hint: Inst.Ref.Optional, - /// The items in the array - list: RefList, -}; - -pub const TupleType = struct { - /// The items in the tuple - items: RefList, -}; - -pub const TupleInit = struct { - /// Optionally provide a result type for more reliable type coercion. - /// If specified, this must be an array type. Otherwise, the first - /// element's type is used. - type_hint: Inst.Ref.Optional, - /// The items in the tuple - items: RefList, -}; - -pub const Decl = struct { - name: NullTerminatedString, - hash: Cache.BinDigest, - instruction_count: u32, - body: Inst.Ref, -}; - -pub const Func = struct { - params: OptionalRefList, - return_type: Inst.Ref.Optional, - body: Inst.Index, - captures: IndexList, - instruction_count: u32, -}; - -pub const ResultTypeArgument = struct { - /// The VALUE that is going to be called. - call_base: Inst.Ref, - /// Which argument - argument: u32, -}; - -pub const ResultTypeStructField = struct { - /// The type of the struct itself, - type: Inst.Ref, - /// Which field - field: NullTerminatedString, -}; - -pub const ResultTypeTupleField = struct { - /// The type of the tuple itself, - type: Inst.Ref, - /// Which item - item: u32, -}; - -pub const ResultTypeArrayValue = struct { - /// The type of the array itself, - type: Inst.Ref, - unused: u32 = 0, -}; - -// TODO: make this an enum -const NodeOffset = i32; - -// TODO: remove InstOp. LazyNode should not have a decl -pub const InstOp = struct { - index: Inst.Index, - op: Report.Operand, - - pub fn lazyNode(inst: InstOp, decl: Comp.Decl.Index) LazyNode { - return .{ - .inst = inst.index, - .operand = inst.op, - .decl = decl, - }; - } -}; - -pub const NullTerminatedString = enum(u32) { - empty = 0, - _, - - pub fn slice(str: NullTerminatedString, string_bytes: []const u8) [:0]const u8 { - if (str == .empty) return ""; - const sub = string_bytes[@intFromEnum(str)..]; - const i = mem.indexOfScalar(u8, sub, 0) orelse unreachable; - - return sub[0..i :0]; - } -}; - -pub const SlicedString = struct { - start: u32, - len: u32, - - pub fn slice(str: SlicedString, string_bytes: []const u8) []const u8 { - return string_bytes[str.start..][0..str.len]; - } -}; - -// TODO: Make the next three structs using a generic comptime thing - -pub const RefList = struct { - start: u32, - len: u32, - - pub fn slice(self: RefList, teg: *const Teg) []const Inst.Ref { - return @ptrCast(teg.extra.list.items[self.start..][0..self.len]); - } -}; - -pub const OptionalRefList = struct { - start: u32, - len: u32, - - pub fn slice(self: OptionalRefList, teg: *const Teg) []const Inst.Ref.Optional { - return @ptrCast(teg.extra.list.items[self.start..][0..self.len]); - } -}; - -pub const IndexList = struct { - start: u32, - len: u32, - - pub fn slice(self: IndexList, teg: *const Teg) []const Inst.Index { - return @ptrCast(teg.extra.list.items[self.start..][0..self.len]); - } -}; - -pub const NullTermStringList = struct { - start: u32, - len: u32, - - pub fn slice(self: IndexList, teg: *const Teg) []const NullTerminatedString { - return @ptrCast(teg.extra.list.items[self.start..][0..self.len]); - } -}; - -pub fn deinit(teg: *Teg, gpa: std.mem.Allocator) void { - gpa.free(teg.string_bytes); - teg.extra.deinit(gpa); - teg.insts.deinit(gpa); - teg.report.deinit(gpa); - teg.* = undefined; -} - -pub fn instTag(teg: *const Teg, index: Inst.Index) Teg.Tag { - return teg.insts.items(.tag)[@intFromEnum(index)]; -} - -pub fn instEncodedData(teg: *const Teg, index: Inst.Index) EncodedData { - return teg.insts.items(.data)[@intFromEnum(index)]; -} - -pub fn instData(teg: *const Teg, comptime tag: Teg.Tag, index: Inst.Index) tag.Data() { - return teg.instEncodedData(index).decode(tag, teg.instTag(index)); -} - -pub fn instDataUnsafe(teg: *const Teg, comptime T: type, index: Inst.Index) T { - return teg.instEncodedData(index).decodeUnsafe(T, teg.instTag(index)); -} - -pub fn instPayload(teg: *const Teg, comptime tag: Teg.Tag, index: Inst.Index) tag.Data().T { - return teg.instData(tag, index).extra_index.get(teg); -} - -pub fn instNodeOffset(teg: *const Teg, index: Inst.Index) i32 { - return switch (teg.instTag(index)) { - // these nodes never get errors, but Teg.Dump will print these regardless - // TODO: - .declaration, - .result_type_argument, - .result_type_struct_field, - .result_type_tuple_field, - .result_type_array_value, - => 0, - - inline else => |tag| teg.instData(tag, index).src_node, - }; -} - -pub fn blockInstructions(teg: *const Teg, index: Inst.Index) []const Inst.Index { - return teg.instData(.block, index).extra_index.get(teg).slice(teg); -} - -pub fn fullFuncDecl(teg: *const Teg, index: Inst.Index) struct { Teg.Decl, Teg.DeclFn } { - const decl, var trail = teg.instData(.declaration, index).extra_index.getTrail(teg); - assert(decl.kind == .function); - const decl_fn = trail.read(&teg.extra, Teg.DeclFn); - return .{ decl, decl_fn }; -} - -pub fn fullDecl(teg: *const Teg, index: Inst.Index) Decl { - const decl = teg.instEncodedData(index).decodeUnsafe(PlNode(Decl), teg.instTag(index)).extra_index.get(teg); - return .{ - .name = decl.name, - .hash = decl.hash, - .instruction_count = decl.instruction_count, - .body = decl.body, - }; -} - -pub fn declData(teg: *const Teg, index: Inst.Index) Teg.Decl { - return teg.instDataUnsafe(PlNode(Decl), index).extra_index.get(teg); -} - -pub fn moduleDeclarations(teg: *const Teg, module_index: Inst.Index) []const Inst.Index { - const module = teg.instData(.module, module_index); - const module_data: IndexList = module.extra_index.get(teg); - const slice = module_data.slice(teg); - if (core.runtime_safety) { - for (slice) |item| { - switch (teg.instTag(item)) { - .declaration, .variable_declaration, .test_declaration => {}, - else => { - std.debug.panic("\ngot unknown tag {s}\n", .{@tagName(teg.instTag(item))}); - }, - } - } - } - return slice; -} - -pub fn rootFunction(teg: *const Teg) Inst.Index { - const decls = teg.moduleDeclarations(.main_module_inst); - const root_fn = decls[decls.len - 1]; - assert(teg.declName(root_fn).len == 0); - return root_fn; -} - -pub fn declName(teg: *const Teg, decl_index: Inst.Index) []const u8 { - const decl = teg.instDataUnsafe(PlNode(Decl), decl_index); - const decl_data: Decl = decl.extra_index.get(teg); - return decl_data.name.slice(teg.string_bytes); -} - -pub fn dumpToWriter(teg: Teg, writer: anytype, tty: ?std.io.tty.Config) !void { - try writer.print("{}", .{Dump{ .teg = teg, .tty = tty }}); -} - -/// Used for generating decl hashes -fn isolatedRefValue(decl_index: Inst.Index, ref_to_hash: Inst.Ref) i32 { - return switch (ref_to_hash.decode()) { - .value => |value| -@as(i32, @intCast(@intFromEnum(value))), - .index => |index| @intCast(@intFromEnum(index) - @intFromEnum(decl_index)), - }; -} - -/// Used for generating decl hashes -fn isolatedIndexValue(decl_index: Inst.Index, index_to_hash: Inst.Index) u32 { - return @intFromEnum(index_to_hash) - @intFromEnum(decl_index); -} - -pub fn writeToHasher( - teg: *const Teg, - hasher: *HashHelper, - tag_list: []Tag, - data_list: []EncodedData, - decl_index: Inst.Index, -) void { - // Add all of the tags - hasher.addBytes(std.mem.sliceAsBytes(tag_list)); - - // Add all of the data - for (tag_list, data_list) |tag, data| { - - // This should roughly line up with Teg.Tag.Data - switch (tag) { - // PlNode(IndexList), - .module, - .block, - .@"comptime", - => { - const pl_node = data.decodeUnsafe(PlNode(IndexList), tag); - hasher.add(pl_node.src_node); - writeIndexListToHasher(hasher, pl_node.extra_index.get(teg).slice(teg), decl_index); - }, - - // PlNode - inline .declaration, - .variable_declaration, - .test_declaration, - .coercion, - .@"break", - .number, - .string, - .call, - .@"if", - .store, - .js_new, - .struct_decl, - .struct_init, - .array_init, - .tuple_init, - .tuple_type, - .func, - .store_decl, - .store_field, - => |pl_node_tag| { - const pl_node = data.decode(pl_node_tag, tag); - hasher.add(pl_node.src_node); - teg.writePlNodeInnerToHasher(hasher, pl_node_tag.Data().T, pl_node.extra_index.get(teg), decl_index); - }, - // StrNode - .load_decl => { - const str_node = data.decodeUnsafe(StrNode, tag); - hasher.add(str_node.src_node); - hasher.addBytes(str_node.string.slice(teg.string_bytes)); - }, - // DataNode - .param => { - const data_node = data.decodeUnsafe(DataNode, tag); - hasher.add(data_node.src_node); - hasher.add(data_node.data); - }, - // RefNode - .memory, - .discard, - .ensure_void, - .negate, - .bool_not, - .bit_not, - .@"extern", - .import, - .fetch, - .compile_error, - .type_of, - .array_type, - .assert, - => { - const ref_node = data.decodeUnsafe(RefNode, tag); - hasher.add(ref_node.src_node); - hasher.add(isolatedRefValue(decl_index, ref_node.operand)); - }, - // PlNode(Bin) - .field, - .index, - .add, - .sub, - .mul, - .div, - .rem, - .pow, - .@"and", - .@"or", - .bit_and, - .bit_or, - .bit_xor, - .shl, - .shr, - .eq, - .ne, - .lt, - .le, - .gt, - .ge, - => { - const pl_node = data.decodeUnsafe(PlNode(Bin), tag); - const bin = pl_node.extra_index.get(teg); - hasher.add(isolatedRefValue(decl_index, bin.lhs)); - hasher.add(isolatedRefValue(decl_index, bin.rhs)); - }, - // Result Types - .result_type_argument => { - const arg = data.decodeUnsafe(ResultTypeArgument, tag); - hasher.add(isolatedRefValue(decl_index, arg.call_base)); - hasher.add(arg.argument); - }, - .result_type_struct_field => { - const struct_field = data.decodeUnsafe(ResultTypeStructField, tag); - hasher.add(isolatedRefValue(decl_index, struct_field.type)); - hasher.addBytes(struct_field.field.slice(teg.string_bytes)); - }, - .result_type_tuple_field => { - const tuple_field = data.decodeUnsafe(ResultTypeTupleField, tag); - hasher.add(isolatedRefValue(decl_index, tuple_field.type)); - hasher.add(tuple_field.item); - }, - .result_type_array_value => { - const array_value = data.decodeUnsafe(ResultTypeArrayValue, tag); - hasher.add(isolatedRefValue(decl_index, array_value.type)); - }, - } - } -} - -fn writeIndexListToHasher( - hasher: *HashHelper, - index_list: []const Inst.Index, - decl_index: Inst.Index, -) void { - hasher.add(index_list.len); - for (index_list) |idx| { - hasher.add(isolatedIndexValue(decl_index, idx)); - } -} - -fn writeRefListToHasher( - hasher: *HashHelper, - decl_index: Inst.Index, - index_list: []const Inst.Ref, -) void { - hasher.add(index_list.len); - for (index_list) |idx| { - hasher.add(isolatedRefValue(decl_index, idx)); - } -} - -fn writePlNodeInnerToHasher( - teg: *const Teg, - hasher: *HashHelper, - comptime T: type, - data: T, - decl_index: Inst.Index, -) void { - switch (T) { - Decl => { - hasher.addBytes(&data.hash); - hasher.addBytes(data.name.slice(teg.string_bytes)); - }, - Float => { - const as_bytes: [2]u32 = .{ - data.low, - data.high, - }; - hasher.add(@as(u64, @bitCast(as_bytes))); - }, - SlicedString => { - hasher.addBytes(data.slice(teg.string_bytes)); - }, - Coercion => { - hasher.add(isolatedRefValue(decl_index, data.type)); - hasher.add(isolatedRefValue(decl_index, data.operand)); - }, - Break => { - hasher.add(isolatedIndexValue(decl_index, data.block)); - hasher.add(isolatedRefValue(decl_index, data.operand)); - }, - Call => { - hasher.add(isolatedRefValue(decl_index, data.base)); - writeRefListToHasher(hasher, decl_index, data.args.slice(teg)); - }, - If => { - hasher.add(isolatedRefValue(decl_index, data.cond)); - hasher.add(isolatedRefValue(decl_index, data.yes)); - hasher.add(isolatedRefValue(decl_index, data.no)); - }, - Store => { - hasher.add(isolatedIndexValue(decl_index, data.memory)); - hasher.add(isolatedRefValue(decl_index, data.operand)); - }, - StoreField => { - hasher.add(isolatedRefValue(decl_index, data.base)); - hasher.add(isolatedRefValue(decl_index, data.field)); - hasher.add(isolatedRefValue(decl_index, data.operand)); - }, - StoreVarDecl => { - hasher.addBytes(data.var_decl.slice(teg.string_bytes)); - hasher.add(isolatedRefValue(decl_index, data.operand)); - }, - JsNew => { - hasher.add(isolatedRefValue(decl_index, data.base)); - writeRefListToHasher(hasher, decl_index, data.args.slice(teg)); - }, - StructDecl => { - hasher.add(data.field_len); - for (data.keys(teg), data.types(teg)) |key, ty| { - hasher.addBytes(key.slice(teg.string_bytes)); - hasher.add(isolatedRefValue(decl_index, ty)); - } - }, - StructInit => { - hasher.add(isolatedRefValue(decl_index, data.base_type)); - hasher.add(data.field_len); - for (data.keys(teg), data.inits(teg)) |key, init| { - hasher.addBytes(key.slice(teg.string_bytes)); - hasher.add(isolatedRefValue(decl_index, init)); - } - }, - ArrayInit => { - if (data.type_hint.unwrap()) |type_hint| - hasher.add(isolatedRefValue(decl_index, type_hint)) - else - hasher.add(@as(i32, std.math.maxInt(i32))); - writeRefListToHasher(hasher, decl_index, data.list.slice(teg)); - }, - Func => { - for (data.params.slice(teg)) |optional| { - if (optional.unwrap()) |item| - hasher.add(isolatedRefValue(decl_index, item)); - } - - if (data.return_type.unwrap()) |type_hint| - hasher.add(isolatedRefValue(decl_index, type_hint)) - else - hasher.add(@as(i32, std.math.maxInt(i32))); - - hasher.add(isolatedIndexValue(decl_index, data.body)); - hasher.add(data.instruction_count); - }, - TupleType => { - hasher.add(data.items.len); - for (data.items.slice(teg)) |item| { - hasher.add(isolatedRefValue(decl_index, item)); - } - }, - TupleInit => { - if (data.type_hint.unwrap()) |type_hint| - hasher.add(isolatedRefValue(decl_index, type_hint)) - else - hasher.add(@as(i32, std.math.maxInt(i32))); - writeRefListToHasher(hasher, decl_index, data.items.slice(teg)); - }, - else => @compileError("hash not implemented for " ++ @typeName(T)), - } -} - -pub const SerializedHeader = extern struct { - const version = @import("config").version_string ++ "\x00"; - - instruction_len: u32, - string_bytes: u32, - extra_len: u32, - report_err_len: u32, - report_warn_len: u32, -}; - -pub fn serialize(teg: Teg, target: core.SerializeTarget) !void { - const header: SerializedHeader = .{ - .instruction_len = @intCast(teg.insts.len), - .string_bytes = @intCast(teg.string_bytes.len), - .extra_len = @intCast(teg.extra.list.items.len), - .report_err_len = @intCast(teg.report.errors.len), - .report_warn_len = @intCast(teg.report.warnings.len), - }; - - var iovecs = [_]std.posix.iovec_const{ - core.iovecConst(SerializedHeader.version), - core.iovecConst(std.mem.asBytes(&header)), - core.iovecConst(std.mem.sliceAsBytes(teg.insts.items(.tag))), - core.iovecConst(std.mem.sliceAsBytes(teg.insts.items(.data))), - core.iovecConst(teg.string_bytes), - core.iovecConst(std.mem.sliceAsBytes(teg.extra.list.items)), - core.iovecConst(std.mem.sliceAsBytes(teg.report.errors)), - core.iovecConst(std.mem.sliceAsBytes(teg.report.warnings)), - }; - try target.writev(&iovecs); -} - -const Teg = @This(); - -const std = @import("std"); -const mem = std.mem; -const assert = std.debug.assert; -const HashHelper = std.Build.Cache.HashHelper; - -const core = @import("mod.zig"); -const Cache = core.Cache; - -const Ast = core.Ast; -const Unused = Ast.Unused; -const NodeIndex = Ast.NodeIndex; -const TokenIndex = Ast.TokenIndex; -const OptionalNodeIndex = Ast.OptionalNodeIndex; -const OptionalTokenIndex = Ast.OptionalTokenIndex; -const OptionalExtraIndex = Ast.OptionalExtraIndex; - -const dod = core.array; -const Float = dod.Float; - -const InternPool = core.InternPool; - -const Air = core.Air; - -const Comp = core.Comp; - -const Report = core.Report; -const LazyNode = Report.LazyNode; diff --git a/src/core/Transform.zig b/src/core/Transform.zig index ccead19a922ce962066ccf642886024f01fde15f..a2c2d8b1f9fe7b70ef9f07c5ffec1593ad00791c 100644 --- a/src/core/Transform.zig +++ b/src/core/Transform.zig @@ -1,5 +1,5 @@ -//! Transform pass converts an Ast into a high-level/untyped instruction format. -//! See Teg.zig for more details about the IR. Transforming handles: +//! Transform pass converts an Ast into a Hir. See Hir.zig for more details +//! about the resulting data structure. Transforming handles: //! //! - Flattening nested expressions into instruction sequences. //! - Resolving all local variables into reference or declaration names. @@ -38,7 +38,7 @@ pub const Error = error{ TransformFail, } || Allocator.Error; -pub fn transform(ast: Ast, gpa: Allocator, fail_report: *Report) Error!Teg { +pub fn transform(ast: Ast, gpa: Allocator, fail_report: *Report) Error!Hir { const trace = tracer.trace(@src()); defer trace.end(); @@ -117,16 +117,16 @@ const ResultType = union(enum) { /// The expression will be cast to the type of this ref type_of: Inst.Ref, /// The expression is an argument to a function. - argument: Teg.ResultTypeArgument, + argument: Hir.ResultTypeArgument, /// The expression is a field in a struct initialization - struct_field: Teg.ResultTypeStructField, + struct_field: Hir.ResultTypeStructField, /// The expression is a field in a tuple initialization - tuple_field: Teg.ResultTypeTupleField, + tuple_field: Hir.ResultTypeTupleField, /// The expression is a field in an array initialization array_value: Inst.Ref, // /// The expression is unknown field - // field: Teg.ResultTypeField, + // field: Hir.ResultTypeField, /// This will return an instruction pointing to the the type pub fn consume(result: *ResultType, t: *Transform) !?Inst.Ref { @@ -259,8 +259,8 @@ const Scope = struct { /// Sema requires that function expressions must know their up-front captures, /// so it can classify a function as comptime or runtime. const CaptureScope = struct { - start_index: Teg.Inst.Index, - symbol_uses: std.AutoArrayHashMapUnmanaged(Teg.Inst.Index, void) = .{}, + start_index: Hir.Inst.Index, + symbol_uses: std.AutoArrayHashMapUnmanaged(Hir.Inst.Index, void) = .{}, fn deinit(s: *CaptureScope, t: *Transform) void { s.symbol_uses.deinit(t.gpa); @@ -535,7 +535,7 @@ fn transformDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Alloc var hasher: std.Build.Cache.HashHelper = .{ .hasher = core.Cache.hasher_init }; hasher.addBytes(id); - const temp_teg: Teg = .{ + const temp_teg: Hir = .{ .insts = t.insts.slice(), .extra = t.extra.constant(), .string_bytes = t.string_bytes.items, @@ -547,7 +547,7 @@ fn transformDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Alloc temp_teg.writeToHasher(&hasher, tag_list, data_list, decl_index); - const decl_extra = try t.extra.append(t.gpa, Teg.Decl, .{ + const decl_extra = try t.extra.append(t.gpa, Hir.Decl, .{ .name = name, .hash = hasher.hasher.finalResult(), .instruction_count = inst_count, @@ -579,7 +579,7 @@ fn transformTestDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A var hasher: std.Build.Cache.HashHelper = .{ .hasher = core.Cache.hasher_init }; - const temp_teg: Teg = .{ + const temp_teg: Hir = .{ .insts = t.insts.slice(), .extra = t.extra.constant(), .string_bytes = t.string_bytes.items, @@ -591,7 +591,7 @@ fn transformTestDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A temp_teg.writeToHasher(&hasher, tag_list, data_list, decl_index); - const decl_extra = try t.extra.append(t.gpa, Teg.Decl, .{ + const decl_extra = try t.extra.append(t.gpa, Hir.Decl, .{ .name = .empty, .hash = hasher.hasher.finalResult(), .instruction_count = inst_count, @@ -738,7 +738,7 @@ fn transformToBlock( .none, .used => result, else => try t.addRef(.coercion, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Coercion, .{ .type = try result_type_ptr.consume(t) orelse unreachable, // `.none` already handled .operand = result, @@ -748,7 +748,7 @@ fn transformToBlock( _ = try t.addIndex(.@"break", .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Break, .{ .block = ret_block, .operand = coerced_result, }), @@ -864,7 +864,7 @@ pub const Builtin = enum { return try t.addRef(.js_new, .{ .src_node = decl.nodeOffset(args[0]), - .extra_index = try t.extra.append(t.gpa, Teg.JsNew, .{ + .extra_index = try t.extra.append(t.gpa, Hir.JsNew, .{ .base = base, .args = args_list, }), @@ -987,7 +987,7 @@ fn transformExpression( return try t.addRef(.coercion, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Coercion, .{ .type = type_ref.?, .operand = data_ref, }), @@ -1075,7 +1075,7 @@ fn transformExpression( // compound compute + assign operation. const is_assign = comptime tag.isAssignBin(); const teg_tag = @field( - Teg.Tag, + Hir.Tag, if (is_assign) @tagName(tag)[0 .. @tagName(tag).len - "_assign".len] else @@ -1096,7 +1096,7 @@ fn transformExpression( const compute_inst = try t.addRef(teg_tag, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Bin, .{ .lhs = if (is_assign) try lhs.toRef(t) else lhs, .rhs = rhs, }), @@ -1116,7 +1116,7 @@ fn transformExpression( .assert, => |tag| { const ast_un = t.ast.nodeData(node).un; - const teg_tag = @field(Teg.Tag, @tagName(tag)); + const teg_tag = @field(Hir.Tag, @tagName(tag)); var operand_type: ResultType = switch (tag) { .negate => .none, // any numeric type allowed @@ -1178,7 +1178,7 @@ fn transformExpression( if (inner_expr) |out| { _ = try t.addRef(.@"break", .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Break, .{ .block = block, .operand = out, }), @@ -1276,7 +1276,7 @@ fn transformExpression( const call_inst = try t.addRef(.call, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Call, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Call, .{ .base = base, .args = args_list, }), @@ -1319,7 +1319,7 @@ fn transformExpression( return try t.addRef(.field, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Bin, .{ .lhs = base, .rhs = rhs, }), @@ -1339,7 +1339,7 @@ fn transformExpression( return try t.addRef(.field, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Bin, .{ .lhs = lhs, .rhs = rhs, }), @@ -1370,7 +1370,7 @@ fn transformExpression( return try t.addRef(.@"if", .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.If, .{ + .extra_index = try t.extra.append(t.gpa, Hir.If, .{ .cond = cond, .yes = yes, .no = no, @@ -1399,7 +1399,7 @@ fn transformExpression( _ = try t.addIndex(.@"break", .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Break, .{ .block = target.block_index, .operand = operand, }), @@ -1431,7 +1431,7 @@ fn transformExpression( .none, .used => operand, else => try t.addRef(.coercion, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Coercion, .{ .type = try break_result.consume(t) orelse unreachable, // `.none` already handled .operand = operand, @@ -1441,7 +1441,7 @@ fn transformExpression( _ = try t.addIndex(.@"break", .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Break, .{ .block = try block.getInst(t), .operand = coerced_operand, }), @@ -1474,7 +1474,7 @@ fn transformExpression( return try t.addRef(.struct_decl, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.StructDecl, .{ + .extra_index = try t.extra.append(t.gpa, Hir.StructDecl, .{ .payload_index = payload_index, .field_len = @intCast(struct_node.fields.len), }), @@ -1524,7 +1524,7 @@ fn transformExpression( return try t.addRef(.struct_init, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.StructInit, .{ + .extra_index = try t.extra.append(t.gpa, Hir.StructInit, .{ .base_type = base_type, .payload_index = payload_index, .field_len = @intCast(struct_node.fields.len), @@ -1549,7 +1549,7 @@ fn transformExpression( return try t.addRef(.array_init, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.ArrayInit, .{ + .extra_index = try t.extra.append(t.gpa, Hir.ArrayInit, .{ .type_hint = if (array_type) |ty| ty.toOptional() else .none, .list = items, }), @@ -1567,7 +1567,7 @@ fn transformExpression( return try t.addRef(.tuple_init, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.TupleInit, .{ + .extra_index = try t.extra.append(t.gpa, Hir.TupleInit, .{ .type_hint = .none, .items = items, }), @@ -1581,7 +1581,7 @@ fn transformExpression( return try t.addRef(.tuple_type, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.TupleType, .{ + .extra_index = try t.extra.append(t.gpa, Hir.TupleType, .{ .items = items, }), }); @@ -1645,7 +1645,7 @@ fn transformExpression( return try t.addRef(.func, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Func, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Func, .{ .params = param_list, .return_type = if (return_type) |ref| ref.toOptional() else .none, .body = body, @@ -1862,7 +1862,7 @@ const AssignmentTarget = union(enum) { }), .field => |field| try t.addRef(.field, .{ .src_node = field.src_node, - .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Bin, .{ .lhs = field.base, .rhs = field.field, }), @@ -1874,21 +1874,21 @@ const AssignmentTarget = union(enum) { return switch (target) { .memory => |memory| try t.addRef(.store, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Store, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Store, .{ .memory = memory, .operand = rhs, }), }), .variable_declaration => |var_decl| try t.addRef(.store_decl, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.StoreVarDecl, .{ + .extra_index = try t.extra.append(t.gpa, Hir.StoreVarDecl, .{ .var_decl = var_decl.string, .operand = rhs, }), }), .field => |field| try t.addRef(.store_field, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.StoreField, .{ + .extra_index = try t.extra.append(t.gpa, Hir.StoreField, .{ .base = field.base, .field = field.field, .operand = rhs, @@ -1998,7 +1998,7 @@ fn transformBlockStatement( if (value_result != .used) { break :coerced try t.addRef(.coercion, .{ .src_node = ctx.nodeOffset(node), - .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{ + .extra_index = try t.extra.append(t.gpa, Hir.Coercion, .{ .type = ref, .operand = data_ref, }), @@ -2110,7 +2110,7 @@ fn transformBlockStatement( } } -fn addUntracked(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index { +fn addUntracked(t: *Transform, comptime tag: Hir.Tag, data: tag.Data()) !Inst.Index { try t.insts.append(t.gpa, .{ .tag = tag, .data = .encode(tag, data), @@ -2118,13 +2118,13 @@ fn addUntracked(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.In return @enumFromInt(t.insts.len - 1); } -fn addIndex(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index { +fn addIndex(t: *Transform, comptime tag: Hir.Tag, data: tag.Data()) !Inst.Index { const index = try t.addUntracked(tag, data); try t.scratch.add(t.arena, Inst.Index, index); return index; } -fn addRef(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Ref { +fn addRef(t: *Transform, comptime tag: Hir.Tag, data: tag.Data()) !Inst.Ref { return (try t.addIndex(tag, data)).toRef(); } @@ -2333,18 +2333,18 @@ const Allocator = mem.Allocator; const assert = std.debug.assert; const core = @import("mod.zig"); -const Teg = core.Teg; -const Inst = Teg.Inst; -const NullTerminatedString = Teg.NullTerminatedString; -const SlicedString = Teg.SlicedString; -const RefList = Teg.RefList; -const IndexList = Teg.IndexList; -const OptionalRefList = Teg.OptionalRefList; -const EncodedData = Teg.EncodedData; -const ExtraIndex = Teg.ExtraIndex; +const Hir = core.Hir; +const Inst = Hir.Inst; +const NullTerminatedString = Hir.NullTerminatedString; +const SlicedString = Hir.SlicedString; +const RefList = Hir.RefList; +const IndexList = Hir.IndexList; +const OptionalRefList = Hir.OptionalRefList; +const EncodedData = Hir.EncodedData; +const ExtraIndex = Hir.ExtraIndex; const array = core.array; -const ExtraStore = array.ExtraStore(.teg, .@"var"); +const ExtraStore = array.ExtraStore(.hir, .@"var"); const Float = array.Float; const Tokenizer = core.Tokenizer; diff --git a/src/core/array.zig b/src/core/array.zig index 2afb7fd636bb3a6a212280b23aaaf5257722b0bb..b307f89555c421e4527f60e0d654f9df52474e7e 100644 --- a/src/core/array.zig +++ b/src/core/array.zig @@ -77,7 +77,7 @@ fn getAsU32(items: []const u32, comptime T: type, index: usize) T { pub const StoreKind = enum { intern_pool, - teg, + hir, air, js_tree, diff --git a/src/core/mod.zig b/src/core/mod.zig index 5329216e139b9bcb06515327656c5a6832e3401e..2967e13ca05552f5d8f70f80d8741cde89ae548f 100644 --- a/src/core/mod.zig +++ b/src/core/mod.zig @@ -17,7 +17,7 @@ pub const U32Stack = Stack(u32); pub const StoredTrace = @import("StoredTrace.zig"); pub const DebugStoredTrace = StoredTrace.DebugStoredTrace; pub const Queue = @import("queue.zig").Queue; -pub const Teg = @import("Teg.zig"); +pub const Hir = @import("Hir.zig"); pub const Tokenizer = @import("Tokenizer.zig"); pub const Transform = @import("Transform.zig"); pub const unicode_props = @import("unicode_props.zig"); diff --git a/src/main.zig b/src/main.zig index 531b4dc0d1a694c3b30fd0b916c6a2ccd97b3144..5a9f96bbfec1d47ce811ad1172159b375c3411e4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -299,7 +299,7 @@ fn cmdBuildOrRun(ctx: Context, comptime mode: enum { build, run }) !void { const main_file = try comp.getFile(file_path); var fail_report: Report = undefined; - const teg = main_file.getTeg(&comp, &fail_report) catch |err| switch (err) { + const hir = main_file.getHir(&comp, &fail_report) catch |err| switch (err) { error.TransformFail => { std.debug.print("Transform failed:\n", .{}); try fail_report.dump(.initComp(&comp, main_file), comp.out); @@ -321,7 +321,7 @@ fn cmdBuildOrRun(ctx: Context, comptime mode: enum { build, run }) !void { if (comp.options.dump_ir) { const stderr = std.io.getStdErr().writer(); - try teg.dumpToWriter(stderr, null); + try hir.dumpToWriter(stderr, null); } const entry_point = comp.enqueueEntryModule(main_file) catch |err| switch (err) { diff --git a/src/wasm/enum_codegen.zig b/src/wasm/enum_codegen.zig index a0e0b1928cf200e63075dd9225cd5e98946693e7..4a8a067c343c5bbeed928819b65cd29c744d2d85 100644 --- a/src/wasm/enum_codegen.zig +++ b/src/wasm/enum_codegen.zig @@ -6,8 +6,8 @@ pub fn main() !void { inline for (.{ .{ "TokenTag", Tokenizer.Token.Tag }, .{ "NodeTag", Ast.Node.Tag }, - .{ "TegInstTag", Teg.Tag }, - .{ "TegInstRef", Teg.Inst.Ref }, + .{ "HirInstTag", Hir.Tag }, + .{ "HirInstRef", Hir.Inst.Ref }, }) |decl| { const name, const enum_type = decl; @@ -24,4 +24,4 @@ const std = @import("std"); const core = @import("core"); const Tokenizer = core.Tokenizer; const Ast = core.Ast; -const Teg = core.Teg; +const Hir = core.Hir; diff --git a/test/compiler_tests.zig b/test/compiler_tests.zig index c38819efb797a6a9ed5d841aea241aaadc634da3..5231041adac86fca5ef0380ae640adc968374f6a 100644 --- a/test/compiler_tests.zig +++ b/test/compiler_tests.zig @@ -98,7 +98,7 @@ fn runTestCase( const main_file = try comp.getFile(file_path); var fail_report: core.Report = undefined; - const teg = main_file.getTeg(&comp, &fail_report) catch |err| switch (err) { + const hir = main_file.getHir(&comp, &fail_report) catch |err| switch (err) { error.TransformFail => { try out.print("Transform failed:\n", .{}); try fail_report.dump(.{ .comp = .{ .comp = &comp, .file = main_file } }, comp.out); @@ -112,7 +112,7 @@ fn runTestCase( }; if (case_options.dump_ir) { - try teg.dumpToWriter(out, .escape_codes); + try hir.dumpToWriter(out, .escape_codes); } const entry_point = try comp.enqueueEntryModule(main_file); @@ -121,8 +121,8 @@ fn runTestCase( if (comp.temp_flag_had_errors) return error.SemaErrors; - if (teg.report.warnings.len > 0) - return error.TegWarnings; + if (hir.report.warnings.len > 0) + return error.HirWarnings; var js_out: std.ArrayListUnmanaged(u8) = .{}; defer js_out.deinit(alloc); @@ -227,7 +227,7 @@ pub fn runTestCaseWrap( const stack = switch (err) { error.TransformFail, error.ParseFail, - error.TegWarnings, + error.HirWarnings, error.SemaErrors, error.AnalysisFail, => core.StoredTrace.empty,