authorchloe caruso <git@paperclover.net> 2024-07-27 02:27:50-07:00
committerchloe caruso <git@paperclover.net> 2024-07-27 02:28:31-07:00
loged6c32291731a7b4296e7d088e38d98e88140eb7
treed28d440450cee0f0fb2aaa6a2cc8cab33fd73b3f
parent5352a9f980dd9bc5aa8205d6fec7eba44e13609b

feat: implement result types and inferred struct literals (#76)

result types (not to be confused with Rust's `Result`) are the semantics of being able to reflect the type of an expression at compile time before it is analyzed. this is how inferred struct literals can figure out what type they are before the actual coercion happens.

13 files changed, 1320 insertions(+), 995 deletions(-)

CHANGELOG.md+2
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1## 0.1.51## 0.1.5
22
3- feat: clarify "defined runtime representation" and "can be represented at runtime"3- feat: clarify "defined runtime representation" and "can be represented at runtime"
4- feat: implement inferred structs + result type semantics
5- chore: remove group expressions, use single expression blocks instead
4- fix(sema): fix passing structs as function params6- fix(sema): fix passing structs as function params
5- fix: diagnostic box drawing characters line up after line 97- fix: diagnostic box drawing characters line up after line 9
6- fix: diagnostics on last line of file print correctly8- fix: diagnostics on last line of file print correctly
build.zig+1
...@@ -103,6 +103,7 @@ pub fn build(b: *std.Build) !void {...@@ -103,6 +103,7 @@ pub fn build(b: *std.Build) !void {
103 const manual_out = manual_run.addOutputFileArg("language-reference.html");103 const manual_out = manual_run.addOutputFileArg("language-reference.html");
104 const install_manual = b.addInstallFile(manual_out, "doc/README.html");104 const install_manual = b.addInstallFile(manual_out, "doc/README.html");
105 b.step("manual", "build language reference").dependOn(&install_manual.step);105 b.step("manual", "build language reference").dependOn(&install_manual.step);
106 b.install_tls.step.dependOn(&install_manual.step);
106107
107 // const wasm_api = b.addExecutable(.{108 // const wasm_api = b.addExecutable(.{
108 // .name = "compiler",109 // .name = "compiler",
doc/language-manual.md+3-4
...@@ -176,13 +176,12 @@ print(center.x, center.y);...@@ -176,13 +176,12 @@ print(center.x, center.y);
176176
177fn addPositions(a: Position, b: Position) -> Position {177fn addPositions(a: Position, b: Position) -> Position {
178 // To allow less verbose code, the struct type can be inferred with `.`178 // To allow less verbose code, the struct type can be inferred with `.`
179 // TODO: that is not implemented ;-;179 .{ x: a.x + b.x, y: a.y + b.y }
180 Position{ x: a.x + b.x, y: a.y + b.y }
181}180}
182181
183let added = addPositions(182let added = addPositions(
184 Position{ x: 1, y: -4 },183 .{ x: 1, y: -4 },
185 Position{ x: 5, y: 8 },184 .{ x: 5, y: 8 },
186);185);
187print("Add result:", added.x, added.y);186print("Add result:", added.x, added.y);
188```187```
src/core/Ast.zig+7-6
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1//! The Abstract Syntax Tree preserves most details of the source code,1//! The Abstract Syntax Tree preserves most details of the source code,
2//! as it is used for formatting. Before semantic analysis, this2//! as it is used for formatting. Before semantic analysis, this
3//! structure is normalized into `Teg`, which removes constructs like3//! structure is normalized into `Teg`, which removes constructs like
4//! grouping, unneeded let bindings, and other transforms.4//! whitespace, unneeded let bindings, and other transforms.
5//!5//!
6//! `ast.nodes` contains nodes stored as three parts:6//! `ast.nodes` contains nodes stored as three parts:
7//! - tag: the type of the node7//! - tag: the type of the node
...@@ -130,10 +130,6 @@ pub const Node = struct {...@@ -130,10 +130,6 @@ pub const Node = struct {
130 /// main_token is the `break`130 /// main_token is the `break`
131 /// data is .@"break"131 /// data is .@"break"
132 @"break",132 @"break",
133 /// `(expr)`
134 /// main_token is the `(`
135 /// data is .un
136 group,
137 /// `lhs + rhs`. main_token is the operator.133 /// `lhs + rhs`. main_token is the operator.
138 add,134 add,
139 /// `lhs - rhs`. main_token is the operator.135 /// `lhs - rhs`. main_token is the operator.
...@@ -222,6 +218,10 @@ pub const Node = struct {...@@ -222,6 +218,10 @@ pub const Node = struct {
222 array_type,218 array_type,
223 /// Array initialization219 /// Array initialization
224 array_init,220 array_init,
221 /// Tuple type
222 tuple_type,
223 /// Tuple Init
224 tuple_init,
225225
226 pub fn isAssignBin(op: Tag) bool {226 pub fn isAssignBin(op: Tag) bool {
227 return switch (op) {227 return switch (op) {
...@@ -309,7 +309,6 @@ pub const Node = struct {...@@ -309,7 +309,6 @@ pub const Node = struct {
309 .fn_decl = .function,309 .fn_decl = .function,
310 .fn_expr = .function,310 .fn_expr = .function,
311 .ge = .bin,311 .ge = .bin,
312 .group = .un,
313 .gt = .bin,312 .gt = .bin,
314 .id = .none,313 .id = .none,
315 .index = .bin,314 .index = .bin,
...@@ -344,6 +343,8 @@ pub const Node = struct {...@@ -344,6 +343,8 @@ pub const Node = struct {
344 .sub = .bin,343 .sub = .bin,
345 .sub_assign = .bin,344 .sub_assign = .bin,
346 .var_decl = .var_decl,345 .var_decl = .var_decl,
346 .tuple_type = .list,
347 .tuple_init = .list,
347 // sort-lines:end348 // sort-lines:end
348 });349 });
349350
src/core/Parse.zig+22-32
...@@ -31,7 +31,7 @@ pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8, ast_error: *AstError....@@ -31,7 +31,7 @@ pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8, ast_error: *AstError.
31 .err = err,31 .err = err,
32 .byte = err_byte,32 .byte = err_byte,
33 } },33 } },
34 .trace = Report.DebugStoredTrace.from(@errorReturnTrace()),34 .trace = DebugStoredTrace.from(@errorReturnTrace()),
35 };35 };
36 return error.ParseFail;36 return error.ParseFail;
37 },37 },
...@@ -143,7 +143,7 @@ pub const AstError = union(enum) {...@@ -143,7 +143,7 @@ pub const AstError = union(enum) {
143143
144 array_type_flipped_hint: Token,144 array_type_flipped_hint: Token,
145145
146 pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report {146 pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: DebugStoredTrace) !Report {
147 return (Report.Err{ .ast = err }).toReport(comp, file, trace);147 return (Report.Err{ .ast = err }).toReport(comp, file, trace);
148 }148 }
149149
...@@ -193,7 +193,7 @@ pub const AstError = union(enum) {...@@ -193,7 +193,7 @@ pub const AstError = union(enum) {
193193
194 pub const WithTrace = struct {194 pub const WithTrace = struct {
195 err: AstError,195 err: AstError,
196 trace: Report.DebugStoredTrace,196 trace: DebugStoredTrace,
197197
198 pub fn toReport(err: WithTrace, comp: *Comp, file: *Comp.File) !Report {198 pub fn toReport(err: WithTrace, comp: *Comp, file: *Comp.File) !Report {
199 return err.err.toReport(comp, file, err.trace);199 return err.err.toReport(comp, file, err.trace);
...@@ -856,7 +856,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -856,7 +856,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
856 .kw_return => try p.parseReturn(),856 .kw_return => try p.parseReturn(),
857 .start_brace => try p.parseBlock(),857 .start_brace => try p.parseBlock(),
858 .start_bracket => try p.parseArrayValueOrType(mode),858 .start_bracket => try p.parseArrayValueOrType(mode),
859 .start_paren => try p.parseGroup(),859 .start_paren => try p.parseTupleType(),
860860
861 // Unary operators861 // Unary operators
862 inline .bang,862 inline .bang,
...@@ -1136,25 +1136,6 @@ fn parseElseValue(p: *Parse) Error!NodeIndex {...@@ -1136,25 +1136,6 @@ fn parseElseValue(p: *Parse) Error!NodeIndex {
1136 };1136 };
1137}1137}
11381138
1139fn parseGroup(p: *Parse) Error!NodeIndex {
1140 const trace = t.trace(@src());
1141 defer trace.end();
1142
1143 const main_token = p.assertToken(.start_paren);
1144
1145 const expr = try p.parseExpression(.expression);
1146
1147 _ = try p.expect(.end_paren);
1148
1149 return try p.addNode(.{
1150 .tag = .group,
1151 .main_token = main_token,
1152 .data = .{ .un = .{
1153 .expr = expr,
1154 } },
1155 });
1156}
1157
1158fn parseAnonInstantiation(p: *Parse) Error!NodeIndex {1139fn parseAnonInstantiation(p: *Parse) Error!NodeIndex {
1159 const trace = t.trace(@src());1140 const trace = t.trace(@src());
1160 defer trace.end();1141 defer trace.end();
...@@ -1266,6 +1247,16 @@ fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -1266,6 +1247,16 @@ fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
1266 });1247 });
1267}1248}
12681249
1250fn parseTupleType(p: *Parse) Error!NodeIndex {
1251 const main_token = p.assertToken(.start_bracket);
1252 const list = try p.parseExpressionList(.end_paren, .expression);
1253 return p.addNode(.{
1254 .tag = .tuple_type,
1255 .main_token = main_token,
1256 .data = .{ .list = list },
1257 });
1258}
1259
1269// helpers1260// helpers
12701261
1271/// Append a node and return its index.1262/// Append a node and return its index.
...@@ -1292,7 +1283,7 @@ fn addExtra(p: *Parse, comptime T: type, extra: T) !ExtraIndex {...@@ -1292,7 +1283,7 @@ fn addExtra(p: *Parse, comptime T: type, extra: T) !ExtraIndex {
1292fn fail(p: *Parse, ast_error: AstError) Error {1283fn fail(p: *Parse, ast_error: AstError) Error {
1293 p.ast_error = .{1284 p.ast_error = .{
1294 .err = ast_error,1285 .err = ast_error,
1295 .trace = Report.DebugStoredTrace.capture(@returnAddress()),1286 .trace = DebugStoredTrace.capture(@returnAddress()),
1296 };1287 };
1297 return Error.ParseFail;1288 return Error.ParseFail;
1298}1289}
...@@ -1366,10 +1357,6 @@ fn nextToken(p: *Parse) TokenIndex {...@@ -1366,10 +1357,6 @@ fn nextToken(p: *Parse) TokenIndex {
1366 return result;1357 return result;
1367}1358}
13681359
1369fn tokensOnSameLine(p: *Parse, token1: TokenIndex, token2: TokenIndex) bool {
1370 return std.mem.indexOfScalar(u8, p.source[p.token_starts[token1]..p.token_starts[token2]], '\n') == null;
1371}
1372
1373fn fullToken(p: *Parse, token: TokenIndex) Token {1360fn fullToken(p: *Parse, token: TokenIndex) Token {
1374 return Token{1361 return Token{
1375 .tag = p.token_tags[token],1362 .tag = p.token_tags[token],
...@@ -1398,11 +1385,14 @@ const mem = std.mem;...@@ -1398,11 +1385,14 @@ const mem = std.mem;
1398const Allocator = mem.Allocator;1385const Allocator = mem.Allocator;
1399const ArrayListUnmanaged = std.ArrayListUnmanaged;1386const ArrayListUnmanaged = std.ArrayListUnmanaged;
14001387
1401const Tokenizer = @import("Tokenizer.zig");1388const core = @import("mod.zig");
1389const DebugStoredTrace = core.DebugStoredTrace;
1390
1391const Tokenizer = core.Tokenizer;
1402const Token = Tokenizer.Token;1392const Token = Tokenizer.Token;
1403const ByteOffset = Tokenizer.ByteOffset;1393const ByteOffset = Tokenizer.ByteOffset;
14041394
1405const Ast = @import("Ast.zig");1395const Ast = core.Ast;
1406const Node = Ast.Node;1396const Node = Ast.Node;
1407const Data = Node.Data;1397const Data = Node.Data;
1408const SubList = Ast.SubList;1398const SubList = Ast.SubList;
...@@ -1418,5 +1408,5 @@ const optionalExtraIndex = OptionalExtraIndex.init;...@@ -1418,5 +1408,5 @@ const optionalExtraIndex = OptionalExtraIndex.init;
14181408
1419const t = @import("tracer");1409const t = @import("tracer");
14201410
1421const Report = @import("Report.zig");1411const Report = core.Report;
1422const Comp = @import("Comp.zig");1412const Comp = core.Comp;
src/core/Report.zig+26-64
...@@ -135,6 +135,17 @@ pub const Err = union(enum) {...@@ -135,6 +135,17 @@ pub const Err = union(enum) {
135 );135 );
136 }136 }
137 },137 },
138 missing_result_type: struct {
139 node: NodeIndex,
140
141 fn build(data: @This(), ctx: *MsgContext) !void {
142 try ctx.msgNode(
143 data.node,
144 "Cannot infer struct initialization type",
145 .{},
146 );
147 }
148 },
138 unresolved_label: struct {149 unresolved_label: struct {
139 node: NodeIndex,150 node: NodeIndex,
140151
...@@ -523,62 +534,6 @@ pub const Warn = union(enum) {...@@ -523,62 +534,6 @@ pub const Warn = union(enum) {
523 },534 },
524};535};
525536
526/// A variant of std.builtin.StackTrace that stores its data within itself
527/// instead of being a pointer. This allows capturing the stack trace and
528/// saving it for later printing.
529pub const StoredTrace = struct {
530 data: [32]usize,
531 index: usize,
532
533 pub const empty = .{
534 .data = .{0} ** 32,
535 .index = 0,
536 };
537
538 fn trace(stored: *StoredTrace) std.builtin.StackTrace {
539 return .{
540 .index = stored.index,
541 .instruction_addresses = &stored.data,
542 };
543 }
544
545 pub fn capture(begin: ?usize) StoredTrace {
546 var stored: StoredTrace = undefined;
547 var frame = stored.trace();
548 std.debug.captureStackTrace(begin orelse @returnAddress(), &frame);
549 stored.index = frame.index;
550 return stored;
551 }
552
553 pub fn from(stack_trace: ?*std.builtin.StackTrace) StoredTrace {
554 if (stack_trace) |stack| {
555 var data: [32]usize = undefined;
556 @memset(&data, 0);
557 const items = @min(stack.instruction_addresses.len, 32);
558 @memcpy(data[0..items], stack.instruction_addresses[0..items]);
559 return .{
560 .data = data,
561 .index = @min(items, stack.index),
562 };
563 } else {
564 return empty;
565 }
566 }
567};
568
569pub const DebugStoredTrace = if (builtin.mode == .Debug)
570 StoredTrace
571else
572 struct {
573 pub fn capture(_: ?usize) @This() {
574 return .{};
575 }
576
577 pub fn from(_: ?*std.builtin.StackTrace) @This() {
578 return .{};
579 }
580 };
581
582pub const UnusedKind = enum {537pub const UnusedKind = enum {
583 param,538 param,
584 constant,539 constant,
...@@ -817,7 +772,7 @@ pub const Builder = struct {...@@ -817,7 +772,7 @@ pub const Builder = struct {
817 try b.errors.append(b.gpa, .{772 try b.errors.append(b.gpa, .{
818 .data = .{ .@"error" = @unionInit(Err, @tagName(kind), data) },773 .data = .{ .@"error" = @unionInit(Err, @tagName(kind), data) },
819 .url = try b.currentFileString(),774 .url = try b.currentFileString(),
820 .debug_stack_trace = if (builtin.mode == .Debug) StoredTrace.capture(@returnAddress()) else .{},775 .debug_stack_trace = DebugStoredTrace.capture(@returnAddress()),
821 });776 });
822 }777 }
823778
...@@ -825,7 +780,7 @@ pub const Builder = struct {...@@ -825,7 +780,7 @@ pub const Builder = struct {
825 try b.warnings.append(b.gpa, .{780 try b.warnings.append(b.gpa, .{
826 .data = .{ .warn = @unionInit(Warn, @tagName(kind), data) },781 .data = .{ .warn = @unionInit(Warn, @tagName(kind), data) },
827 .url = try b.currentFileString(),782 .url = try b.currentFileString(),
828 .debug_stack_trace = if (builtin.mode == .Debug) StoredTrace.capture(@returnAddress()) else .{},783 .debug_stack_trace = DebugStoredTrace.capture(@returnAddress()),
829 });784 });
830 }785 }
831786
...@@ -1050,7 +1005,7 @@ pub const LazyNode = struct {...@@ -1050,7 +1005,7 @@ pub const LazyNode = struct {
1050 const node_tag = ctx.ast.nodeTag(base_node);1005 const node_tag = ctx.ast.nodeTag(base_node);
10511006
1052 switch (teg.instTag(lazy.inst)) {1007 switch (teg.instTag(lazy.inst)) {
1053 .as => switch (node_tag) {1008 .coercion => switch (node_tag) {
1054 .var_decl => switch (lazy.operand) {1009 .var_decl => switch (lazy.operand) {
1055 .lhs => return data.var_decl.type.unwrap().?,1010 .lhs => return data.var_decl.type.unwrap().?,
1056 .rhs => return data.var_decl.init,1011 .rhs => return data.var_decl.init,
...@@ -1121,6 +1076,10 @@ pub const LazyNode = struct {...@@ -1121,6 +1076,10 @@ pub const LazyNode = struct {
1121 const items = data.list.slice(ctx.ast);1076 const items = data.list.slice(ctx.ast);
1122 return items[lazy.operand.getArgId()];1077 return items[lazy.operand.getArgId()];
1123 },1078 },
1079 .field => {
1080 assert(node_tag == .field);
1081 return base_node;
1082 },
1124 else => {},1083 else => {},
1125 }1084 }
11261085
...@@ -1358,19 +1317,22 @@ const builtin = @import("builtin");...@@ -1358,19 +1317,22 @@ const builtin = @import("builtin");
13581317
1359const unicode_props = @import("unicode_props.zig");1318const unicode_props = @import("unicode_props.zig");
13601319
1361const Tokenizer = @import("Tokenizer.zig");1320const core = @import("mod.zig");
1321const DebugStoredTrace = core.DebugStoredTrace;
1322
1323const Tokenizer = core.Tokenizer;
1362const ByteOffset = Tokenizer.ByteOffset;1324const ByteOffset = Tokenizer.ByteOffset;
13631325
1364const Ast = @import("Ast.zig");1326const Ast = core.Ast;
1365const TokenIndex = Ast.TokenIndex;1327const TokenIndex = Ast.TokenIndex;
1366const NodeIndex = Ast.NodeIndex;1328const NodeIndex = Ast.NodeIndex;
1367const Location = Ast.Location;1329const Location = Ast.Location;
13681330
1369const Teg = @import("Teg.zig");1331const Teg = core.Teg;
13701332
1371const Comp = @import("Comp.zig");1333const Comp = core.Comp;
1372const File = Comp.File;1334const File = Comp.File;
13731335
1374const InternPool = @import("InternPool.zig");1336const InternPool = core.InternPool;
1375const Value = InternPool.Value;1337const Value = InternPool.Value;
1376const Type = InternPool.Type;1338const Type = InternPool.Type;
src/core/Sema.zig+152-55
...@@ -66,24 +66,30 @@ pub const Error = error{...@@ -66,24 +66,30 @@ pub const Error = error{
66 OutOfMemory,66 OutOfMemory,
67} || Comp.File.GetTegError;67} || Comp.File.GetTegError;
6868
69/// Instead of using a Hash map for this, just pre-allocate a list long enough to hold everything.69/// Instead of using a Hash map for this, all of the information is pre-allocated.
70/// This is fine because all instructions are going to be adjacent in a single declaration.70/// This is only possible because all instructions are adjacent in Teg.
71const InstMap = struct {71const InstMap = struct {
72 items: []Air.Inst.Ref.Optional,72 items: []Air.Inst.Ref.Optional,
73 start: Teg.Inst.Index,73 start: Teg.Inst.Index,
74 debug_traces: if (std.debug.runtime_safety) []StoredTrace else void,
7475
75 pub fn init(allocator: Allocator, code: *const Teg, inst_start: Teg.Inst.Index) !InstMap {76 pub fn init(allocator: Allocator, code: *const Teg, inst_start: Teg.Inst.Index) !InstMap {
76 const decl = code.declData(inst_start);77 const decl = code.declData(inst_start);
77 const insts = try allocator.alloc(Air.Inst.Ref.Optional, decl.instruction_count);78 const insts = try allocator.alloc(Air.Inst.Ref.Optional, decl.instruction_count);
79 const debug_traces = if (std.debug.runtime_safety)
80 try allocator.alloc(StoredTrace, decl.instruction_count);
78 @memset(insts, .none);81 @memset(insts, .none);
79 return .{82 return .{
80 .items = insts,83 .items = insts,
81 .start = inst_start,84 .start = inst_start,
85 .debug_traces = debug_traces,
82 };86 };
83 }87 }
8488
85 pub fn deinit(map: *InstMap, allocator: Allocator) void {89 pub fn deinit(map: *InstMap, allocator: Allocator) void {
86 allocator.free(map.items);90 allocator.free(map.items);
91 if (std.debug.runtime_safety)
92 allocator.free(map.debug_traces);
87 map.* = undefined;93 map.* = undefined;
88 }94 }
8995
...@@ -95,16 +101,21 @@ const InstMap = struct {...@@ -95,16 +101,21 @@ const InstMap = struct {
95 }101 }
96102
97 pub fn add(map: *InstMap, from: Teg.Inst.Index, to: Air.Inst.Ref) !void {103 pub fn add(map: *InstMap, from: Teg.Inst.Index, to: Air.Inst.Ref) !void {
104 const source_i = @intFromEnum(from);
105 const start_i = @intFromEnum(map.start);
106 const index = source_i - start_i;
98 if (assertFailed(map.get(from) == null)) {107 if (assertFailed(map.get(from) == null)) {
99 const s: *const Sema = @fieldParentPtr("inst_map", map);108 const s: *const Sema = @fieldParentPtr("inst_map", map);
109 std.debug.print("Original was added here:\n", .{});
110 std.debug.dumpStackTrace(map.debug_traces[index].trace());
100 std.debug.panic("%{d} is already mapped (to {})", .{111 std.debug.panic("%{d} is already mapped (to {})", .{
101 @intFromEnum(from),112 @intFromEnum(from),
102 map.get(from).?.dump(s.ip),113 map.get(from).?.dump(s.ip),
103 });114 });
104 }115 }
105 const source_i = @intFromEnum(from);116 map.items[index] = to.toOptional();
106 const start_i = @intFromEnum(map.start);117 if (std.debug.runtime_safety)
107 map.items[source_i - start_i] = to.toOptional();118 map.debug_traces[index] = StoredTrace.capture(@returnAddress());
108 }119 }
109};120};
110121
...@@ -216,36 +227,44 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {...@@ -216,36 +227,44 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {
216 s.params[i] = ty;227 s.params[i] = ty;
217 }228 }
218229
219 const return_type = try s.resolveType(230 var return_type, const early_value = if (fn_decl.return_type.unwrap()) |ty| brk: {
220 s.inst_map.start.op(.return_type),231 const return_type = try s.resolveType(
221 fn_decl.return_type,232 s.inst_map.start.op(.return_type),
222 );233 ty,
234 );
235 const value = try s.makeValue(.{
236 .func = .{
237 .ty = try s.makeType(.{
238 .function_type = .{
239 .param_types = s.params,
240 .return_type = return_type,
241 },
242 }),
243 .owner_decl = s.decl_index,
244 .teg_body_inst = s.decl.teg_decl_index,
245 },
246 });
223247
224 const func_prototype = (try s.ip.get(s.gpa, .{248 s.decl.raw_value = value.typeOf(s.ip).value();
225 .function_type = .{249 s.decl.analysis = .in_progress_type_available;
226 .param_types = s.params,
227 .return_type = return_type,
228 },
229 })).toTypeUnsafe(s.ip);
230 const value = try s.ip.get(s.gpa, .{
231 .func = .{
232 .ty = func_prototype,
233 .owner_decl = s.decl_index,
234 .teg_body_inst = s.decl.teg_decl_index,
235 },
236 });
237250
238 s.decl.raw_value = value.typeOf(s.ip).value();251 break :brk .{ return_type, value };
239 s.decl.analysis = .in_progress_type_available;252 } else .{ null, null };
240253
241 const root_block = decl.body;254 const root_block = decl.body;
242 switch (root_block.decode()) {255 switch (root_block.decode()) {
243 .value => |val| {256 .value => |val| {
244 const coerced = try s.coerceValue(257 const coerced = if (return_type) |ty|
245 s.inst_map.start.op(.operand),258 try s.coerceValue(
246 val,259 s.inst_map.start.op(.operand),
247 return_type,260 val,
248 );261 ty,
262 )
263 else brk: {
264 return_type = val.typeOf(s.ip);
265 break :brk val;
266 };
267
249 const block = try s.addBlock();268 const block = try s.addBlock();
250 _ = try s.addInstRef(.@"break", .{269 _ = try s.addInstRef(.@"break", .{
251 .block_ref = block.block_index,270 .block_ref = block.block_index,
...@@ -253,11 +272,30 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {...@@ -253,11 +272,30 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {
253 });272 });
254 },273 },
255 .index => |index| {274 .index => |index| {
256 try s.temp_result_map.putNoClobber(s.arena, index, return_type);275 if (return_type) |ty| {
257 _ = try s.analyzeBlock(index);276 try s.temp_result_map.putNoClobber(s.arena, index, ty);
277 }
278
279 const out = try s.analyzeBlock(index);
280
281 return_type = return_type orelse s.typeOfRef(out);
258 },282 },
259 }283 }
260284
285 const value = early_value orelse try s.makeValue(.{
286 .func = .{
287 .ty = try s.makeType(.{
288 .function_type = .{
289 .param_types = s.params,
290 .return_type = return_type orelse
291 unreachable, // type is reassigned above
292 },
293 }),
294 .owner_decl = s.decl_index,
295 .teg_body_inst = s.decl.teg_decl_index,
296 },
297 });
298
261 s.decl.analysis = .complete;299 s.decl.analysis = .complete;
262 s.decl.raw_value = value;300 s.decl.raw_value = value;
263301
...@@ -342,7 +380,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -342,7 +380,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
342 return s.addInstRef(.js_global_ref, operand.asString(s.ip));380 return s.addInstRef(.js_global_ref, operand.asString(s.ip));
343 },381 },
344 .field => {382 .field => {
345 const field: Teg.Inst.Bin = s.code.instPayload(.field, in);383 const field: Teg.Bin = s.code.instPayload(.field, in);
346 const base = try s.resolveRef(field.lhs);384 const base = try s.resolveRef(field.lhs);
347385
348 const rhs = try s.resolveCoerceValue(in.op(.rhs), field.rhs, .string);386 const rhs = try s.resolveCoerceValue(in.op(.rhs), field.rhs, .string);
...@@ -402,7 +440,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -402,7 +440,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
402 });440 });
403 },441 },
404 .load_decl => {442 .load_decl => {
405 const request: Teg.Inst.StrNode = s.code.instData(.load_decl, in);443 const request: Teg.StrNode = s.code.instData(.load_decl, in);
406 const decl_name = try s.ip.getOrPutString(444 const decl_name = try s.ip.getOrPutString(
407 s.gpa,445 s.gpa,
408 request.string.slice(s.code.string_bytes),446 request.string.slice(s.code.string_bytes),
...@@ -416,7 +454,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -416,7 +454,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
416 return resolved.toAir();454 return resolved.toAir();
417 },455 },
418 .param => {456 .param => {
419 const param: Teg.Inst.DataNode = s.code.instData(.param, in);457 const param: Teg.DataNode = s.code.instData(.param, in);
420 return s.addInstRef(.arg, .{458 return s.addInstRef(.arg, .{
421 .type = s.params[param.data],459 .type = s.params[param.data],
422 .arg_number = param.data,460 .arg_number = param.data,
...@@ -430,7 +468,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -430,7 +468,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
430 .gt,468 .gt,
431 .eq,469 .eq,
432 => |tag| {470 => |tag| {
433 const bin: Teg.Inst.Bin = s.code.instPayload(tag, in);471 const bin: Teg.Bin = s.code.instPayload(tag, in);
434 const op = @field(BinaryOp, @tagName(tag));472 const op = @field(BinaryOp, @tagName(tag));
435473
436 const lhs = try s.resolveRef(bin.lhs);474 const lhs = try s.resolveRef(bin.lhs);
...@@ -458,7 +496,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -458,7 +496,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
458 });496 });
459 },497 },
460 .bool_not => {498 .bool_not => {
461 const un: Teg.Inst.RefNode = s.code.instData(.bool_not, in);499 const un: Teg.RefNode = s.code.instData(.bool_not, in);
462500
463 const operand = try s.resolveCoerceRef(in.op(.operand), un.operand, .bool);501 const operand = try s.resolveCoerceRef(in.op(.operand), un.operand, .bool);
464502
...@@ -474,7 +512,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -474,7 +512,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
474 return s.addInstRef(.bool_not, .{ .operand = operand });512 return s.addInstRef(.bool_not, .{ .operand = operand });
475 },513 },
476 .negate => {514 .negate => {
477 const un: Teg.Inst.RefNode = s.code.instData(.negate, in);515 const un: Teg.RefNode = s.code.instData(.negate, in);
478516
479 const operand = try s.resolveRef(un.operand);517 const operand = try s.resolveRef(un.operand);
480518
...@@ -496,7 +534,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -496,7 +534,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
496 return s.addInstRef(.negate, .{ .operand = operand });534 return s.addInstRef(.negate, .{ .operand = operand });
497 },535 },
498 .@"break" => {536 .@"break" => {
499 const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in);537 const brk: Teg.Break = s.code.instPayload(.@"break", in);
500 const operand = try s.resolveRef(brk.operand);538 const operand = try s.resolveRef(brk.operand);
501 const block_ref = s.inst_map.get(brk.block).?.decode().index; // block ref is resolved by teg539 const block_ref = s.inst_map.get(brk.block).?.decode().index; // block ref is resolved by teg
502540
...@@ -511,7 +549,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -511,7 +549,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
511 });549 });
512 },550 },
513 .call => {551 .call => {
514 const call: Teg.Inst.Call = s.code.instPayload(.call, in);552 const call: Teg.Call = s.code.instPayload(.call, in);
515 const base = try s.resolveRef(call.base);553 const base = try s.resolveRef(call.base);
516 const base_type = s.typeOfRef(base);554 const base_type = s.typeOfRef(base);
517555
...@@ -568,7 +606,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -568,7 +606,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
568 });606 });
569 },607 },
570 .js_new => {608 .js_new => {
571 const call: Teg.Inst.JsNew = s.code.instPayload(.js_new, in);609 const call: Teg.JsNew = s.code.instPayload(.js_new, in);
572 const base = try s.resolveRef(call.base);610 const base = try s.resolveRef(call.base);
573611
574 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);612 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
...@@ -612,7 +650,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -612,7 +650,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
612 return InternPool.Value.void.toAir();650 return InternPool.Value.void.toAir();
613 },651 },
614 .@"if" => {652 .@"if" => {
615 const data: Teg.Inst.If = s.code.instData(.@"if", in).extra_index.get(&s.code);653 const data: Teg.If = s.code.instData(.@"if", in).extra_index.get(&s.code);
616654
617 const cond_raw = try s.resolveRef(data.cond);655 const cond_raw = try s.resolveRef(data.cond);
618 const cond = try s.coerceRef(in.op(.operand), cond_raw, .bool);656 const cond = try s.coerceRef(in.op(.operand), cond_raw, .bool);
...@@ -661,7 +699,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -661,7 +699,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
661 });699 });
662 },700 },
663 .store => {701 .store => {
664 const data: Teg.Inst.Store = s.code.instPayload(.store, in);702 const data: Teg.Store = s.code.instPayload(.store, in);
665 assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction703 assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction
666704
667 const location = s.mapTeg(data.memory.toRef()).decode().index;705 const location = s.mapTeg(data.memory.toRef()).decode().index;
...@@ -677,24 +715,24 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -677,24 +715,24 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
677 .operand = operand_coerced,715 .operand = operand_coerced,
678 });716 });
679 },717 },
680 .as => {718 .coercion => {
681 const as: Teg.Inst.As = s.code.instPayload(.as, in);719 const as: Teg.Coercion = s.code.instPayload(.coercion, in);
682 const coerce_to = try s.resolveCoerceValue(in.op(.lhs), as.type, .type);720 const coerce_to = try s.resolveCoerceValue(in.op(.lhs), as.type, .type);
683 const operand = try s.resolveRef(as.operand);721 const operand = try s.resolveRef(as.operand);
684 return try s.coerceRef(in.op(.rhs), operand, coerce_to.toType(s.ip).?);722 return try s.coerceRef(in.op(.rhs), operand, coerce_to.toType(s.ip).?);
685 },723 },
686 .@"comptime" => {724 .@"comptime" => {
687 const node: Teg.Inst.RefNode = s.code.instData(.@"comptime", in);725 const node: Teg.RefNode = s.code.instData(.@"comptime", in);
688 const value = try s.resolveValue(node.operand);726 const value = try s.resolveValue(node.operand);
689 return value.toAir();727 return value.toAir();
690 },728 },
691 .type_of => {729 .type_of => {
692 const node: Teg.Inst.RefNode = s.code.instData(.type_of, in);730 const node: Teg.RefNode = s.code.instData(.type_of, in);
693 const value = try s.resolveValue(node.operand);731 const value = try s.resolveValue(node.operand);
694 return value.typeOf(s.ip).value().toAir();732 return value.typeOf(s.ip).value().toAir();
695 },733 },
696 .compile_error => {734 .compile_error => {
697 const node: Teg.Inst.RefNode = s.code.instData(.compile_error, in);735 const node: Teg.RefNode = s.code.instData(.compile_error, in);
698 const value = try s.resolveCoerceValue(736 const value = try s.resolveCoerceValue(
699 in.op(operandArgument(0)),737 in.op(operandArgument(0)),
700 node.operand,738 node.operand,
...@@ -707,7 +745,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -707,7 +745,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
707 });745 });
708 },746 },
709 .struct_decl => {747 .struct_decl => {
710 const node: Teg.Inst.StructDecl = s.code.instPayload(.struct_decl, in);748 const node: Teg.StructDecl = s.code.instPayload(.struct_decl, in);
711749
712 const scratch = try s.scratch.reserveMulti(750 const scratch = try s.scratch.reserveMulti(
713 s.arena,751 s.arena,
...@@ -732,7 +770,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -732,7 +770,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
732 } });770 } });
733 },771 },
734 .struct_init => {772 .struct_init => {
735 const node: Teg.Inst.StructInit = s.code.instPayload(.struct_init, in);773 const node: Teg.StructInit = s.code.instPayload(.struct_init, in);
736774
737 // Ensure we have a valid struct to operate on775 // Ensure we have a valid struct to operate on
738 const ty = try s.resolveType(in.op(.operand), node.base_type);776 const ty = try s.resolveType(in.op(.operand), node.base_type);
...@@ -835,9 +873,8 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -835,9 +873,8 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
835 });873 });
836 }874 }
837 },875 },
838
839 .array_init => {876 .array_init => {
840 const node: Teg.Inst.ArrayInit = s.code.instPayload(.array_init, in);877 const node: Teg.ArrayInit = s.code.instPayload(.array_init, in);
841878
842 assert(node.list.len > 0);879 assert(node.list.len > 0);
843880
...@@ -894,13 +931,72 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -894,13 +931,72 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
894 });931 });
895 }932 }
896 },933 },
897
898 .array_type => {934 .array_type => {
899 const un: Teg.Inst.RefNode = s.code.instData(.array_type, in);935 const un: Teg.RefNode = s.code.instData(.array_type, in);
900 const operand = try s.resolveType(in.op(.operand), un.operand);936 const operand = try s.resolveType(in.op(.operand), un.operand);
901 return s.makeValueAir(.{ .array_type = .{ .item_type = operand } });937 return s.makeValueAir(.{ .array_type = .{ .item_type = operand } });
902 },938 },
903939
940 .result_type_argument => {
941 const result: Teg.ResultTypeArgument = s.code.instData(.result_type_argument, in);
942
943 const operand = try s.resolveRef(result.call_base);
944 const ty = s.typeOfRef(operand);
945
946 const func: InternPool.Key.FunctionType = ty.toFunctionPrototype(s.ip) orelse
947 return Value.opaque_type.toAir();
948
949 if (result.argument >= func.param_types.len)
950 return Value.opaque_type.toAir();
951
952 return func.param_types[result.argument].value().toAir();
953 },
954
955 .result_type_array_value => {
956 const result: Teg.ResultTypeArrayValue = s.code.instData(.result_type_array_value, in);
957
958 const operand = try s.resolveValue(result.type);
959 const ty = operand.toType(s.ip) orelse
960 return Value.opaque_type;
961
962 const array_type = ty.toArrayType(s.ip) orelse
963 return Value.opaque_type.toAir();
964
965 return array_type.item_type.value().toAir();
966 },
967
968 .result_type_tuple_field => {
969 const result: Teg.ResultTypeTupleField = s.code.instData(.result_type_array_value, in);
970
971 const operand = try s.resolveValue(result.type);
972 const ty = operand.toType(s.ip) orelse
973 return Value.opaque_type;
974
975 _ = ty;
976
977 @panic("TODO: tuple result type resolution"); // tuples are not implemented
978 },
979
980 .result_type_struct_field => {
981 const result: Teg.ResultTypeStructField = s.code.instData(.result_type_struct_field, in);
982
983 const operand = try s.resolveValue(result.type);
984 const ty = operand.toType(s.ip) orelse
985 return Value.opaque_type;
986
987 const struct_type = ty.toStructType(s.ip) orelse
988 return Value.opaque_type.toAir();
989
990 const to_match = result.field.slice(s.code.string_bytes);
991 for (struct_type.field_keys, struct_type.field_types) |k, v| {
992 if (k.eqlSlice(to_match, s.ip)) {
993 return v.value().toAir();
994 }
995 }
996
997 return Value.opaque_type.toAir();
998 },
999
904 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),1000 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),
905 }1001 }
906}1002}
...@@ -1001,13 +1097,13 @@ fn resolveValue(s: *Sema, ref: Teg.Inst.Ref) !Value {...@@ -1001,13 +1097,13 @@ fn resolveValue(s: *Sema, ref: Teg.Inst.Ref) !Value {
1001 const break_inst = indexes[1];1097 const break_inst = indexes[1];
10021098
1003 if (s.code.instTag(break_inst) != .@"break") break :special_cased;1099 if (s.code.instTag(break_inst) != .@"break") break :special_cased;
1004 const break_data: Teg.Inst.Break = s.code.instPayload(.@"break", break_inst);1100 const break_data: Teg.Break = s.code.instPayload(.@"break", break_inst);
1005 if (break_data.operand != load_inst.toRef()) break :special_cased;1101 if (break_data.operand != load_inst.toRef()) break :special_cased;
1006 if (break_data.block != index) break :special_cased;1102 if (break_data.block != index) break :special_cased;
10071103
1008 if (s.code.instTag(load_inst) != .load_decl) break :special_cased;1104 if (s.code.instTag(load_inst) != .load_decl) break :special_cased;
10091105
1010 const request: Teg.Inst.StrNode = s.code.instData(.load_decl, load_inst);1106 const request: Teg.StrNode = s.code.instData(.load_decl, load_inst);
1011 const decl_name = try s.ip.getOrPutString(1107 const decl_name = try s.ip.getOrPutString(
1012 s.gpa,1108 s.gpa,
1013 request.string.slice(s.code.string_bytes),1109 request.string.slice(s.code.string_bytes),
...@@ -1620,6 +1716,7 @@ const DynamicBitSetUnmanaged = std.DynamicBitSetUnmanaged;...@@ -1620,6 +1716,7 @@ const DynamicBitSetUnmanaged = std.DynamicBitSetUnmanaged;
16201716
1621const core = @import("mod.zig");1717const core = @import("mod.zig");
1622const assertFailed = core.assertFailed;1718const assertFailed = core.assertFailed;
1719const StoredTrace = core.StoredTrace;
16231720
1624const Comp = core.Comp;1721const Comp = core.Comp;
1625const File = Comp.File;1722const File = Comp.File;
src/core/StoredTrace.zig created+65
...@@ -0,0 +1,65 @@
1//! A variant of std.builtin.StackTrace that stores its data within itself
2//! instead of being a pointer. This allows capturing the stack trace and
3//! saving it for later printing.
4
5data: [31]usize,
6index: usize,
7
8pub const empty = .{
9 .data = .{0} ** 31,
10 .index = 0,
11};
12
13pub fn trace(stored: *StoredTrace) std.builtin.StackTrace {
14 return .{
15 .index = stored.index,
16 .instruction_addresses = &stored.data,
17 };
18}
19
20pub fn capture(begin: ?usize) StoredTrace {
21 var stored: StoredTrace = undefined;
22 var frame = stored.trace();
23 std.debug.captureStackTrace(begin orelse @returnAddress(), &frame);
24 stored.index = frame.index;
25 return stored;
26}
27
28pub fn from(stack_trace: ?*std.builtin.StackTrace) StoredTrace {
29 if (stack_trace) |stack| {
30 var data: [31]usize = undefined;
31 @memset(&data, 0);
32 const items = @min(stack.instruction_addresses.len, 32);
33 @memcpy(data[0..items], stack.instruction_addresses[0..items]);
34 return .{
35 .data = data,
36 .index = @min(items, stack.index),
37 };
38 } else {
39 return empty;
40 }
41}
42
43pub const BlankImpl = struct {
44 pub fn trace(_: *BlankImpl) std.builtin.StackTrace {
45 @compileError("trace is not available with StoredTrace.BlankImpl");
46 }
47
48 pub fn capture(_: ?usize) @This() {
49 return .{};
50 }
51
52 pub fn from(_: ?*std.builtin.StackTrace) @This() {
53 return .{};
54 }
55};
56
57pub const DebugStoredTrace = if (builtin.mode == .Debug)
58 StoredTrace
59else
60 BlankImpl;
61
62pub const StoredTrace = @This();
63
64const std = @import("std");
65const builtin = @import("builtin");
src/core/Teg.Dump.zig+48-16
...@@ -68,9 +68,11 @@ const State = struct {...@@ -68,9 +68,11 @@ const State = struct {
68 try writer.writeAll("params: ");68 try writer.writeAll("params: ");
69 try s.dumpRefList(writer, fn_data.params.slice(teg), '[', ']');69 try s.dumpRefList(writer, fn_data.params.slice(teg), '[', ']');
70 try s.newline(writer);70 try s.newline(writer);
71 try writer.writeAll("return_type: ");71 if (fn_data.return_type.unwrap()) |return_type| {
72 try s.dumpRef(writer, fn_data.return_type);72 try writer.writeAll("return_type: ");
73 try s.newline(writer);73 try s.dumpRef(writer, return_type);
74 try s.newline(writer);
75 }
74 },76 },
75 .immutable => {},77 .immutable => {},
76 }78 }
...@@ -129,8 +131,8 @@ const State = struct {...@@ -129,8 +131,8 @@ const State = struct {
129 try s.dumpRefMarker(writer, data.rhs);131 try s.dumpRefMarker(writer, data.rhs);
130 try writer.writeAll(")");132 try writer.writeAll(")");
131 },133 },
132 .as => {134 .coercion => {
133 const data = teg.instData(.as, inst).extra_index.get(teg);135 const data = teg.instData(.coercion, inst).extra_index.get(teg);
134 try writer.writeAll("(");136 try writer.writeAll("(");
135 try s.dumpRefMarker(writer, data.type);137 try s.dumpRefMarker(writer, data.type);
136 try writer.writeAll(", ");138 try writer.writeAll(", ");
...@@ -138,7 +140,7 @@ const State = struct {...@@ -138,7 +140,7 @@ const State = struct {
138 try writer.writeAll(")");140 try writer.writeAll(")");
139 },141 },
140 .@"break" => {142 .@"break" => {
141 const data: Teg.Inst.Break = teg.instData(.@"break", inst).extra_index.get(teg);143 const data: Teg.Break = teg.instData(.@"break", inst).extra_index.get(teg);
142 try writer.writeAll("(");144 try writer.writeAll("(");
143 try s.dumpRefMarker(writer, data.block.toRef());145 try s.dumpRefMarker(writer, data.block.toRef());
144 try writer.writeAll(", ");146 try writer.writeAll(", ");
...@@ -146,7 +148,7 @@ const State = struct {...@@ -146,7 +148,7 @@ const State = struct {
146 try writer.writeAll(")");148 try writer.writeAll(")");
147 },149 },
148 .store => {150 .store => {
149 const data: Teg.Inst.Store = teg.instData(.store, inst).extra_index.get(teg);151 const data: Teg.Store = teg.instData(.store, inst).extra_index.get(teg);
150 try writer.writeAll("(");152 try writer.writeAll("(");
151 try s.dumpRefMarker(writer, data.memory.toRef());153 try s.dumpRefMarker(writer, data.memory.toRef());
152 try writer.writeAll(", ");154 try writer.writeAll(", ");
...@@ -164,7 +166,6 @@ const State = struct {...@@ -164,7 +166,6 @@ const State = struct {
164 inline .memory,166 inline .memory,
165 .discard,167 .discard,
166 .ensure_void,168 .ensure_void,
167 .ensure_bool,
168 .negate,169 .negate,
169 .bool_not,170 .bool_not,
170 .bit_not,171 .bit_not,
...@@ -181,7 +182,7 @@ const State = struct {...@@ -181,7 +182,7 @@ const State = struct {
181 try writer.writeAll(")");182 try writer.writeAll(")");
182 },183 },
183 .call => {184 .call => {
184 const call: Teg.Inst.Call = teg.instData(.call, inst).extra_index.get(teg);185 const call: Teg.Call = teg.instData(.call, inst).extra_index.get(teg);
185 try writer.writeAll("(");186 try writer.writeAll("(");
186 try s.dumpRefMarker(writer, call.base);187 try s.dumpRefMarker(writer, call.base);
187 try writer.writeAll(",");188 try writer.writeAll(",");
...@@ -189,7 +190,7 @@ const State = struct {...@@ -189,7 +190,7 @@ const State = struct {
189 try writer.writeAll(")");190 try writer.writeAll(")");
190 },191 },
191 .js_new => {192 .js_new => {
192 const call: Teg.Inst.JsNew = teg.instData(.js_new, inst).extra_index.get(teg);193 const call: Teg.JsNew = teg.instData(.js_new, inst).extra_index.get(teg);
193 try writer.writeAll("(");194 try writer.writeAll("(");
194 try s.dumpRefMarker(writer, call.base);195 try s.dumpRefMarker(writer, call.base);
195 try writer.writeAll(",");196 try writer.writeAll(",");
...@@ -197,14 +198,14 @@ const State = struct {...@@ -197,14 +198,14 @@ const State = struct {
197 try writer.writeAll(")");198 try writer.writeAll(")");
198 },199 },
199 .load_decl => {200 .load_decl => {
200 const call: Teg.Inst.StrNode = teg.instData(.load_decl, inst);201 const call: Teg.StrNode = teg.instData(.load_decl, inst);
201202
202 try tty.setColor(writer, .red);203 try tty.setColor(writer, .red);
203 try writer.print(" {s}", .{call.string.slice(teg.string_bytes)});204 try writer.print(" {s}", .{call.string.slice(teg.string_bytes)});
204 try tty.setColor(writer, .reset);205 try tty.setColor(writer, .reset);
205 },206 },
206 .@"if" => {207 .@"if" => {
207 const data: Teg.Inst.If = teg.instPayload(.@"if", inst);208 const data: Teg.If = teg.instPayload(.@"if", inst);
208 try writer.writeAll("(");209 try writer.writeAll("(");
209 try s.dumpRefMarker(writer, data.cond);210 try s.dumpRefMarker(writer, data.cond);
210 try writer.writeAll(") {");211 try writer.writeAll(") {");
...@@ -220,13 +221,13 @@ const State = struct {...@@ -220,13 +221,13 @@ const State = struct {
220 try writer.writeAll("}");221 try writer.writeAll("}");
221 },222 },
222 .@"comptime" => {223 .@"comptime" => {
223 const data: Teg.Inst.RefNode = teg.instData(.@"comptime", inst);224 const data: Teg.RefNode = teg.instData(.@"comptime", inst);
224 try writer.writeAll("(");225 try writer.writeAll("(");
225 try s.dumpRef(writer, data.operand);226 try s.dumpRef(writer, data.operand);
226 try writer.writeAll(")");227 try writer.writeAll(")");
227 },228 },
228 .struct_decl => {229 .struct_decl => {
229 const data: Teg.Inst.StructDecl = teg.instPayload(.struct_decl, inst);230 const data: Teg.StructDecl = teg.instPayload(.struct_decl, inst);
230 try writer.writeAll(" {");231 try writer.writeAll(" {");
231 s.indent += 1;232 s.indent += 1;
232 try s.newline(writer);233 try s.newline(writer);
...@@ -241,7 +242,7 @@ const State = struct {...@@ -241,7 +242,7 @@ const State = struct {
241 try writer.writeAll("}");242 try writer.writeAll("}");
242 },243 },
243 .struct_init => {244 .struct_init => {
244 const data: Teg.Inst.StructInit = teg.instPayload(.struct_init, inst);245 const data: Teg.StructInit = teg.instPayload(.struct_init, inst);
245 try writer.writeAll("(");246 try writer.writeAll("(");
246 try s.dumpRefMarker(writer, data.base_type);247 try s.dumpRefMarker(writer, data.base_type);
247 try writer.writeAll(") {");248 try writer.writeAll(") {");
...@@ -258,7 +259,7 @@ const State = struct {...@@ -258,7 +259,7 @@ const State = struct {
258 try writer.writeAll("}");259 try writer.writeAll("}");
259 },260 },
260 .array_init => {261 .array_init => {
261 const data: Teg.Inst.ArrayInit = teg.instPayload(.array_init, inst);262 const data: Teg.ArrayInit = teg.instPayload(.array_init, inst);
262263
263 if (data.type_hint.unwrap()) |type_hint| {264 if (data.type_hint.unwrap()) |type_hint| {
264 try writer.writeAll("(");265 try writer.writeAll("(");
...@@ -284,6 +285,37 @@ const State = struct {...@@ -284,6 +285,37 @@ const State = struct {
284 if (data.type_hint != .none)285 if (data.type_hint != .none)
285 try writer.writeAll(")");286 try writer.writeAll(")");
286 },287 },
288 .result_type_argument => {
289 const data: Teg.ResultTypeArgument = teg.instData(.result_type_argument, inst);
290
291 try writer.writeAll("(");
292 try s.dumpRefMarker(writer, data.call_base);
293 try writer.print(", {d}", .{data.argument});
294 try writer.writeAll(")");
295 },
296 .result_type_struct_field => {
297 const data: Teg.ResultTypeStructField = teg.instData(.result_type_struct_field, inst);
298
299 try writer.writeAll("(");
300 try s.dumpRefMarker(writer, data.type);
301 try writer.print(", {s}", .{data.field.slice(s.teg.string_bytes)});
302 try writer.writeAll(")");
303 },
304 .result_type_tuple_field => {
305 const data: Teg.ResultTypeTupleField = teg.instData(.result_type_tuple_field, inst);
306
307 try writer.writeAll("(");
308 try s.dumpRefMarker(writer, data.type);
309 try writer.print(", {d}", .{data.item});
310 try writer.writeAll(")");
311 },
312 .result_type_array_value => {
313 const data: Teg.ResultTypeArrayValue = teg.instData(.result_type_array_value, inst);
314
315 try writer.writeAll("(");
316 try s.dumpRefMarker(writer, data.type);
317 try writer.writeAll(")");
318 },
287 }319 }
288 }320 }
289321
src/core/Teg.zig+331-281
...@@ -6,183 +6,199 @@ extra: ExtraStore = .{},...@@ -6,183 +6,199 @@ extra: ExtraStore = .{},
6report: Report,6report: Report,
77
8pub const ExtraStore = dod.ExtraStore(.teg, .@"const");8pub const ExtraStore = dod.ExtraStore(.teg, .@"const");
9pub const EncodedData = dod.EncodedData(Inst.Tag, u64);9pub const EncodedData = dod.EncodedData(Tag, u64);
10pub const ExtraIndex = ExtraStore.Index;10pub const ExtraIndex = ExtraStore.Index;
11pub const Dump = @import("Teg.Dump.zig");11pub const Dump = @import("Teg.Dump.zig");
1212
13pub const Inst = struct {13pub const Tag = enum {
14 pub const List = std.MultiArrayList(Inst);14 /// Either the top level module or a `module` keyword. Contains a list of `.declarations`
15 /// Every analyzed module gets a `Comp.Namespace`
16 module,
17 /// Smallest unit of re-compilation. Every analyzed declaration gets a `Comp.Decl`
18 declaration,
19 /// Retrieves a declaration value via it's name. Since declarations are
20 /// analyzed in isolation of each other, all references to instructions
21 /// outside of the current declaration must be in the form of this.
22 load_decl,
23 /// Blocks are a list of instructions. The block will end with a `.break`,
24 /// which will assign the block a return value.
25 block,
26 /// Ends a block (current or any parent) with a value.
27 /// `return` expressions are lowered to this.
28 @"break",
29 /// Creates a memory cell in the current scope with a starting value.
30 memory,
31 /// Re-assign a memory cell with a new ref.
32 store,
33 /// Performs a type coercion, such as in variable declarations.
34 /// The resulting Air Ref will be guaranteed to be the coerced type.
35 coercion,
36 /// Load a parameter
37 param,
38 /// Asserts the operand is not void. Used for discard statements.
39 discard,
40 /// Asserts the operand is `void`. Used for undiscarded expressions.
41 ensure_void,
42 /// Numeric constant.
43 number,
44 /// String literal constant.
45 string,
46 /// Branch takes a boolean condition, and yes and no paths.
47 @"if",
48
49 // The following instructions are used to implement `Transform.ResultType`
50 result_type_argument,
51 result_type_struct_field,
52 result_type_tuple_field,
53 result_type_array_value,
54
55 struct_decl,
56 struct_init,
57
58 array_type,
59 array_init,
60
61 add,
62 sub,
63 mul,
64 div,
65 rem,
66 pow,
67 @"and",
68 @"or",
69 bit_and,
70 bit_or,
71 bit_xor,
72 shl,
73 shr,
74 eq,
75 ne,
76 lt,
77 le,
78 gt,
79 ge,
80
81 negate,
82 bool_not,
83 bit_not,
84 field,
85 index,
86 call,
87 @"comptime",
88
89 /// Implements the `@js` builtin
90 js,
91 /// Implements the `@jsNew` builtin
92 js_new,
93 /// Implements the `@import` builtin
94 import,
95 /// Implements the `@fetch` builtin
96 fetch,
97 /// Implements the `@compileError` builtin
98 compile_error,
99 /// Implements the `@typeOf` builtin
100 type_of,
101
102 pub fn Data(comptime tag: Tag) type {
103 return switch (tag) {
104 .module => PlNode(IndexList),
105 .declaration => PlNode(Decl),
106 .block => PlNode(IndexList),
107 .coercion => PlNode(Coercion),
108 .@"break" => PlNode(Break),
109 .number => PlNode(Float),
110 .string => PlNode(SlicedString),
111 .call => PlNode(Call),
112 .@"if" => PlNode(If),
113 .load_decl => StrNode,
114 .param => DataNode,
115 .store => PlNode(Store),
116 .js_new => PlNode(JsNew),
117 .struct_decl => PlNode(StructDecl),
118 .struct_init => PlNode(StructInit),
119 .array_init => PlNode(ArrayInit),
120
121 .result_type_argument => ResultTypeArgument,
122 .result_type_struct_field => ResultTypeStructField,
123 .result_type_tuple_field => ResultTypeTupleField,
124 .result_type_array_value => ResultTypeArrayValue,
125
126 // Single instruction operand
127 .memory,
128 .discard,
129 .ensure_void,
130 .negate,
131 .bool_not,
132 .bit_not,
133 .js,
134 .import,
135 .fetch,
136 .compile_error,
137 .@"comptime",
138 .type_of,
139 .array_type,
140 => RefNode,
141
142 // Two instruction operands
143 .field,
144 .index,
145 .add,
146 .sub,
147 .mul,
148 .div,
149 .rem,
150 .pow,
151 .@"and",
152 .@"or",
153 .bit_and,
154 .bit_or,
155 .bit_xor,
156 .shl,
157 .shr,
158 .eq,
159 .ne,
160 .lt,
161 .le,
162 .gt,
163 .ge,
164 => PlNode(Bin),
165 };
166 }
15167
16 tag: Tag,168 pub fn airTag(comptime tag: Tag) Air.Inst.Tag {
17 data: EncodedData,169 return @field(Air.Inst.Tag, @tagName(tag));
170 }
171};
18172
19 // TODO: fully document every thing173pub fn PlNode(comptime Payload: type) type {
20 pub const Tag = enum {174 return struct {
21 module,175 pub const T = Payload;
22 declaration,
23
24 block,
25 /// Creates a memory cell in the current scope.
26 memory,
27 /// Performs a type coercion, such as in variable declarations.
28 /// Emits a sema error if the type conversion fails.
29 /// `let x: i32 = 1`
30 as,
31 load_decl,
32 /// Load a parameter
33 param,
34 store,
35 discard,
36 ensure_void,
37 ensure_bool,
38 /// lowered from `break` or `return`
39 /// ends a block with a resulting value
40 @"break",
41 number,
42 string,
43
44 @"if",
45
46 struct_decl,
47 struct_init,
48
49 array_type,
50 array_init,
51
52 add,
53 sub,
54 mul,
55 div,
56 rem,
57 pow,
58 @"and",
59 @"or",
60 bit_and,
61 bit_or,
62 bit_xor,
63 shl,
64 shr,
65 eq,
66 ne,
67 lt,
68 le,
69 gt,
70 ge,
71
72 negate,
73 bool_not,
74 bit_not,
75 field,
76 index,
77 call,
78 @"comptime",
79
80 /// Implements the `@js` builtin
81 js,
82 /// Implements the `@jsNew` builtin
83 js_new,
84 /// Implements the `@import` builtin
85 import,
86 /// Implements the `@fetch` builtin
87 fetch,
88 /// Implements the `@compileError` builtin
89 compile_error,
90 /// Implements the `@typeOf` builtin
91 type_of,
92
93 pub fn Data(comptime tag: Tag) type {
94 return switch (tag) {
95 .module => PlNode(IndexList),
96 .declaration => PlNode(Decl),
97 .block => PlNode(IndexList),
98 .as => PlNode(As),
99 .@"break" => PlNode(Break),
100 .number => PlNode(Float),
101 .string => PlNode(SlicedString),
102 .call => PlNode(Call),
103 .@"if" => PlNode(If),
104 .load_decl => StrNode,
105 .param => DataNode,
106 .store => PlNode(Store),
107 .js_new => PlNode(JsNew),
108 .struct_decl => PlNode(StructDecl),
109 .struct_init => PlNode(StructInit),
110 .array_init => PlNode(ArrayInit),
111
112 // Single instruction operand
113 .memory,
114 .discard,
115 .ensure_void,
116 .ensure_bool,
117 .negate,
118 .bool_not,
119 .bit_not,
120 .js,
121 .import,
122 .fetch,
123 .compile_error,
124 .@"comptime",
125 .type_of,
126 .array_type,
127 => RefNode,
128
129 // Two instruction operands
130 .field,
131 .index,
132 .add,
133 .sub,
134 .mul,
135 .div,
136 .rem,
137 .pow,
138 .@"and",
139 .@"or",
140 .bit_and,
141 .bit_or,
142 .bit_xor,
143 .shl,
144 .shr,
145 .eq,
146 .ne,
147 .lt,
148 .le,
149 .gt,
150 .ge,
151 => PlNode(Bin),
152 };
153 }
154176
155 pub fn airTag(comptime tag: Tag) Air.Inst.Tag {177 src_node: NodeOffset,
156 return @field(Air.Inst.Tag, @tagName(tag));178 extra_index: ExtraIndex(Payload),
157 }
158 };179 };
180}
159181
160 // TODO: make this an enum182pub const RefNode = struct {
161 const NodeOffset = i32;183 src_node: NodeOffset,
162184 operand: Inst.Ref,
163 pub fn PlNode(comptime Payload: type) type {185};
164 return struct {
165 pub const T = Payload;
166186
167 src_node: NodeOffset,187pub const StrNode = struct {
168 extra_index: ExtraIndex(Payload),188 src_node: NodeOffset,
169 };189 string: NullTerminatedString,
170 }190};
171191
172 pub const RefNode = struct {192pub const DataNode = struct {
173 src_node: NodeOffset,193 src_node: NodeOffset,
174 operand: Inst.Ref,194 data: u32,
175 };195};
176196
177 pub const StrNode = struct {197pub const Inst = struct {
178 src_node: NodeOffset,198 pub const List = std.MultiArrayList(Inst);
179 string: NullTerminatedString,
180 };
181199
182 pub const DataNode = struct {200 tag: Tag,
183 src_node: NodeOffset,201 data: EncodedData,
184 data: u32,
185 };
186202
187 /// The position of a Teg instruction within the `Teg` instructions array.203 /// The position of a Teg instruction within the `Teg` instructions array.
188 pub const Index = enum(u32) {204 pub const Index = enum(u32) {
...@@ -197,36 +213,28 @@ pub const Inst = struct {...@@ -197,36 +213,28 @@ pub const Inst = struct {
197 return @enumFromInt(static_len + @intFromEnum(i));213 return @enumFromInt(static_len + @intFromEnum(i));
198 }214 }
199215
200 pub fn toOptional(i: Index) OptionalIndex {216 pub fn toOptional(i: Index) Optional {
201 return @enumFromInt(@intFromEnum(i));217 return @enumFromInt(@intFromEnum(i));
202 }218 }
203219
204 pub fn op(i: Index, operand: Report.Operand) InstOp {220 pub fn op(i: Index, operand: Report.Operand) InstOp {
205 return .{ .index = i, .op = operand };221 return .{ .index = i, .op = operand };
206 }222 }
207 };
208223
209 pub const OptionalIndex = enum(u32) {224 pub const Optional = enum(u32) {
210 /// Teg is structured so that the outermost "main" module of any file225 none = std.math.maxInt(u32),
211 /// is always at index 0.226 _,
212 main_module_inst = 0,
213 ref_start_index = Index.static_len,
214 none = std.math.maxInt(u32),
215 _,
216227
217 pub fn unwrap(oi: OptionalIndex) ?Index {228 pub fn unwrap(oi: Optional) ?Index {
218 return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));229 return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));
219 }230 }
231 };
220 };232 };
221233
222 /// A reference to an instruction, or to an InternPool index, or to neither.234 /// A reference to an instruction, or to a static InternPool value.
223 ///235 ///
224 /// If the integer tag value is < InternPool.static_len, then it236 /// If the integer tag value is < InternPool.static_len, then it
225 /// corresponds to an InternPool index. Otherwise, this refers to a ZIR237 /// corresponds to a Value. Otherwise, it is an instruction index.
226 /// instruction.
227 ///
228 /// The tag type is specified so that it is safe to bitcast between `[]u32`
229 /// and `[]Ref`.
230 pub const Ref = enum(u32) {238 pub const Ref = enum(u32) {
231 opaque_type,239 opaque_type,
232 bool_type,240 bool_type,
...@@ -282,6 +290,10 @@ pub const Inst = struct {...@@ -282,6 +290,10 @@ pub const Inst = struct {
282 return @enumFromInt(ref_int);290 return @enumFromInt(ref_int);
283 }291 }
284292
293 pub fn toOptional(ref: Ref) Optional {
294 return @enumFromInt(@intFromEnum(ref));
295 }
296
285 pub const Optional = enum(u32) {297 pub const Optional = enum(u32) {
286 none = std.math.maxInt(u32),298 none = std.math.maxInt(u32),
287 _,299 _,
...@@ -291,113 +303,143 @@ pub const Inst = struct {...@@ -291,113 +303,143 @@ pub const Inst = struct {
291 }303 }
292 };304 };
293 };305 };
306};
294307
295 pub const Bin = struct {308pub const Bin = struct {
296 lhs: Inst.Ref,309 lhs: Inst.Ref,
297 rhs: Inst.Ref,310 rhs: Inst.Ref,
298 };311};
299312
300 pub const Store = struct {313pub const Store = struct {
301 memory: Inst.Index,314 memory: Inst.Index,
302 operand: Inst.Ref,315 operand: Inst.Ref,
303 };316};
304317
305 pub const As = struct {318pub const Coercion = struct {
306 type: Inst.Ref,319 type: Inst.Ref,
307 operand: Inst.Ref,320 operand: Inst.Ref,
308 };321};
309322
310 pub const Break = struct {323pub const Break = struct {
311 block: Inst.Index,324 block: Inst.Index,
312 operand: Inst.Ref,325 operand: Inst.Ref,
313 };326};
314327
315 pub const Call = struct {328pub const Call = struct {
316 base: Inst.Ref,329 base: Inst.Ref,
317 args: RefList,330 args: RefList,
318 };331};
319332
320 pub const If = struct {333pub const If = struct {
321 cond: Inst.Ref,334 cond: Inst.Ref,
322 yes: Inst.Ref,335 yes: Inst.Ref,
323 no: Inst.Ref,336 no: Inst.Ref,
324 };337};
325338
326 pub const JsNew = struct {339pub const JsNew = struct {
327 base: Inst.Ref,340 base: Inst.Ref,
328 args: RefList,341 args: RefList,
329 };342};
330343
331 /// Index into extra which contains `field_len * 2` u32s. The first half344/// Index into extra which contains `field_len * 2` u32s. The first half
332 /// are the keys (NullTerminatedString) and the second half are field types345/// are the keys (NullTerminatedString) and the second half are field types
333 /// (Ref). This basically is like a specialized MultiArrayList.346/// (Ref). This basically is like a specialized MultiArrayList.
334 pub const StructDecl = struct {347pub const StructDecl = struct {
335 payload_index: u32,348 payload_index: u32,
336 field_len: u32,349 field_len: u32,
337350
338 pub fn keys(decl: StructDecl, teg: *const Teg) []const NullTerminatedString {351 pub fn keys(decl: StructDecl, teg: *const Teg) []const NullTerminatedString {
339 return @ptrCast(352 return @ptrCast(
340 teg.extra.list.items[decl.payload_index..][0..decl.field_len],353 teg.extra.list.items[decl.payload_index..][0..decl.field_len],
341 );354 );
342 }355 }
343356
344 pub fn types(decl: StructDecl, teg: *const Teg) []const Ref {357 pub fn types(decl: StructDecl, teg: *const Teg) []const Inst.Ref {
345 return @ptrCast(358 return @ptrCast(
346 teg.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len],359 teg.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len],
347 );360 );
348 }361 }
349 };362};
350363
351 /// This memory is laid out with the same strategy described in StructDecl364/// This memory is laid out with the same strategy described in StructDecl
352 pub const StructInit = struct {365pub const StructInit = struct {
353 base_type: Ref,366 base_type: Inst.Ref,
354 payload_index: u32,367 payload_index: u32,
355 field_len: u32,368 field_len: u32,
356369
357 pub fn keys(decl: StructInit, teg: *const Teg) []const NullTerminatedString {370 pub fn keys(decl: StructInit, teg: *const Teg) []const NullTerminatedString {
358 return @ptrCast(371 return @ptrCast(
359 teg.extra.list.items[decl.payload_index..][0..decl.field_len],372 teg.extra.list.items[decl.payload_index..][0..decl.field_len],
360 );373 );
361 }374 }
362375
363 pub fn inits(decl: StructInit, teg: *const Teg) []const Ref {376 pub fn inits(decl: StructInit, teg: *const Teg) []const Inst.Ref {
364 return @ptrCast(377 return @ptrCast(
365 teg.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len],378 teg.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len],
366 );379 );
367 }380 }
368 };381};
369382
370 pub const ArrayInit = struct {383pub const ArrayInit = struct {
371 /// Optionally provide a result type for more reliable type coercion.384 /// Optionally provide a result type for more reliable type coercion.
372 /// If specified, this must be an array type. Otherwise, the first385 /// If specified, this must be an array type. Otherwise, the first
373 /// element's type is used.386 /// element's type is used.
374 type_hint: Ref.Optional,387 type_hint: Inst.Ref.Optional,
375 /// The items in the array388 /// The items in the array
376 list: RefList,389 list: RefList,
377 };390};
378391
379 /// This will contain trailing data depending on the .kind392/// This will contain trailing data depending on the .kind
380 pub const Decl = struct {393pub const Decl = struct {
381 name: NullTerminatedString,394 name: NullTerminatedString,
382 hash: Cache.BinDigest,395 hash: Cache.BinDigest,
383 kind: Kind,396 kind: Kind,
384 instruction_count: u32,397 instruction_count: u32,
385 body: Ref,398 body: Inst.Ref,
386
387 pub const Kind = enum {
388 // TODO: remove function, see #45
389 function,
390 immutable,
391 };
392 };
393399
394 /// Trailing data for function declarations400 pub const Kind = enum {
395 pub const DeclFn = struct {401 // TODO: remove function, see #45
396 params: RefList,402 function,
397 return_type: Ref,403 immutable,
398 };404 };
399};405};
400406
407/// Trailing data for function declarations
408pub const DeclFn = struct {
409 params: RefList,
410 return_type: Inst.Ref.Optional,
411};
412
413pub const ResultTypeArgument = struct {
414 /// The VALUE that is going to be called.
415 call_base: Inst.Ref,
416 /// Which argument
417 argument: u32,
418};
419
420pub const ResultTypeStructField = struct {
421 /// The type of the struct itself,
422 type: Inst.Ref,
423 /// Which field
424 field: NullTerminatedString,
425};
426
427pub const ResultTypeTupleField = struct {
428 /// The type of the tuple itself,
429 type: Inst.Ref,
430 /// Which item
431 item: u32,
432};
433
434pub const ResultTypeArrayValue = struct {
435 /// The type of the array itself,
436 type: Inst.Ref,
437 unused: u32 = 0,
438};
439
440// TODO: make this an enum
441const NodeOffset = i32;
442
401pub const InstOp = struct {443pub const InstOp = struct {
402 index: Inst.Index,444 index: Inst.Index,
403 op: Report.Operand,445 op: Report.Operand,
...@@ -470,7 +512,7 @@ pub fn deinit(teg: *Teg, gpa: std.mem.Allocator) void {...@@ -470,7 +512,7 @@ pub fn deinit(teg: *Teg, gpa: std.mem.Allocator) void {
470 teg.* = undefined;512 teg.* = undefined;
471}513}
472514
473pub fn instTag(teg: *const Teg, index: Inst.Index) Inst.Tag {515pub fn instTag(teg: *const Teg, index: Inst.Index) Teg.Tag {
474 return teg.insts.items(.tag)[@intFromEnum(index)];516 return teg.insts.items(.tag)[@intFromEnum(index)];
475}517}
476518
...@@ -478,17 +520,25 @@ pub fn instEncodedData(teg: *const Teg, index: Inst.Index) EncodedData {...@@ -478,17 +520,25 @@ pub fn instEncodedData(teg: *const Teg, index: Inst.Index) EncodedData {
478 return teg.insts.items(.data)[@intFromEnum(index)];520 return teg.insts.items(.data)[@intFromEnum(index)];
479}521}
480522
481pub fn instData(teg: *const Teg, comptime tag: Inst.Tag, index: Inst.Index) tag.Data() {523pub fn instData(teg: *const Teg, comptime tag: Teg.Tag, index: Inst.Index) tag.Data() {
482 return teg.instEncodedData(index).decode(tag, teg.instTag(index));524 return teg.instEncodedData(index).decode(tag, teg.instTag(index));
483}525}
484526
485pub fn instPayload(teg: *const Teg, comptime tag: Inst.Tag, index: Inst.Index) tag.Data().T {527pub fn instPayload(teg: *const Teg, comptime tag: Teg.Tag, index: Inst.Index) tag.Data().T {
486 return teg.instData(tag, index).extra_index.get(teg);528 return teg.instData(tag, index).extra_index.get(teg);
487}529}
488530
489pub fn instNodeOffset(teg: *const Teg, index: Inst.Index) i32 {531pub fn instNodeOffset(teg: *const Teg, index: Inst.Index) i32 {
490 return switch (teg.instTag(index)) {532 return switch (teg.instTag(index)) {
491 .declaration => 0,533 // these nodes never get errors, but Teg.Dump will print these regardless
534 // TODO:
535 .declaration,
536 .result_type_argument,
537 .result_type_struct_field,
538 .result_type_tuple_field,
539 .result_type_array_value,
540 => 0,
541
492 inline else => |tag| teg.instData(tag, index).src_node,542 inline else => |tag| teg.instData(tag, index).src_node,
493 };543 };
494}544}
...@@ -497,10 +547,10 @@ pub fn blockInstructions(teg: *const Teg, index: Inst.Index) []const Inst.Index...@@ -497,10 +547,10 @@ pub fn blockInstructions(teg: *const Teg, index: Inst.Index) []const Inst.Index
497 return teg.instData(.block, index).extra_index.get(teg).slice(teg);547 return teg.instData(.block, index).extra_index.get(teg).slice(teg);
498}548}
499549
500pub fn fullFuncDecl(teg: *const Teg, index: Inst.Index) struct { Inst.Decl, Inst.DeclFn } {550pub fn fullFuncDecl(teg: *const Teg, index: Inst.Index) struct { Teg.Decl, Teg.DeclFn } {
501 const decl, var trail = teg.instData(.declaration, index).extra_index.getTrail(teg);551 const decl, var trail = teg.instData(.declaration, index).extra_index.getTrail(teg);
502 assert(decl.kind == .function);552 assert(decl.kind == .function);
503 const decl_fn = trail.read(&teg.extra, Inst.DeclFn);553 const decl_fn = trail.read(&teg.extra, Teg.DeclFn);
504 return .{ decl, decl_fn };554 return .{ decl, decl_fn };
505}555}
506556
...@@ -512,7 +562,7 @@ pub const FullDecl = struct {...@@ -512,7 +562,7 @@ pub const FullDecl = struct {
512 body: Inst.Ref,562 body: Inst.Ref,
513563
514 const Data = union(enum) {564 const Data = union(enum) {
515 function: Inst.DeclFn,565 function: Teg.DeclFn,
516 immutable: struct {},566 immutable: struct {},
517 };567 };
518};568};
...@@ -537,7 +587,7 @@ pub fn fullDecl(teg: *const Teg, index: Inst.Index) FullDecl {...@@ -537,7 +587,7 @@ pub fn fullDecl(teg: *const Teg, index: Inst.Index) FullDecl {
537 };587 };
538}588}
539589
540pub fn declData(teg: *const Teg, index: Inst.Index) Inst.Decl {590pub fn declData(teg: *const Teg, index: Inst.Index) Teg.Decl {
541 return teg.instData(.declaration, index).extra_index.get(teg);591 return teg.instData(.declaration, index).extra_index.get(teg);
542}592}
543593
...@@ -565,7 +615,7 @@ pub fn rootFunction(teg: *const Teg) Inst.Index {...@@ -565,7 +615,7 @@ pub fn rootFunction(teg: *const Teg) Inst.Index {
565615
566pub fn declName(teg: *const Teg, decl_index: Inst.Index) []const u8 {616pub fn declName(teg: *const Teg, decl_index: Inst.Index) []const u8 {
567 const decl = teg.instData(.declaration, decl_index);617 const decl = teg.instData(.declaration, decl_index);
568 const decl_data: Inst.Decl = decl.extra_index.get(teg);618 const decl_data: Teg.Decl = decl.extra_index.get(teg);
569 return decl_data.name.slice(teg.string_bytes);619 return decl_data.name.slice(teg.string_bytes);
570}620}
571621
src/core/Transform.zig+660-532
...@@ -1,26 +1,35 @@...@@ -1,26 +1,35 @@
1//! Transform pass converts an Ast into a high-level/untyped instruction format.1//! Transform pass converts an Ast into a high-level/untyped instruction format.
2//! See Teg.zig for more details about the IR. Transforming handles:2//! See Teg.zig for more details about the IR. Transforming handles:
3//!3//!
4//! - Flattening nested expressions into instruction sequences.
4//! - Resolving all local variables into reference or declaration names.5//! - Resolving all local variables into reference or declaration names.
5//! - Resolving breaks and returns to their block id.6//! - Resolving breaks and returns to their generated block id.
6//! - Annotating expression statements with `.discard` and `.ensure_void`7//! - Annotating expression statements with `.discard` and `.ensure_void`
7//! - Flattening nested expressions into instruction sequences.8//! - Resolving implicit expression with their corresponding result type
8//!9//!
9//! Some trivial diagnostics such as unused variables, unknown result types,10//! Some trivial diagnostics such as unused variables, unknown result types,
10//! invalid assignment targets, and other style issues are also reported here.11//! invalid assignment targets, and other style issues are also reported here.
12
11gpa: Allocator,13gpa: Allocator,
14arena: Allocator,
15
12ast: Ast,16ast: Ast,
17
18// these values are allocated through `gpa`, so that resulting IR can be
19// trivially shrunk before returning.
20
13insts: Inst.List = .{},21insts: Inst.List = .{},
14string_bytes: std.ArrayListUnmanaged(u8) = .{},
15extra: ExtraStore = .{},22extra: ExtraStore = .{},
23string_bytes: std.ArrayListUnmanaged(u8) = .{},
24report: Report.Builder,
1625
17scratch: std.ArrayListUnmanaged(u32) = .{},26// however, these are allocated through `arena`, since they are always freed at
18id_decode_buf: std.ArrayList(u8),27// the end of the pass.
1928
29scratch: core.U32Stack = .{},
30id_decode_buf: std.ArrayList(u8),
20scopes: std.ArrayListUnmanaged(Scope) = .{},31scopes: std.ArrayListUnmanaged(Scope) = .{},
2132
22report: Report.Builder,
23
24pub const Error = error{33pub const Error = error{
25 /// A `Report` will contain at least one error.34 /// A `Report` will contain at least one error.
26 TransformFail,35 TransformFail,
...@@ -40,6 +49,8 @@ pub fn transform(...@@ -40,6 +49,8 @@ pub fn transform(
40 var t: Transform = .{49 var t: Transform = .{
41 .ast = ast,50 .ast = ast,
42 .gpa = gpa,51 .gpa = gpa,
52 // TODO: Currently this is illusion of free will
53 .arena = gpa,
43 .report = .{ .gpa = gpa, .current_file = file },54 .report = .{ .gpa = gpa, .current_file = file },
44 .id_decode_buf = std.ArrayList(u8).initCapacity(sfa.get(), 4096) catch unreachable,55 .id_decode_buf = std.ArrayList(u8).initCapacity(sfa.get(), 4096) catch unreachable,
45 };56 };
...@@ -57,9 +68,7 @@ pub fn transform(...@@ -57,9 +68,7 @@ pub fn transform(
57 return error.TransformFail;68 return error.TransformFail;
58 }69 }
5970
60 assert(@intFromEnum(module) == 0);71 assert(module == .main_module_inst);
61
62 try t.insts.setCapacity(t.gpa, t.insts.len);
6372
64 return .{73 return .{
65 .insts = t.insts.toOwnedSlice(),74 .insts = t.insts.toOwnedSlice(),
...@@ -69,84 +78,82 @@ pub fn transform(...@@ -69,84 +78,82 @@ pub fn transform(
69 };78 };
70}79}
7180
72pub fn deinit(t: *Transform) void {81fn deinit(t: *Transform) void {
73 t.scratch.deinit(t.gpa);
74 assert(t.scopes.items.len == 0);82 assert(t.scopes.items.len == 0);
75 t.scopes.deinit(t.gpa);83 t.id_decode_buf.deinit();
84 t.scratch.list.deinit(t.arena);
85 t.scopes.deinit(t.arena);
86 t.id_decode_buf.deinit();
76 t.string_bytes.deinit(t.gpa);87 t.string_bytes.deinit(t.gpa);
77 t.insts.deinit(t.gpa);88 t.insts.deinit(t.gpa);
78 t.extra.deinit(t.gpa);89 t.extra.deinit(t.gpa);
79 t.id_decode_buf.deinit();
80 t.* = undefined;90 t.* = undefined;
81}91}
8292
83const Scope = struct {93/// Represents how the parent node is going to implicitly cast this expression.
84 symbols: std.StringArrayHashMapUnmanaged(Symbol) = .{},94///
8595/// Consider an expression like `let x: S = .{ y: 1 }`. The type from the
86 const Symbol = struct {96/// declaration must impact the visited struct initialization expression. This
87 node: NodeIndex,97/// behavior is recursive, so something more complex can properly infer:
88 data: Symbol.Data,98///
89 // TODO: optimize this structure99/// struct S { a: int }
90 used: bool = false,100/// struct Y {}
91 mutated: bool = false,101/// let x: [](S, []Y) = [ .{ .{ a: 1 }, [ .{} ] } ];
92 discarded: ?NodeIndex = null,102///
93103/// The innermost `.{}` must eventually analyze to `Y{}`, which can be unwrapped
94 const Data = union(enum) {104/// from the type expression via specialized instructions for this purpose.
95 declaration,105const ResultType = union(enum) {
96 param,106 /// There is no result type available
97 mutable: Inst.Index,107 none,
98 immutable: Inst.Ref,108 /// The result type was consumed
109 used,
110 /// The expression will be cast to this ref.
111 ref: Inst.Ref,
112 /// The expression will be cast to the type of this ref
113 type_of: Inst.Ref,
114 /// The expression is an argument to a function.
115 argument: Teg.ResultTypeArgument,
116 /// The expression is a field in a struct initialization
117 struct_field: Teg.ResultTypeStructField,
118 /// The expression is a field in a tuple initialization
119 tuple_field: Teg.ResultTypeTupleField,
120 /// The expression is a field in an array initialization
121 array_value: Inst.Ref,
122
123 /// This will return an instruction pointing to the the type
124 pub fn consume(result: *ResultType, t: *Transform) !?Inst.Ref {
125 if (result.* == .none) return null;
126 defer result.* = .used;
127 return switch (result.*) {
128 .none => unreachable,
129 .used => unreachable, // Use after free
130 .ref => |r| r,
131 .type_of => |r| try t.addRef(.type_of, .{
132 // Result types don't have associated nodes, which is ok because
133 // type_of cannot fail in the context we are using it. Instead,
134 // we will use a predictable poison value, so that an attempt
135 // to print this will crash.
136 .src_node = std.math.maxInt(i32),
137 .operand = r,
138 }),
139 .argument => |r| try t.addRef(.result_type_argument, r),
140 .struct_field => |r| try t.addRef(.result_type_struct_field, r),
141 .tuple_field => |r| try t.addRef(.result_type_tuple_field, r),
142 .array_value => |r| try t.addRef(.result_type_array_value, .{ .type = r }),
99 };143 };
100
101 pub fn addWarnings(symbol: Symbol, t: *Transform) !void {
102 if (!symbol.mutated and symbol.data == .mutable) {
103 try t.report.warn(.not_mutated_var, .{
104 .node = symbol.node,
105 });
106 }
107 if (!symbol.used) {
108 try t.report.warn(.unused, .{
109 .node = symbol.node,
110 .kind = switch (symbol.data) {
111 .declaration => .function,
112 .param => .param,
113 .mutable => .variable,
114 .immutable => switch (t.ast.nodeTag(symbol.node)) {
115 .param_decl => .param,
116 else => .constant,
117 },
118 },
119 });
120 } else if (symbol.discarded) |discard| {
121 try t.report.warn(.unnecessary_discard, .{
122 .node = discard,
123 });
124 }
125 }
126 };
127
128 fn addWarnings(s: Scope, t: *Transform) !void {
129 for (s.symbols.values()) |value| {
130 try value.addWarnings(t);
131 }
132 }
133
134 fn deinit(s: *Scope, t: *Transform) void {
135 for (s.symbols.keys()) |k| {
136 if (!isSliceInBuffer(u8, t.ast.utf8_source, k)) {
137 t.gpa.free(k);
138 }
139 }
140 s.symbols.deinit(t.gpa);
141 s.* = undefined;
142 }144 }
143};145};
144146
145const BlockContext = struct {147const BlockContext = struct {
146 node_base: i32,148 decl_node_offset: i32,
147 return_target: ?*?Inst.Index = null,149 return_target: ?*ReturnTarget = null,
148 break_target: ?*BreakTarget = null,150 break_target: ?*BreakTarget = null,
149151
152 const ReturnTarget = struct {
153 generated_block: ?Inst.Index = null,
154 result_type: ResultType,
155 };
156
150 /// This struct is always stored on the stack; the idea is to pass mutable157 /// This struct is always stored on the stack; the idea is to pass mutable
151 /// pointers to it. When `break :brk` is encountered, it will generate the158 /// pointers to it. When `break :brk` is encountered, it will generate the
152 /// block instruction. This way, redundant blocks are never created.159 /// block instruction. This way, redundant blocks are never created.
...@@ -157,6 +164,7 @@ const BlockContext = struct {...@@ -157,6 +164,7 @@ const BlockContext = struct {
157 /// If `null`, this label was unused.164 /// If `null`, this label was unused.
158 block_index: ?Inst.Index = null,165 block_index: ?Inst.Index = null,
159 parent: ?*BreakTarget = null,166 parent: ?*BreakTarget = null,
167 result_type: *ResultType,
160168
161 fn addChild(parent: *BreakTarget, label: ?[]const u8, is_loop: bool) BreakTarget {169 fn addChild(parent: *BreakTarget, label: ?[]const u8, is_loop: bool) BreakTarget {
162 return .{170 return .{
...@@ -168,10 +176,7 @@ const BlockContext = struct {...@@ -168,10 +176,7 @@ const BlockContext = struct {
168176
169 fn getInst(target: *BreakTarget, t: *Transform) !Inst.Index {177 fn getInst(target: *BreakTarget, t: *Transform) !Inst.Index {
170 return target.block_index orelse {178 return target.block_index orelse {
171 const inst = try t.addRootInst(.{179 const inst = try t.addUntracked(.block, undefined);
172 .tag = .block,
173 .data = undefined,
174 });
175 target.block_index = inst;180 target.block_index = inst;
176 return inst;181 return inst;
177 };182 };
...@@ -180,41 +185,41 @@ const BlockContext = struct {...@@ -180,41 +185,41 @@ const BlockContext = struct {
180185
181 fn init(root_node: NodeIndex) BlockContext {186 fn init(root_node: NodeIndex) BlockContext {
182 return .{187 return .{
183 .node_base = @intCast(root_node),188 .decl_node_offset = @intCast(root_node),
184 };189 };
185 }190 }
186191
187 fn nodeOffset(self: BlockContext, node: NodeIndex) i32 {192 fn nodeOffset(self: BlockContext, node: NodeIndex) i32 {
188 return @as(i32, @intCast(node)) - self.node_base;193 return @as(i32, @intCast(node)) - self.decl_node_offset;
189 }194 }
190195
191 fn declOffset(self: BlockContext, node: NodeIndex) BlockContext {196 fn declOffset(self: BlockContext, node: NodeIndex) BlockContext {
192 return .{197 return .{
193 .node_base = self.nodeOffset(node),198 .decl_node_offset = self.nodeOffset(node),
194 };199 };
195 }200 }
196201
197 fn withReturn(self: BlockContext, return_target: ?*?Inst.Index) BlockContext {202 fn withReturn(self: BlockContext, return_target: ?*ReturnTarget) BlockContext {
198 return .{203 return .{
199 .node_base = self.node_base,204 .decl_node_offset = self.decl_node_offset,
200 .return_target = return_target,205 .return_target = return_target,
201 };206 };
202 }207 }
203208
204 fn withBreak(self: BlockContext, break_target: *BreakTarget) BlockContext {209 fn withBreak(self: BlockContext, break_target: *BreakTarget) BlockContext {
205 return .{210 return .{
206 .node_base = self.node_base,211 .decl_node_offset = self.decl_node_offset,
207 .return_target = self.return_target,212 .return_target = self.return_target,
208 .break_target = break_target,213 .break_target = break_target,
209 };214 };
210 }215 }
211216
212 fn findAndMarkBreak(self: BlockContext, t: *Transform, label: []const u8) !?Inst.Index {217 fn lookupNamedBreak(self: BlockContext, label: []const u8) ?*BreakTarget {
213 var break_target = self.break_target;218 var break_target = self.break_target;
214 while (break_target) |target| : (break_target = target.parent) {219 while (break_target) |target| : (break_target = target.parent) {
215 if (target.label) |target_label| {220 if (target.label) |target_label| {
216 if (mem.eql(u8, label, target_label)) {221 if (mem.eql(u8, label, target_label)) {
217 return try target.getInst(t);222 return target;
218 }223 }
219 }224 }
220 }225 }
...@@ -222,6 +227,84 @@ const BlockContext = struct {...@@ -222,6 +227,84 @@ const BlockContext = struct {
222 }227 }
223};228};
224229
230const Scope = struct {
231 symbols: std.StringArrayHashMapUnmanaged(Symbol) = .{},
232
233 // TODO: optimize this structure's size.
234 const Symbol = struct {
235 node: NodeIndex,
236 data: Symbol.Data,
237 used: bool = false,
238 mutated: bool = false,
239 discarded: ?NodeIndex = null,
240 type: SymbolResultType,
241
242 const Data = union(enum) {
243 declaration,
244 param,
245 mutable: Inst.Index,
246 immutable: Inst.Ref,
247 };
248
249 const SymbolResultType = union(enum) {
250 /// Points to the resolved result type (.ref), as is.
251 present: Inst.Ref,
252 /// This points to the value. Which means a `type_of` instruction
253 /// must be inserted before it may be used.
254 missing: Inst.Ref,
255
256 pub fn resolve(srt: SymbolResultType) ResultType {
257 return switch (srt) {
258 .present => |type_ref| .{ .ref = type_ref },
259 .missing => |value_ref| .{ .type_of = value_ref },
260 };
261 }
262 };
263
264 pub fn addWarnings(symbol: Symbol, t: *Transform) !void {
265 if (!symbol.mutated and symbol.data == .mutable) {
266 try t.report.warn(.not_mutated_var, .{
267 .node = symbol.node,
268 });
269 }
270 if (!symbol.used) {
271 try t.report.warn(.unused, .{
272 .node = symbol.node,
273 .kind = switch (symbol.data) {
274 .declaration => .function,
275 .param => .param,
276 .mutable => .variable,
277 .immutable => switch (t.ast.nodeTag(symbol.node)) {
278 .param_decl => .param,
279 else => .constant,
280 },
281 },
282 });
283 } else if (symbol.discarded) |discard| {
284 try t.report.warn(.unnecessary_discard, .{
285 .node = discard,
286 });
287 }
288 }
289 };
290
291 fn addWarnings(s: Scope, t: *Transform) !void {
292 for (s.symbols.values()) |value| {
293 try value.addWarnings(t);
294 }
295 }
296
297 fn deinit(s: *Scope, t: *Transform) void {
298 for (s.symbols.keys()) |k| {
299 if (!isSliceInBuffer(u8, t.ast.utf8_source, k)) {
300 t.gpa.free(k);
301 }
302 }
303 s.symbols.deinit(t.gpa);
304 s.* = undefined;
305 }
306};
307
225const Liveness = enum { alive, dead };308const Liveness = enum { alive, dead };
226309
227const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{310const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{
...@@ -242,10 +325,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I...@@ -242,10 +325,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
242 const trace = tracer.trace(@src());325 const trace = tracer.trace(@src());
243 defer trace.end();326 defer trace.end();
244327
245 const module = try t.addRootInst(.{328 const module = try t.addUntracked(.module, undefined);
246 .tag = .module,
247 .data = undefined,
248 });
249329
250 const decl = BlockContext.init(module_node);330 const decl = BlockContext.init(module_node);
251331
...@@ -271,10 +351,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I...@@ -271,10 +351,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
271 }351 }
272 }352 }
273353
274 const top_level_fn = try t.addRootInst(.{354 const top_level_fn = try t.addUntracked(.declaration, undefined);
275 .tag = .declaration,
276 .data = undefined,
277 });
278355
279 temp[temp.len - 1] = top_level_fn;356 temp[temp.len - 1] = top_level_fn;
280357
...@@ -285,42 +362,39 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I...@@ -285,42 +362,39 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
285 try t.extra.list.appendSlice(t.gpa, @ptrCast(temp));362 try t.extra.list.appendSlice(t.gpa, @ptrCast(temp));
286363
287 const top_level_code_ref: Inst.Ref = ref: {364 const top_level_code_ref: Inst.Ref = ref: {
288 const scratch_top = t.scratch.items.len;365 const scratch = t.scratch.mark();
289 defer t.scratch.shrinkRetainingCapacity(scratch_top);366 defer scratch.deinit();
290367
291 for (statements) |statement| {368 for (statements) |statement| {
292 try t.transformModuleStatement(decl, statement);369 try t.transformModuleStatement(decl, statement);
293 }370 }
294371
295 // if there are no new instructions, we dont have to create a block context372 // if there are no new instructions, we dont have to create a block context
296 if (scratch_top == t.scratch.items.len) {373 if (scratch.len() == 0) {
297 break :ref .void;374 break :ref .void;
298 }375 }
299376
300 const block = try t.addRootInst(.{377 const block = try t.addUntracked(.block, undefined);
301 .tag = .block,
302 .data = undefined,
303 });
304378
305 t.getInstData(block).* = EncodedData.encode(.block, .{379 t.getInstData(block).* = EncodedData.encode(.block, .{
306 .src_node = decl.nodeOffset(module_node),380 .src_node = decl.nodeOffset(module_node),
307 .extra_index = try t.extra.append(t.gpa, IndexList, try t.scratchToIndexList(scratch_top)),381 .extra_index = try t.extra.append(t.gpa, IndexList, try t.scratchToIndexList(scratch)),
308 });382 });
309383
310 break :ref block.toRef();384 break :ref block.toRef();
311 };385 };
312386
313 const decl_extra, var trail = try t.extra.appendTrailing(t.gpa, Inst.Decl, .{387 const decl_extra, var trail = try t.extra.appendTrailing(t.gpa, Teg.Decl, .{
314 .kind = .function,388 .kind = .function,
315 .name = NullTerminatedString.empty,389 .name = NullTerminatedString.empty,
316 .hash = .{0} ** 16,390 .hash = .{0} ** 16,
317 .instruction_count = @as(u32, @intCast(t.insts.len)) - @intFromEnum(top_level_fn),391 .instruction_count = @as(u32, @intCast(t.insts.len)) - @intFromEnum(top_level_fn),
318 .body = top_level_code_ref,392 .body = top_level_code_ref,
319 }, dod.extraSizeOf(Inst.DeclFn));393 }, extra.extraSizeOf(Teg.DeclFn));
320394
321 trail.writeStruct(Inst.DeclFn, .{395 trail.writeStruct(Teg.DeclFn, .{
322 .params = .{ .start = 0, .len = 0 },396 .params = .{ .start = 0, .len = 0 },
323 .return_type = Inst.Ref.void,397 .return_type = Inst.Ref.void.toOptional(),
324 });398 });
325399
326 t.getInstData(top_level_fn).* = EncodedData.encode(.declaration, .{400 t.getInstData(top_level_fn).* = EncodedData.encode(.declaration, .{
...@@ -361,10 +435,14 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void...@@ -361,10 +435,14 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void
361 gop.value_ptr.* = .{435 gop.value_ptr.* = .{
362 .node = node,436 .node = node,
363 .data = .declaration,437 .data = .declaration,
438 .type = .{ .present = .void },
364 };439 };
365 },440 },
366 .struct_decl => {441 .struct_decl => {
367 const decl = t.ast.fullStructDecl(node);442 const decl = t.ast.fullStructDecl(node);
443 var id, const id_allocated = try t.decodeIdentifierExtra(decl.id);
444 if (id_allocated)
445 id = try t.gpa.dupe(u8, id);
368 const gop = try t.scope().symbols.getOrPut(t.gpa, decl.id);446 const gop = try t.scope().symbols.getOrPut(t.gpa, decl.id);
369 if (gop.found_existing) {447 if (gop.found_existing) {
370 try t.report.err(.duplicate_function, .{448 try t.report.err(.duplicate_function, .{
...@@ -377,6 +455,7 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void...@@ -377,6 +455,7 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void
377 gop.value_ptr.* = .{455 gop.value_ptr.* = .{
378 .node = node,456 .node = node,
379 .data = .declaration,457 .data = .declaration,
458 .type = .{ .present = .void },
380 };459 };
381 },460 },
382 else => {},461 else => {},
...@@ -397,14 +476,14 @@ fn transformModuleStatement(t: *Transform, decl: BlockContext, node: NodeIndex)...@@ -397,14 +476,14 @@ fn transformModuleStatement(t: *Transform, decl: BlockContext, node: NodeIndex)
397 .struct_decl,476 .struct_decl,
398 => {}, // declarations are analyzed separately477 => {}, // declarations are analyzed separately
399478
400 else => _ = try t.transformBlockStatement(decl, node, false),479 else => _ = try t.transformBlockStatement(decl, node, null),
401 }480 }
402}481}
403482
404fn transformDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index {483fn transformDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index {
405 return switch (t.ast.nodeTag(node)) {484 return switch (t.ast.nodeTag(node)) {
406 .fn_decl => t.transformFunction(outer_decl, node),485 .fn_decl => t.transformFunction(outer_decl, node),
407 .struct_decl => t.transformStruct(outer_decl, node),486 .struct_decl => t.transformStructDeclaration(outer_decl, node),
408 else => unreachable, // not a declaration487 else => unreachable, // not a declaration
409 };488 };
410}489}
...@@ -416,33 +495,40 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A...@@ -416,33 +495,40 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
416 const decl = outer_decl.declOffset(node);495 const decl = outer_decl.declOffset(node);
417 const fn_decl = t.ast.fullFnDecl(node);496 const fn_decl = t.ast.fullFnDecl(node);
418497
419 const decl_inst = try t.addRootInst(.{498 const decl_inst = try t.addUntracked(.declaration, undefined);
420 .tag = .declaration,
421 .data = undefined,
422 });
423499
424 try t.pushScope();500 try t.pushScope();
425 defer t.popScope();501 defer t.popScope();
426502
427 const param_list = brk: {503 const param_list = brk: {
428 const scratch_top = t.scratch.items.len;504 const scratch = t.scratch.mark();
429 defer t.scratch.shrinkRetainingCapacity(scratch_top);505 defer scratch.deinit();
430506
431 for (fn_decl.params) |param| {507 for (fn_decl.params) |param| {
432 try t.addScratch(Inst.Ref, try t.transformParamDecl(decl, param));508 try t.scratch.add(t.arena, Inst.Ref, try t.transformParamDecl(decl, param));
433 }509 }
434510
435 break :brk try t.scratchToRefList(scratch_top);511 break :brk try t.scratchToRefList(scratch);
436 };512 };
437513
438 const return_type = if (fn_decl.return_type) |return_type_node|514 const return_type = if (fn_decl.return_type) |return_type_node|
439 try t.transformExpressionToBlock(decl, return_type_node, false)515 try t.transformToBlock(
516 decl,
517 return_type_node,
518 .result_without_coercion,
519 .{ .ref = .type_type },
520 )
440 else521 else
441 Inst.Ref.void;522 null;
442523
443 const body = try t.transformExpressionToBlock(decl, fn_decl.body, true);524 const body = try t.transformToBlock(
525 decl,
526 fn_decl.body,
527 .function_body,
528 if (return_type) |ref| .{ .ref = ref } else .none,
529 );
444530
445 const decl_extra = try t.extra.append(t.gpa, Inst.Decl, .{531 const decl_extra = try t.extra.append(t.gpa, Teg.Decl, .{
446 .kind = .function,532 .kind = .function,
447 .name = try t.addNullTerminatedString(fn_decl.id),533 .name = try t.addNullTerminatedString(fn_decl.id),
448 .hash = .{0} ** 16,534 .hash = .{0} ** 16,
...@@ -450,9 +536,9 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A...@@ -450,9 +536,9 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
450 .body = body,536 .body = body,
451 });537 });
452538
453 _ = try t.extra.append(t.gpa, Inst.DeclFn, .{539 _ = try t.extra.append(t.gpa, Teg.DeclFn, .{
454 .params = param_list,540 .params = param_list,
455 .return_type = return_type,541 .return_type = if (return_type) |r| r.toOptional() else .none,
456 });542 });
457543
458 t.getInstData(decl_inst).* = EncodedData.encode(.declaration, .{544 t.getInstData(decl_inst).* = EncodedData.encode(.declaration, .{
...@@ -465,21 +551,18 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A...@@ -465,21 +551,18 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
465 return decl_inst;551 return decl_inst;
466}552}
467553
468fn transformStruct(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index {554fn transformStructDeclaration(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index {
469 const trace = tracer.trace(@src());555 const trace = tracer.trace(@src());
470 defer trace.end();556 defer trace.end();
471557
472 const decl = outer_decl.declOffset(node);558 const decl = outer_decl.declOffset(node);
473 const decl_data = t.ast.fullStructDecl(node);559 const decl_data = t.ast.fullStructDecl(node);
474560
475 const decl_inst = try t.addRootInst(.{561 const decl_inst = try t.addUntracked(.declaration, undefined);
476 .tag = .declaration,
477 .data = undefined,
478 });
479562
480 const body = try t.transformExpressionToBlock(decl, node, false);563 const body = try t.transformToBlock(decl, node, .result_without_coercion, .none);
481564
482 const decl_extra = try t.extra.append(t.gpa, Inst.Decl, .{565 const decl_extra = try t.extra.append(t.gpa, Teg.Decl, .{
483 .kind = .immutable,566 .kind = .immutable,
484 .name = try t.addNullTerminatedString(decl_data.id),567 .name = try t.addNullTerminatedString(decl_data.id),
485 .hash = .{0} ** 16,568 .hash = .{0} ** 16,
...@@ -495,76 +578,83 @@ fn transformStruct(t: *Transform, outer_decl: BlockContext, node: NodeIndex) All...@@ -495,76 +578,83 @@ fn transformStruct(t: *Transform, outer_decl: BlockContext, node: NodeIndex) All
495 return decl_inst;578 return decl_inst;
496}579}
497580
498const TransformFunctionCtx = struct {581const TransformToBlockMode = enum {
499 param_list: RefList,582 tracked_result,
583 result_without_coercion,
584 coerced_result,
585 function_body,
586
587 fn ResultType(mode: TransformToBlockMode) type {
588 return switch (mode) {
589 .tracked_result => *Transform.ResultType,
590 else => Transform.ResultType,
591 };
592 }
500};593};
501594
502/// Converts expressions into blocks with a break. This is important595/// guh
503/// for places where a block is not allowed, such as a parameter list.596fn transformToBlock(
504/// This is also used for the function body, even though it is a block.
505/// Simple refs turn into just the ref
506fn transformExpressionToBlock(
507 t: *Transform,597 t: *Transform,
508 decl: BlockContext,598 ctx: BlockContext,
509 node: NodeIndex,599 node: NodeIndex,
510 comptime is_function: bool,600 comptime mode: TransformToBlockMode,
601 result_type: mode.ResultType(),
511) Allocator.Error!Inst.Ref {602) Allocator.Error!Inst.Ref {
512 const trace = tracer.trace(@src());603 const trace = tracer.trace(@src());
513 defer trace.end();604 defer trace.end();
514605
515 const node_tag = t.ast.nodes.items(.tag)[node];606 const scratch = t.scratch.mark();
516 switch (node_tag) {607 defer scratch.deinit();
517 .id => {
518 // Lower builtin ids without using a block
519 const id = try t.decodeIdentifier(t.ast.basicNodeSlice(node));
520 if (BuiltInIds.get(id)) |ref| {
521 return ref;
522 }
523 },
524
525 else => {},
526 }
527
528 var block: ?Inst.Index = null;
529608
530 const scratch_top = t.scratch.items.len;609 // For function blocks, add .param instructions for all of the parameters
531 defer t.scratch.shrinkRetainingCapacity(scratch_top);610 if (mode == .function_body) {
532
533 if (is_function) {
534 const s = t.scopes.getLast();611 const s = t.scopes.getLast();
535 for (s.symbols.values(), 0..) |*param, i| {612 for (s.symbols.values(), 0..) |*param, i| {
536 assert(param.data == .param);613 assert(param.data == .param);
537 const ref = try t.addInstRef(.{614 const ref = try t.addRef(.param, .{
538 .tag = .param,615 .src_node = ctx.nodeOffset(node),
539 .data = EncodedData.encode(.param, .{616 .data = @intCast(i),
540 .src_node = decl.nodeOffset(node),
541 .data = @intCast(i),
542 }),
543 });617 });
544 param.data = .{ .immutable = ref };618 param.data = .{ .immutable = ref };
545 }619 }
546 }620 }
547621
622 // The result type is only passed by reference if mode is tracked_result.
623 const copies_result_type = mode.ResultType() == ResultType;
624 var result_type_storage = if (copies_result_type) result_type;
625
626 var ret = if (mode == .function_body)
627 BlockContext.ReturnTarget{ .result_type = result_type_storage };
628
548 const expr = try t.transformExpression(629 const expr = try t.transformExpression(
549 if (is_function)630 // Function blocks are allowed to return, in which
550 decl.withReturn(&block)631 // it will return to this block.
632 if (mode == .function_body)
633 ctx.withReturn(&ret)
551 else634 else
552 decl,635 ctx,
636 // Pass result type pointer
637 if (copies_result_type)
638 &result_type_storage
639 else
640 result_type,
553 node,641 node,
554 );642 );
555643
556 // if there are no new instructions, we dont have to create a block context644 // if there are no new instructions, we dont have to create a block context
557 if (scratch_top == t.scratch.items.len) {645 if (scratch.len() == 0) {
558 const result = expr orelse unreachable; // null means we *added* break or return646 const result = expr orelse
647 unreachable; // null means we *added* break or return
559648
560 // TODO: evaluate if `if` blocks would be fine in sema with instruction refs
561 // with this extra if check here, we filter out non-blocks.
562 if (result.value() != null) {649 if (result.value() != null) {
563 assert(block == null);650 if (mode == .function_body)
651 assert(ret.generated_block == null); // return was called without an instruction
652
564 return result;653 return result;
565 }654 }
566 }655 }
567656
657 // If we analyzed a block, then just use that. No need for an extra wrapper block.
568 if (expr) |result| {658 if (expr) |result| {
569 if (result.index()) |index| {659 if (result.index()) |index| {
570 if (t.insts.items(.tag)[@intFromEnum(index)] == .block) {660 if (t.insts.items(.tag)[@intFromEnum(index)] == .block) {
...@@ -573,32 +663,26 @@ fn transformExpressionToBlock(...@@ -573,32 +663,26 @@ fn transformExpressionToBlock(
573 }663 }
574 }664 }
575665
576 const ret_block = if (block) |b|666 // Otherwise try using the function return block, or create a new one
577 b667 const maybe_generated_block = if (mode == .function_body) ret.generated_block else null;
578 else668 const ret_block = maybe_generated_block orelse try t.addUntracked(.block, undefined);
579 try t.addRootInst(.{
580 .tag = .block,
581 .data = undefined,
582 });
583669
670 // The block must end with a break
584 if (expr) |result| {671 if (expr) |result| {
585 _ = try t.addInstRef(.{672 _ = try t.addIndex(.@"break", .{
586 .tag = .@"break",673 .src_node = ctx.nodeOffset(node),
587 .data = EncodedData.encode(.@"break", .{674 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
588 .src_node = decl.nodeOffset(node),675 .block = ret_block,
589 .extra_index = try t.extra.append(t.gpa, Inst.Break, .{676 .operand = result,
590 .block = ret_block,
591 .operand = result,
592 }),
593 }),677 }),
594 });678 });
595 }679 }
596680
597 const content = try t.scratchToIndexList(scratch_top);681 const content = try t.scratchToIndexList(scratch);
598 assert(content.len > 0);682 assert(content.len > 0);
599683
600 t.getInstData(ret_block).* = EncodedData.encode(.block, .{684 t.getInstData(ret_block).* = EncodedData.encode(.block, .{
601 .src_node = decl.nodeOffset(node),685 .src_node = ctx.nodeOffset(node),
602 .extra_index = try t.extra.append(t.gpa, IndexList, content),686 .extra_index = try t.extra.append(t.gpa, IndexList, content),
603 });687 });
604688
...@@ -610,7 +694,13 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc...@@ -610,7 +694,13 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc
610 defer trace.end();694 defer trace.end();
611695
612 const param_decl = t.ast.fullParamDecl(param);696 const param_decl = t.ast.fullParamDecl(param);
613 const value = try t.transformExpressionToBlock(decl, param_decl.type orelse @panic("TODO: inferred param types"), false);697 const ty = try t.transformToBlock(
698 decl,
699 param_decl.type orelse
700 @panic("TODO: inferred param types"),
701 .result_without_coercion,
702 .{ .ref = .type_type },
703 );
614704
615 var id, const id_allocated = try t.decodeIdentifierExtra(param_decl.id);705 var id, const id_allocated = try t.decodeIdentifierExtra(param_decl.id);
616 if (id_allocated)706 if (id_allocated)
...@@ -631,9 +721,10 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc...@@ -631,9 +721,10 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc
631 sym.value_ptr.* = .{721 sym.value_ptr.* = .{
632 .node = param,722 .node = param,
633 .data = .param,723 .data = .param,
724 .type = .{ .present = ty },
634 };725 };
635726
636 return value;727 return ty;
637}728}
638729
639pub const Builtin = enum {730pub const Builtin = enum {
...@@ -664,14 +755,12 @@ pub const Builtin = enum {...@@ -664,14 +755,12 @@ pub const Builtin = enum {
664 return .void;755 return .void;
665 }756 }
666757
667 const source = try t.transformExpression(decl, args[0]) orelse return null;758 var result: ResultType = .{ .ref = .string_type };
759 const source = try t.transformExpression(decl, &result, args[0]) orelse return null;
668760
669 return try t.addInstRef(.{761 return try t.addRef(.js, .{
670 .tag = .js,762 .src_node = decl.nodeOffset(args[0]),
671 .data = EncodedData.encode(.js, .{763 .operand = source,
672 .src_node = decl.nodeOffset(args[0]),
673 .operand = source,
674 }),
675 });764 });
676 }765 }
677766
...@@ -684,17 +773,15 @@ pub const Builtin = enum {...@@ -684,17 +773,15 @@ pub const Builtin = enum {
684 return .void;773 return .void;
685 }774 }
686775
687 const base = try t.transformExpression(decl, args[0]) orelse return null;776 var opaque_type: ResultType = .{ .ref = .opaque_type };
688 const args_list = try t.transformExpressionList(decl, args[1..]) orelse return null;777 const base = try t.transformExpression(decl, &opaque_type, args[0]) orelse return null;
778 const args_list = try t.transformExpressionList(decl, .{ .ref = .opaque_type }, args[1..]) orelse return null;
689779
690 return try t.addInstRef(.{780 return try t.addRef(.js_new, .{
691 .tag = .js_new,781 .src_node = decl.nodeOffset(args[0]),
692 .data = EncodedData.encode(.js_new, .{782 .extra_index = try t.extra.append(t.gpa, Teg.JsNew, .{
693 .src_node = decl.nodeOffset(args[0]),783 .base = base,
694 .extra_index = try t.extra.append(t.gpa, Inst.JsNew, .{784 .args = args_list,
695 .base = base,
696 .args = args_list,
697 }),
698 }),785 }),
699 });786 });
700 }787 }
...@@ -708,15 +795,13 @@ pub const Builtin = enum {...@@ -708,15 +795,13 @@ pub const Builtin = enum {
708 return .void;795 return .void;
709 }796 }
710797
711 const source = try t.transformExpression(decl, args[0]) orelse798 var string_type: ResultType = .{ .ref = .string_type };
799 const source = try t.transformExpression(decl, &string_type, args[0]) orelse
712 return null;800 return null;
713801
714 return try t.addInstRef(.{802 return try t.addRef(.compile_error, .{
715 .tag = .compile_error,803 .src_node = decl.nodeOffset(node),
716 .data = EncodedData.encode(.compile_error, .{804 .operand = source,
717 .src_node = decl.nodeOffset(node),
718 .operand = source,
719 }),
720 });805 });
721 }806 }
722807
...@@ -729,22 +814,25 @@ pub const Builtin = enum {...@@ -729,22 +814,25 @@ pub const Builtin = enum {
729 return .void;814 return .void;
730 }815 }
731816
732 const source = try t.transformExpression(decl, args[0]) orelse817 var result: ResultType = .none;
818 const source = try t.transformExpression(decl, &result, args[0]) orelse
733 return null;819 return null;
734820
735 return try t.addInstRef(.{821 return try t.addRef(.type_of, .{
736 .tag = .type_of,822 .src_node = decl.nodeOffset(node),
737 .data = EncodedData.encode(.type_of, .{823 .operand = source,
738 .src_node = decl.nodeOffset(node),
739 .operand = source,
740 }),
741 });824 });
742 }825 }
743};826};
744827
745/// This is the primary transformation function: converting an AST expression into instructions.828/// This is the primary transformation function: converting an AST expression into instructions.
746/// `null` represents a `return` or `break`, aka this branch of code is dead.829/// `null` represents a `return` or `break`, aka this branch of code is dead.
747fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Allocator.Error!?Inst.Ref {830fn transformExpression(
831 t: *Transform,
832 ctx: BlockContext,
833 result: *ResultType,
834 node: NodeIndex,
835) Allocator.Error!?Inst.Ref {
748 const node_tag = t.ast.nodes.items(.tag)[node];836 const node_tag = t.ast.nodes.items(.tag)[node];
749837
750 const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)});838 const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)});
...@@ -775,12 +863,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -775,12 +863,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
775 lookup.used = true;863 lookup.used = true;
776864
777 return switch (lookup.data) {865 return switch (lookup.data) {
778 .declaration => try t.addInstRef(.{866 .declaration => try t.addRef(.load_decl, .{
779 .tag = .load_decl,867 .src_node = ctx.nodeOffset(node),
780 .data = EncodedData.encode(.load_decl, .{868 .string = try t.addNullTerminatedString(id),
781 .src_node = decl.nodeOffset(node),
782 .string = try t.addNullTerminatedString(id),
783 }),
784 }),869 }),
785 .mutable => |idx| idx.toRef(),870 .mutable => |idx| idx.toRef(),
786 .immutable => |ref| ref,871 .immutable => |ref| ref,
...@@ -800,41 +885,32 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -800,41 +885,32 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
800 try t.report.warn(.zero_prefixed_literal, .{ .token = t.ast.nodeToken(node) });885 try t.report.warn(.zero_prefixed_literal, .{ .token = t.ast.nodeToken(node) });
801 }886 }
802887
803 return try t.addInstRef(.{888 return try t.addRef(.number, .{
804 .tag = .number,889 .src_node = ctx.nodeOffset(node),
805 .data = EncodedData.encode(.number, .{890 .extra_index = try t.extra.append(t.gpa, Float, Float.encode(value)),
806 .src_node = decl.nodeOffset(node),
807 .extra_index = try t.extra.append(t.gpa, Float, Float.encode(value)),
808 }),
809 });891 });
810 },892 },
811893
812 .string => {894 .string => {
813 const str = t.ast.basicNodeSlice(node);895 const str = t.ast.basicNodeSlice(node);
814 const sliced = try t.addDecodedSlicedString(str[1 .. str.len - 1]);896 const sliced = try t.addDecodedSlicedString(str[1 .. str.len - 1]);
815 return try t.addInstRef(.{897 return try t.addRef(.string, .{
816 .tag = .string,898 .src_node = ctx.nodeOffset(node),
817 .data = EncodedData.encode(.string, .{899 .extra_index = try t.extra.append(t.gpa, SlicedString, sliced),
818 .src_node = decl.nodeOffset(node),
819 .extra_index = try t.extra.append(t.gpa, SlicedString, sliced),
820 }),
821 });900 });
822 },901 },
823902
824 .assign => {903 .assign => {
825 const ast_bin = t.ast.nodeData(node).bin;904 const ast_bin = t.ast.nodeData(node).bin;
826 const lhs = try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse905 const lhs, var result_type = try t.transformAssignmentTarget(ctx, ast_bin.lhs) orelse
827 return .void;906 return .void;
828 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse907 const rhs = try t.transformExpression(ctx, &result_type, ast_bin.rhs) orelse
829 return null;908 return null;
830 const assign_inst = try t.addInstRef(.{909 const assign_inst = try t.addRef(.store, .{
831 .tag = .store,910 .src_node = ctx.nodeOffset(node),
832 .data = EncodedData.encode(.store, .{911 .extra_index = try t.extra.append(t.gpa, Teg.Store, .{
833 .src_node = decl.nodeOffset(node),912 .memory = lhs,
834 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{913 .operand = rhs,
835 .memory = lhs,
836 .operand = rhs,
837 }),
838 }),914 }),
839 });915 });
840 return assign_inst;916 return assign_inst;
...@@ -877,41 +953,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -877,41 +953,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
877 // compound compute + assign operation.953 // compound compute + assign operation.
878 const is_assign = comptime tag.isAssignBin();954 const is_assign = comptime tag.isAssignBin();
879 const teg_tag = @field(955 const teg_tag = @field(
880 Teg.Inst.Tag,956 Teg.Tag,
881 if (is_assign)957 if (is_assign)
882 @tagName(tag)[0 .. @tagName(tag).len - "_assign".len]958 @tagName(tag)[0 .. @tagName(tag).len - "_assign".len]
883 else959 else
884 @tagName(tag),960 @tagName(tag),
885 );961 );
886 const ast_bin = t.ast.nodeData(node).bin;962 const ast_bin = t.ast.nodeData(node).bin;
887 const lhs = if (is_assign)963 var lhs_result_type: ResultType = .none;
888 try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse964 const lhs, var assign_result_type: ResultType = if (is_assign)
965 try t.transformAssignmentTarget(ctx, ast_bin.lhs) orelse
889 return .void966 return .void
890 else967 else
891 try t.transformExpression(decl, ast_bin.lhs) orelse968 .{
892 return null;969 try t.transformExpression(ctx, &lhs_result_type, ast_bin.lhs) orelse
893 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null;970 return null,
894971 .none,
895 const compute_inst = try t.addInstRef(.{972 };
896 .tag = teg_tag,973 const rhs = try t.transformExpression(ctx, &assign_result_type, ast_bin.rhs) orelse return null;
897 .data = EncodedData.encode(teg_tag, .{974
898 .src_node = decl.nodeOffset(node),975 const compute_inst = try t.addRef(teg_tag, .{
899 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{976 .src_node = ctx.nodeOffset(node),
900 .lhs = if (is_assign) lhs.toRef() else lhs,977 .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{
901 .rhs = rhs,978 .lhs = if (is_assign) lhs.toRef() else lhs,
902 }),979 .rhs = rhs,
903 }),980 }),
904 });981 });
905982
906 if (is_assign) {983 if (is_assign) {
907 const assign_inst = try t.addInstRef(.{984 const assign_inst = try t.addRef(.store, .{
908 .tag = .store,985 .src_node = ctx.nodeOffset(node),
909 .data = EncodedData.encode(.store, .{986 .extra_index = try t.extra.append(t.gpa, Teg.Store, .{
910 .src_node = decl.nodeOffset(node),987 .memory = lhs,
911 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{988 .operand = compute_inst,
912 .memory = lhs,
913 .operand = compute_inst,
914 }),
915 }),989 }),
916 });990 });
917 return assign_inst;991 return assign_inst;
...@@ -926,29 +1000,31 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -926,29 +1000,31 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
926 .array_type,1000 .array_type,
927 => |tag| {1001 => |tag| {
928 const ast_un = t.ast.nodeData(node).un;1002 const ast_un = t.ast.nodeData(node).un;
929 const teg_tag = @field(Teg.Inst.Tag, @tagName(tag));1003 const teg_tag = @field(Teg.Tag, @tagName(tag));
9301004
931 const operand = try t.transformExpression(decl, ast_un.expr) orelse1005 var operand_type: ResultType = switch (tag) {
1006 .negate => .none, // any numeric type allowed
1007 .bool_not => .{ .ref = .bool_type },
1008 .bit_not => .{ .ref = .int32_type },
1009 .array_type => .{ .ref = .type_type },
1010 else => @compileError(unreachable),
1011 };
1012
1013 const operand = try t.transformExpression(ctx, &operand_type, ast_un.expr) orelse
932 return null;1014 return null;
9331015
934 return try t.addInstRef(.{1016 return try t.addRef(teg_tag, .{
935 .tag = teg_tag,1017 .src_node = ctx.nodeOffset(node),
936 .data = EncodedData.encode(teg_tag, .{1018 .operand = operand,
937 .src_node = decl.nodeOffset(node),
938 .operand = operand,
939 }),
940 });1019 });
941 },1020 },
9421021
943 .@"comptime" => {1022 .@"comptime" => {
944 const ast_un = t.ast.nodeData(node).un;1023 const ast_un = t.ast.nodeData(node).un;
945 const operand = try t.transformExpressionToBlock(decl, ast_un.expr, false);1024 const operand = try t.transformToBlock(ctx, ast_un.expr, .tracked_result, result);
946 return try t.addInstRef(.{1025 return try t.addRef(.@"comptime", .{
947 .tag = .@"comptime",1026 .src_node = ctx.nodeOffset(node),
948 .data = EncodedData.encode(.@"comptime", .{1027 .operand = operand,
949 .src_node = decl.nodeOffset(node),
950 .operand = operand,
951 }),
952 });1028 });
953 },1029 },
9541030
...@@ -957,43 +1033,42 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -957,43 +1033,42 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
957 const label = t.ast.tokenSlice(t.ast.nodeToken(node));1033 const label = t.ast.tokenSlice(t.ast.nodeToken(node));
9581034
959 var target: BlockContext.BreakTarget = .{1035 var target: BlockContext.BreakTarget = .{
960 .parent = decl.break_target,1036 .parent = ctx.break_target,
961 .label = label,1037 .label = label,
962 .is_loop = false,1038 .is_loop = false,
1039 .result_type = result,
963 };1040 };
964 const inner_ctx = decl.withBreak(&target);1041 const inner_ctx = ctx.withBreak(&target);
9651042
966 const scratch_top = t.scratch.items.len;1043 const scratch = t.scratch.mark();
9671044
968 const inner_expr = try t.transformExpression(inner_ctx, expr);1045 const inner_expr = try t.transformExpression(inner_ctx, result, expr);
9691046
970 // if there are no new instructions, we dont have to create a block context1047 // if there are no new instructions, we dont have to create a block context
971 if (scratch_top == t.scratch.items.len)1048 if (scratch.len() == 0)
972 return inner_expr orelse unreachable; // null means we added break or return1049 return inner_expr orelse
1050 unreachable; // null means we added break or return
9731051
974 if (target.block_index) |block| {1052 if (target.block_index) |block| {
975 if (inner_expr) |result| {1053 if (inner_expr) |out| {
976 _ = try t.addInstRef(.{1054 _ = try t.addRef(.@"break", .{
977 .tag = .@"break",1055 .src_node = ctx.nodeOffset(node),
978 .data = EncodedData.encode(.@"break", .{1056 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
979 .src_node = decl.nodeOffset(node),1057 .block = block,
980 .extra_index = try t.extra.append(t.gpa, Inst.Break, .{1058 .operand = out,
981 .block = block,
982 .operand = result,
983 }),
984 }),1059 }),
985 });1060 });
986 }1061 }
9871062
988 const content = try t.scratchToIndexList(scratch_top);1063 const content = try t.scratchToIndexList(scratch);
989 t.scratch.shrinkRetainingCapacity(scratch_top);1064 scratch.deinit();
9901065
991 t.getInstData(block).* = EncodedData.encode(.block, .{1066 t.getInstData(block).* = EncodedData.encode(.block, .{
992 .src_node = decl.nodeOffset(node),1067 .src_node = ctx.nodeOffset(node),
993 .extra_index = try t.extra.append(t.gpa, IndexList, content),1068 .extra_index = try t.extra.append(t.gpa, IndexList, content),
994 });1069 });
9951070
996 try t.addScratch(Inst.Index, block);1071 try t.scratch.add(t.arena, Inst.Index, block);
9971072
998 return block.toRef();1073 return block.toRef();
999 } else {1074 } else {
...@@ -1012,9 +1087,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1012,9 +1087,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1012 for (statements, 1..) |statement, i| {1087 for (statements, 1..) |statement, i| {
1013 const is_last = i == statements.len;1088 const is_last = i == statements.len;
1014 last = try t.transformBlockStatement(1089 last = try t.transformBlockStatement(
1015 decl,1090 ctx,
1016 statement,1091 statement,
1017 tag == .block_implicit_return and is_last,1092 if (tag == .block_implicit_return and is_last)
1093 result
1094 else
1095 null,
1018 ) orelse {1096 ) orelse {
1019 return null;1097 return null;
1020 };1098 };
...@@ -1023,8 +1101,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1023,8 +1101,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1023 return if (tag == .block_implicit_return) last else .void;1101 return if (tag == .block_implicit_return) last else .void;
1024 },1102 },
10251103
1026 .builtin_call,1104 .builtin_call => {
1027 => {
1028 const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse {1105 const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse {
1029 try t.report.err(.invalid_builtin, .{ .node = node });1106 try t.report.err(.invalid_builtin, .{ .node = node });
1030 return .void;1107 return .void;
...@@ -1034,12 +1111,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1034,12 +1111,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1034 const args = base.slice(&t.ast);1111 const args = base.slice(&t.ast);
10351112
1036 return switch (builtin) {1113 return switch (builtin) {
1037 .js => Builtin.builtin_js(t, decl, node, args),1114 .js => Builtin.builtin_js(t, ctx, node, args),
1038 .js_new => Builtin.builtin_jsNew(t, decl, node, args),1115 .js_new => Builtin.builtin_jsNew(t, ctx, node, args),
1039 // .import => Builtin.import(t, decl, node, args),1116 // .import => Builtin.import(t, decl, node, args),
1040 // .fetch => Builtin.fetch(t, decl, node, args),1117 // .fetch => Builtin.fetch(t, decl, node, args),
1041 .compile_error => Builtin.builtin_compileError(t, decl, node, args),1118 .compile_error => Builtin.builtin_compileError(t, ctx, node, args),
1042 .type_of => Builtin.builtin_typeOf(t, decl, node, args),1119 .type_of => Builtin.builtin_typeOf(t, ctx, node, args),
1043 else => @panic("TODO"),1120 else => @panic("TODO"),
1044 };1121 };
1045 },1122 },
...@@ -1048,17 +1125,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1048,17 +1125,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1048 const call = t.ast.nodeData(node).call;1125 const call = t.ast.nodeData(node).call;
1049 const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast);1126 const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast);
10501127
1051 const base = try t.transformExpression(decl, call.base) orelse return null;1128 var base_result: ResultType = .none;
1052 const args_list = try t.transformExpressionList(decl, args) orelse return null;1129 const base = try t.transformExpression(ctx, &base_result, call.base) orelse return null;
1130
1131 const args_list: RefList = call_args: {
1132 const extra_top = try t.extra.alloc(t.gpa, args.len);
1133
1134 for (args, 0.., extra_top..) |arg, arg_id, index| {
1135 var arg_result: ResultType = .{ .argument = .{
1136 .call_base = base,
1137 .argument = @intCast(arg_id),
1138 } };
1139 t.extra.setU32(
1140 index,
1141 @intFromEnum(try t.transformExpression(ctx, &arg_result, arg) orelse
1142 return null),
1143 );
1144 }
10531145
1054 const call_inst = try t.addInstRef(.{1146 break :call_args .{
1055 .tag = .call,1147 .start = @intCast(extra_top),
1056 .data = EncodedData.encode(.call, .{1148 .len = @intCast(args.len),
1057 .src_node = decl.nodeOffset(node),1149 };
1058 .extra_index = try t.extra.append(t.gpa, Inst.Call, .{1150 };
1059 .base = base,1151
1060 .args = args_list,1152 const call_inst = try t.addRef(.call, .{
1061 }),1153 .src_node = ctx.nodeOffset(node),
1154 .extra_index = try t.extra.append(t.gpa, Teg.Call, .{
1155 .base = base,
1156 .args = args_list,
1062 }),1157 }),
1063 });1158 });
10641159
...@@ -1068,28 +1163,23 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1068,28 +1163,23 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1068 .field => {1163 .field => {
1069 const field = t.ast.nodeData(node).field;1164 const field = t.ast.nodeData(node).field;
10701165
1071 const base = try t.transformExpression(decl, field.base) orelse return null;1166 var base_result: ResultType = .none;
1167 const base = try t.transformExpression(ctx, &base_result, field.base) orelse return null;
10721168
1073 // TODO: handle identifier escapes1169 // TODO: handle identifier escapes
1074 const id = t.ast.tokenSlice(field.field_token);1170 const id = t.ast.tokenSlice(field.field_token);
1075 const prop_name_str = try t.addSlicedString(id);1171 const prop_name_str = try t.addSlicedString(id);
10761172
1077 const string_ref = try t.addInstRef(.{1173 const string_ref = try t.addRef(.string, .{
1078 .tag = .string,1174 .src_node = ctx.nodeOffset(node),
1079 .data = EncodedData.encode(.string, .{1175 .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str),
1080 .src_node = decl.nodeOffset(node),
1081 .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str),
1082 }),
1083 });1176 });
10841177
1085 return try t.addInstRef(.{1178 return try t.addRef(.field, .{
1086 .tag = .field,1179 .src_node = ctx.nodeOffset(node),
1087 .data = EncodedData.encode(.field, .{1180 .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{
1088 .src_node = decl.nodeOffset(node),1181 .lhs = base,
1089 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{1182 .rhs = string_ref,
1090 .lhs = base,
1091 .rhs = string_ref,
1092 }),
1093 }),1183 }),
1094 });1184 });
1095 },1185 },
...@@ -1098,45 +1188,36 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1098,45 +1188,36 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1098 const if_node = t.ast.nodeData(node).@"if";1188 const if_node = t.ast.nodeData(node).@"if";
1099 const if_data = t.ast.extraData(if_node.extra, Ast.If);1189 const if_data = t.ast.extraData(if_node.extra, Ast.If);
11001190
1101 const cond = try t.transformExpression(decl, if_node.cond) orelse1191 var cond_result: ResultType = .{ .ref = .bool_type };
1192 const cond = try t.transformExpression(ctx, &cond_result, if_node.cond) orelse
1102 return null;1193 return null;
11031194
1104 const yes = try t.transformExpressionToBlock(1195 var yes_result = result.*;
1105 decl,1196 const yes = try t.transformToBlock(ctx, if_data.yes, .tracked_result, &yes_result);
1106 if_data.yes,1197
1107 false,1198 // TODO: can this re-use the resolved ref from above
1108 );1199 var no_result: ResultType = if (if_data.no == .none) .used else result.*;
1109 const no = if (if_data.no.unwrap()) |no|1200 const no = if (if_data.no.unwrap()) |no|
1110 try t.transformExpressionToBlock(1201 try t.transformToBlock(
1111 decl,1202 ctx,
1112 no,1203 no,
1113 false,1204 .tracked_result,
1205 &no_result,
1114 )1206 )
1115 else1207 else
1116 Inst.Ref.void;1208 Inst.Ref.void;
11171209
1118 if (yes == .void and no == .void) {1210 // Mark the incoming result type as used if both branches use the result type
1119 // when both branches do nothing, we can omit the branch,1211 if (result.* != .none and yes_result == .none and no_result == .none) {
1120 // but we still need to type-check the condition operand1212 result.* = .used;
1121 _ = try t.addInstRef(.{
1122 .tag = .ensure_bool,
1123 .data = EncodedData.encode(.ensure_bool, .{
1124 .src_node = decl.nodeOffset(node),
1125 .operand = cond,
1126 }),
1127 });
1128 return .void;
1129 }1213 }
11301214
1131 return try t.addInstRef(.{1215 return try t.addRef(.@"if", .{
1132 .tag = .@"if",1216 .src_node = ctx.nodeOffset(node),
1133 .data = EncodedData.encode(.@"if", .{1217 .extra_index = try t.extra.append(t.gpa, Teg.If, .{
1134 .src_node = decl.nodeOffset(node),1218 .cond = cond,
1135 .extra_index = try t.extra.append(t.gpa, Inst.If, .{1219 .yes = yes,
1136 .cond = cond,1220 .no = no,
1137 .yes = yes,
1138 .no = no,
1139 }),
1140 }),1221 }),
1141 });1222 });
1142 },1223 },
...@@ -1144,36 +1225,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1144,36 +1225,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1144 .@"return" => {1225 .@"return" => {
1145 const ret_node = t.ast.nodeData(node).@"return";1226 const ret_node = t.ast.nodeData(node).@"return";
11461227
1147 const ret_block_ptr = decl.return_target orelse {1228 const target = ctx.return_target orelse {
1148 try t.report.err(.invalid_return, .{ .node = node });1229 try t.report.err(.invalid_return, .{ .node = node });
1149 return .void;1230 return .void;
1150 };1231 };
11511232
1152 const ret_block = if (ret_block_ptr.*) |block|1233 const ret_block = if (target.generated_block) |block|
1153 block1234 block
1154 else append_block: {1235 else append_block: {
1155 const block = try t.addRootInst(.{1236 const block = try t.addUntracked(.block, undefined);
1156 .tag = .block,1237 target.generated_block = block;
1157 .data = undefined,
1158 });
1159 ret_block_ptr.* = block;
1160 break :append_block block;1238 break :append_block block;
1161 };1239 };
11621240
1241 var return_result = target.result_type;
1242
1163 const operand: Inst.Ref = if (ret_node.expr.unwrap()) |expr|1243 const operand: Inst.Ref = if (ret_node.expr.unwrap()) |expr|
1164 try t.transformExpression(decl.withReturn(null), expr) orelse1244 try t.transformExpression(
1165 return null1245 ctx.withReturn(null),
1246 &return_result,
1247 expr,
1248 ) orelse return null
1166 else1249 else
1167 .void;1250 .void;
11681251
1169 _ = try t.addInstRef(.{1252 _ = try t.addIndex(.@"break", .{
1170 .tag = .@"break",1253 .src_node = ctx.nodeOffset(node),
1171 .data = EncodedData.encode(.@"break", .{1254 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
1172 .src_node = decl.nodeOffset(node),1255 .block = ret_block,
1173 .extra_index = try t.extra.append(t.gpa, Inst.Break, .{1256 .operand = operand,
1174 .block = ret_block,
1175 .operand = operand,
1176 }),
1177 }),1257 }),
1178 });1258 });
11791259
...@@ -1187,36 +1267,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1187,36 +1267,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1187 @panic("TODO: unlabelled breaks"),1267 @panic("TODO: unlabelled breaks"),
1188 );1268 );
11891269
1190 const block = try decl.findAndMarkBreak(t, label) orelse {1270 const block: *BlockContext.BreakTarget = ctx.lookupNamedBreak(label) orelse {
1191 try t.report.err(.unresolved_label, .{ .node = node });1271 try t.report.err(.unresolved_label, .{ .node = node });
1192 return null;1272 return null;
1193 };1273 };
11941274
1275 var break_result = block.result_type.*;
1195 const operand: Inst.Ref = if (brk_node.value.unwrap()) |expr|1276 const operand: Inst.Ref = if (brk_node.value.unwrap()) |expr|
1196 try t.transformExpression(decl.withReturn(null), expr) orelse1277 try t.transformExpression(ctx.withReturn(null), &break_result, expr) orelse
1197 return null1278 return null
1198 else1279 else
1199 .void;1280 .void;
12001281
1201 _ = try t.addInstRef(.{1282 _ = try t.addIndex(.@"break", .{
1202 .tag = .@"break",1283 .src_node = ctx.nodeOffset(node),
1203 .data = EncodedData.encode(.@"break", .{1284 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
1204 .src_node = decl.nodeOffset(node),1285 .block = try block.getInst(t),
1205 .extra_index = try t.extra.append(t.gpa, Inst.Break, .{1286 .operand = operand,
1206 .block = block,
1207 .operand = operand,
1208 }),
1209 }),1287 }),
1210 });1288 });
12111289
1212 return null; // control flow ends1290 return null; // control flow ends
1213 },1291 },
12141292
1215 .group => {
1216 const group_node = t.ast.nodeData(node).un;
1217 return try t.transformExpression(decl, group_node.expr);
1218 },
1219
1220 .struct_decl => {1293 .struct_decl => {
1221 const struct_node = t.ast.fullStructDecl(node);1294 const struct_node = t.ast.fullStructDecl(node);
1222 const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2);1295 const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2);
...@@ -1229,32 +1302,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1229,32 +1302,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1229 const field = t.ast.fullStructField(field_node);1302 const field = t.ast.fullStructField(field_node);
12301303
1231 const id = try t.decodeIdentifier(field.id);1304 const id = try t.decodeIdentifier(field.id);
1305 const key = try t.addNullTerminatedString(id);
1306
1307 var type_result: ResultType = .{ .ref = .type_type };
1308 const value = try t.transformExpression(ctx, &type_result, field.type) orelse
1309 return null;
12321310
1233 t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id)));1311 t.extra.setU32(i, @intFromEnum(key));
1234 t.extra.setU32(j, @intFromEnum(try t.transformExpressionToBlock(decl, field.type, false)));1312 t.extra.setU32(j, @intFromEnum(value));
1235 }1313 }
12361314
1237 return try t.addInstRef(.{1315 return try t.addRef(.struct_decl, .{
1238 .tag = .struct_decl,1316 .src_node = ctx.nodeOffset(node),
1239 .data = EncodedData.encode(.struct_decl, .{1317 .extra_index = try t.extra.append(t.gpa, Teg.StructDecl, .{
1240 .src_node = decl.nodeOffset(node),1318 .payload_index = payload_index,
1241 .extra_index = try t.extra.append(t.gpa, Inst.StructDecl, .{1319 .field_len = @intCast(struct_node.fields.len),
1242 .payload_index = payload_index,
1243 .field_len = @intCast(struct_node.fields.len),
1244 }),
1245 }),1320 }),
1246 });1321 });
1247 },1322 },
12481323
1249 .struct_init => {1324 .struct_init => {
1250 const struct_node = t.ast.fullStructInit(node);1325 const struct_node = t.ast.fullStructInit(node);
1326 // TODO: use t.scratch for this
1251 const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2);1327 const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2);
12521328
1253 const base_type = try t.transformExpression(1329 var result_type: ResultType = .{ .ref = .type_type };
1254 decl,1330 const base_type = if (struct_node.type) |ty|
1255 struct_node.type orelse @panic("TODO: TEG implicit struct"),1331 try t.transformExpression(ctx, &result_type, ty) orelse
1256 ) orelse1332 return null
1257 @panic("TODO: print better error");1333 else
1334 (try result.consume(t)) orelse {
1335 try t.report.err(.missing_result_type, .{ .node = node });
1336 return .void;
1337 };
12581338
1259 for (1339 for (
1260 struct_node.fields,1340 struct_node.fields,
...@@ -1264,21 +1344,30 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1264,21 +1344,30 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1264 const field = t.ast.fullStructField(field_node);1344 const field = t.ast.fullStructField(field_node);
12651345
1266 const id = try t.decodeIdentifier(field.id);1346 const id = try t.decodeIdentifier(field.id);
12671347 const field_name = try t.addNullTerminatedString(id);
1268 t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id)));1348
1269 t.extra.setU32(j, @intFromEnum((try t.transformExpression(decl, field.type)) orelse1349 var field_result_type: ResultType = .{ .struct_field = .{
1270 @panic("TODO: print better error")));1350 .type = base_type,
1351 .field = field_name,
1352 } };
1353 const field_value = (try t.transformExpression(
1354 ctx,
1355 &field_result_type,
1356 field.type,
1357 )) orelse return null;
1358
1359 t.extra.setU32(i, @intFromEnum(field_name));
1360 t.extra.setU32(j, @intFromEnum(
1361 field_value,
1362 ));
1271 }1363 }
12721364
1273 return try t.addInstRef(.{1365 return try t.addRef(.struct_init, .{
1274 .tag = .struct_init,1366 .src_node = ctx.nodeOffset(node),
1275 .data = EncodedData.encode(.struct_init, .{1367 .extra_index = try t.extra.append(t.gpa, Teg.StructInit, .{
1276 .src_node = decl.nodeOffset(node),1368 .base_type = base_type,
1277 .extra_index = try t.extra.append(t.gpa, Inst.StructInit, .{1369 .payload_index = payload_index,
1278 .base_type = base_type,1370 .field_len = @intCast(struct_node.fields.len),
1279 .payload_index = payload_index,
1280 .field_len = @intCast(struct_node.fields.len),
1281 }),
1282 }),1371 }),
1283 });1372 });
1284 },1373 },
...@@ -1286,17 +1375,19 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1286,17 +1375,19 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1286 .array_init => {1375 .array_init => {
1287 const array_init = t.ast.nodeData(node).list;1376 const array_init = t.ast.nodeData(node).list;
12881377
1289 const items = try t.transformExpressionList(decl, array_init.slice(&t.ast)) orelse1378 const array_type = try result.consume(t);
1290 return null;
12911379
1292 return try t.addInstRef(.{1380 const items = try t.transformExpressionList(
1293 .tag = .array_init,1381 ctx,
1294 .data = EncodedData.encode(.array_init, .{1382 if (array_type) |ty| .{ .array_value = ty } else .none,
1295 .src_node = decl.nodeOffset(node),1383 array_init.slice(&t.ast),
1296 .extra_index = try t.extra.append(t.gpa, Inst.ArrayInit, .{1384 ) orelse return null;
1297 .type_hint = .none,1385
1298 .list = items,1386 return try t.addRef(.array_init, .{
1299 }),1387 .src_node = ctx.nodeOffset(node),
1388 .extra_index = try t.extra.append(t.gpa, Teg.ArrayInit, .{
1389 .type_hint = .none,
1390 .list = items,
1300 }),1391 }),
1301 });1392 });
1302 },1393 },
...@@ -1305,12 +1396,16 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1305,12 +1396,16 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1305 }1396 }
1306}1397}
13071398
1308fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?RefList {1399fn transformExpressionList(t: *Transform, decl: BlockContext, result: ResultType, args: []const NodeIndex) !?RefList {
1309 const extra_top = try t.extra.alloc(t.gpa, args.len);1400 const extra_top = try t.extra.alloc(t.gpa, args.len);
13101401
1311 for (args, extra_top..) |arg, i| {1402 for (args, extra_top..) |arg, i| {
1312 t.extra.setU32(i, @intFromEnum(try t.transformExpression(decl, arg) orelse1403 var result_mut = result;
1313 return null));1404 t.extra.setU32(
1405 i,
1406 @intFromEnum(try t.transformExpression(decl, &result_mut, arg) orelse
1407 return null),
1408 );
1314 }1409 }
13151410
1316 return .{1411 return .{
...@@ -1319,7 +1414,25 @@ fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const Node...@@ -1319,7 +1414,25 @@ fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const Node
1319 };1414 };
1320}1415}
13211416
1322fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) Allocator.Error!?Inst.Index {1417/// The left-hand side of an assignment is known as the "assignment target".
1418/// This returns the assignment target's Ref, as well as a result type to visit
1419/// the right-hand side with. Note that only a few expressions are valid here:
1420///
1421/// - A plain identifier, which is resolved directly to a `memory` instruction
1422/// - A `field` node: TODO
1423/// - An `index` node: TODO
1424///
1425/// Note that these three are the root nodes, it is legal to have:
1426///
1427/// get().x = 1; // `field` of a `call` node
1428///
1429/// In the end, the assignment target must comprise either a ref to a `memory`
1430/// or a value with a reference type; and optionally a sub property.
1431fn transformAssignmentTarget(
1432 t: *Transform,
1433 decl: BlockContext,
1434 node: NodeIndex,
1435) Allocator.Error!?struct { Inst.Index, ResultType } {
1323 const node_tag = t.ast.nodes.items(.tag)[node];1436 const node_tag = t.ast.nodes.items(.tag)[node];
13241437
1325 const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)});1438 const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)});
...@@ -1345,7 +1458,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)...@@ -1345,7 +1458,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)
1345 return null;1458 return null;
1346 }1459 }
13471460
1348 const lookup = t.lookupId(id) orelse {1461 const lookup: *Scope.Symbol = t.lookupId(id) orelse {
1349 try t.report.err(.unknown_identifier, .{ .node = node });1462 try t.report.err(.unknown_identifier, .{ .node = node });
1350 return null;1463 return null;
1351 };1464 };
...@@ -1353,7 +1466,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)...@@ -1353,7 +1466,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)
1353 return switch (lookup.data) {1466 return switch (lookup.data) {
1354 .mutable => |inst| {1467 .mutable => |inst| {
1355 lookup.mutated = true;1468 lookup.mutated = true;
1356 return inst;1469 return .{ inst, lookup.type.resolve() };
1357 },1470 },
1358 else => {1471 else => {
1359 lookup.used = true;1472 lookup.used = true;
...@@ -1371,6 +1484,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)...@@ -1371,6 +1484,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)
1371 },1484 },
1372 },1485 },
1373 });1486 });
1487
1374 return null;1488 return null;
1375 },1489 },
1376 };1490 };
...@@ -1379,10 +1493,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)...@@ -1379,10 +1493,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)
1379 // TODO: allow field expressions, but these need to emit a different store node1493 // TODO: allow field expressions, but these need to emit a different store node
13801494
1381 else => {1495 else => {
1382 _ = try t.transformExpression(decl, node);1496 var result_type: ResultType = .none;
1497 _ = try t.transformExpression(decl, &result_type, node);
1498
1383 try t.report.err(.invalid_assignment_target, .{1499 try t.report.err(.invalid_assignment_target, .{
1384 .node = node,1500 .node = node,
1385 });1501 });
1502
1386 return null;1503 return null;
1387 },1504 },
1388 }1505 }
...@@ -1390,10 +1507,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)...@@ -1390,10 +1507,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex)
13901507
1391fn transformBlockStatement(1508fn transformBlockStatement(
1392 t: *Transform,1509 t: *Transform,
1393 decl: BlockContext,1510 ctx: BlockContext,
1394 node: NodeIndex,1511 node: NodeIndex,
1395 is_implicit_return_expression: bool,1512 maybe_result: ?*ResultType,
1396) !?Inst.Ref {1513) !?Inst.Ref {
1514 var void_result_storage: ResultType = .{ .ref = .void };
1515 const result = maybe_result orelse &void_result_storage;
1516
1397 const node_tag = t.ast.nodes.items(.tag)[node];1517 const node_tag = t.ast.nodes.items(.tag)[node];
13981518
1399 const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)});1519 const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)});
...@@ -1437,16 +1557,16 @@ fn transformBlockStatement(...@@ -1437,16 +1557,16 @@ fn transformBlockStatement(
14371557
1438 return .void;1558 return .void;
1439 },1559 },
1440 else => try t.transformExpression(decl, var_decl.init) orelse1560 else => ref: {
1441 return null,1561 var empty_type: ResultType = .none;
1562 break :ref try t.transformExpression(ctx, &empty_type, var_decl.init) orelse
1563 return null;
1564 },
1442 };1565 };
14431566
1444 return try t.addInstRef(.{1567 return try t.addRef(.discard, .{
1445 .tag = .discard,1568 .src_node = ctx.nodeOffset(node),
1446 .data = EncodedData.encode(.discard, .{1569 .operand = ref,
1447 .src_node = decl.nodeOffset(node),
1448 .operand = ref,
1449 }),
1450 });1570 });
1451 }1571 }
14521572
...@@ -1464,36 +1584,37 @@ fn transformBlockStatement(...@@ -1464,36 +1584,37 @@ fn transformBlockStatement(
1464 return .void;1584 return .void;
1465 }1585 }
14661586
1587 var type_result: ResultType = .{ .ref = .type_type };
1467 const type_ref = if (var_decl.type) |data_type|1588 const type_ref = if (var_decl.type) |data_type|
1468 try t.transformExpression(decl, data_type)1589 try t.transformExpression(ctx, &type_result, data_type)
1469 else1590 else
1470 null;1591 null;
1471 const data_ref = try t.transformExpression(decl, var_decl.init) orelse
1472 return null;
1473 const data_coerced_ref = if (type_ref) |ref|
1474 try t.addInstRef(.{
1475 .tag = .as,
1476 .data = EncodedData.encode(.as, .{
1477 .src_node = decl.nodeOffset(node),
1478 .extra_index = try t.extra.append(t.gpa, Inst.As, .{
1479 .type = ref,
1480 .operand = data_ref,
1481 }),
1482 }),
1483 })
1484 else
1485 data_ref;
14861592
1487 const mem_ref = if (var_decl.is_const)1593 var value_result: ResultType = if (type_ref) |ty|
1488 data_coerced_ref1594 .{ .ref = ty }
1489 else1595 else
1490 try t.addInstRef(.{1596 .none;
1491 .tag = .memory,1597
1492 .data = EncodedData.encode(.memory, .{1598 const data_ref = try t.transformExpression(ctx, &value_result, var_decl.init) orelse
1493 .src_node = decl.nodeOffset(node),1599 return null;
1494 .operand = data_coerced_ref,1600
1495 }),1601 // Add a .coerce instruction if there was a type declared.
1496 });1602 const data_coerced_ref = coerced: {
1603 if (type_ref) |ref| {
1604 // We can avoid the coercion instruction if the result type was used.
1605 if (value_result != .used) {
1606 break :coerced try t.addRef(.coercion, .{
1607 .src_node = ctx.nodeOffset(node),
1608 .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{
1609 .type = ref,
1610 .operand = data_ref,
1611 }),
1612 });
1613 }
1614 }
1615
1616 break :coerced data_ref;
1617 };
14971618
1498 const gop = try t.scope().symbols.getOrPut(t.gpa, id);1619 const gop = try t.scope().symbols.getOrPut(t.gpa, id);
14991620
...@@ -1505,16 +1626,26 @@ fn transformBlockStatement(...@@ -1505,16 +1626,26 @@ fn transformBlockStatement(
15051626
1506 gop.value_ptr.* = .{1627 gop.value_ptr.* = .{
1507 .node = node,1628 .node = node,
1508 .data = switch (var_decl.is_const) {1629 .data = if (var_decl.is_const)
1509 true => .{ .immutable = mem_ref },1630 .{ .immutable = data_coerced_ref }
1510 false => .{ .mutable = mem_ref.index().? },1631 else
1511 },1632 .{
1633 .mutable = try t.addIndex(.memory, .{
1634 .src_node = ctx.nodeOffset(node),
1635 .operand = data_coerced_ref,
1636 }),
1637 },
1638 .type = if (type_ref) |ty|
1639 .{ .present = ty }
1640 else
1641 .{ .missing = data_ref },
1512 };1642 };
15131643
1514 return .void;1644 return .void;
1515 },1645 },
15161646
1517 .fn_decl,1647 .fn_decl,
1648 .struct_decl,
1518 => |tag| {1649 => |tag| {
1519 std.debug.panic("TODO: block statement {s}", .{@tagName(tag)});1650 std.debug.panic("TODO: block statement {s}", .{@tagName(tag)});
1520 },1651 },
...@@ -1530,18 +1661,17 @@ fn transformBlockStatement(...@@ -1530,18 +1661,17 @@ fn transformBlockStatement(
1530 // discard, instead of appending an .ensure_void check,1661 // discard, instead of appending an .ensure_void check,
1531 // we will add a .discard instruction instead.1662 // we will add a .discard instruction instead.
1532 // this will emit a warning if given void.1663 // this will emit a warning if given void.
1533 const result = try t.transformExpression(decl, bin.rhs) orelse1664 var result_type: ResultType = .none;
1665 const value = try t.transformExpression(ctx, &result_type, bin.rhs) orelse
1534 return null;1666 return null;
1535 return try t.addInstRef(.{1667
1536 .tag = .discard,1668 return try t.addRef(.discard, .{
1537 .data = EncodedData.encode(.discard, .{1669 .src_node = ctx.nodeOffset(node),
1538 .src_node = decl.nodeOffset(node),1670 .operand = value,
1539 .operand = result,
1540 }),
1541 });1671 });
1542 } else {1672 } else {
1543 // process this assignment as usual, do not append .ensure_void1673 // process this assignment as usual, do not append .ensure_void
1544 return try t.transformExpression(decl, node);1674 return try t.transformExpression(ctx, result, node);
1545 }1675 }
1546 },1676 },
15471677
...@@ -1560,50 +1690,51 @@ fn transformBlockStatement(...@@ -1560,50 +1690,51 @@ fn transformBlockStatement(
1560 .shr_assign,1690 .shr_assign,
1561 => {1691 => {
1562 // do not append a .ensure_void check1692 // do not append a .ensure_void check
1563 return try t.transformExpression(decl, node);1693 return try t.transformExpression(ctx, result, node);
1564 },1694 },
15651695
1566 else => {1696 else => {
1567 const result = try t.transformExpression(decl, node) orelse1697 const value = try t.transformExpression(ctx, result, node) orelse
1568 return null;1698 return null;
15691699
1570 if (!is_implicit_return_expression and result != .void) {1700 if (maybe_result == null and value != .void) {
1571 if (t.ast.nodeTag(node) == .id) {1701 if (t.ast.nodeTag(node) == .id) {
1572 try t.report.warn(.dangling_identifier, .{1702 try t.report.warn(.dangling_identifier, .{
1573 .node = node,1703 .node = node,
1574 });1704 });
1575 return result;
1576 }1705 }
15771706
1578 return try t.addInstRef(.{1707 return try t.addRef(.ensure_void, .{
1579 .tag = .ensure_void,1708 .src_node = ctx.nodeOffset(node),
1580 .data = EncodedData.encode(.ensure_void, .{1709 .operand = value,
1581 .src_node = decl.nodeOffset(node),
1582 .operand = result,
1583 }),
1584 });1710 });
1585 }1711 }
15861712
1587 return result;1713 return value;
1588 },1714 },
1589 }1715 }
1590}1716}
15911717
1592fn addRootInst(t: *Transform, inst: Inst) !Inst.Index {1718fn addUntracked(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index {
1593 try t.insts.append(t.gpa, inst);1719 try t.insts.append(t.gpa, .{
1720 .tag = tag,
1721 .data = EncodedData.encode(tag, data),
1722 });
1594 return @enumFromInt(t.insts.len - 1);1723 return @enumFromInt(t.insts.len - 1);
1595}1724}
15961725
1597fn addInstRef(t: *Transform, inst: Inst) !Inst.Ref {1726fn addIndex(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index {
1598 try t.scratch.ensureUnusedCapacity(t.gpa, 1);1727 const index = try t.addUntracked(tag, data);
1599 try t.insts.append(t.gpa, inst);1728 try t.scratch.add(t.arena, Inst.Index, index);
1600 const index: Inst.Index = @enumFromInt(t.insts.len - 1);1729 return index;
1601 t.scratch.appendAssumeCapacity(@intFromEnum(index));1730}
1602 return index.toRef();1731
1732fn addRef(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Ref {
1733 return (try t.addIndex(tag, data)).toRef();
1603}1734}
16041735
1605fn pushScope(t: *Transform) !void {1736fn pushScope(t: *Transform) !void {
1606 try t.scopes.append(t.gpa, .{});1737 try t.scopes.append(t.arena, .{});
1607}1738}
16081739
1609fn popScope(t: *Transform) void {1740fn popScope(t: *Transform) void {
...@@ -1620,22 +1751,18 @@ fn getInstData(t: *Transform, index: Inst.Index) *EncodedData {...@@ -1620,22 +1751,18 @@ fn getInstData(t: *Transform, index: Inst.Index) *EncodedData {
1620 return &t.insts.items(.data)[@intFromEnum(index)];1751 return &t.insts.items(.data)[@intFromEnum(index)];
1621}1752}
16221753
1623fn addScratch(t: *Transform, comptime T: type, value: T) !void {1754fn scratchToRefList(t: *Transform, scratch_top: core.U32Stack.Mark) !RefList {
1624 try t.scratch.append(t.gpa, @intFromEnum(value));1755 const items = scratch_top.slice(u32);
1625}
1626
1627fn scratchToRefList(t: *Transform, scratch_top: usize) !RefList {
1628 const items = t.scratch.items[scratch_top..];
1629 const extra_top = t.extra.list.items.len;1756 const extra_top = t.extra.list.items.len;
1630 try t.extra.list.appendSlice(t.gpa, t.scratch.items[scratch_top..]);1757 try t.extra.list.appendSlice(t.gpa, items);
1631 return .{1758 return .{
1632 .start = @intCast(extra_top),1759 .start = @intCast(extra_top),
1633 .len = @intCast(items.len),1760 .len = @intCast(items.len),
1634 };1761 };
1635}1762}
16361763
1637fn scratchToIndexList(t: *Transform, scratch_top: usize) !IndexList {1764fn scratchToIndexList(t: *Transform, scratch: core.U32Stack.Mark) !IndexList {
1638 const items = t.scratch.items[scratch_top..];1765 const items = scratch.slice(u32);
1639 const extra_top = t.extra.list.items.len;1766 const extra_top = t.extra.list.items.len;
1640 try t.extra.list.appendSlice(t.gpa, items);1767 try t.extra.list.appendSlice(t.gpa, items);
1641 return .{1768 return .{
...@@ -1715,8 +1842,8 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString {...@@ -1715,8 +1842,8 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString {
1715 // string. this is done so that duplicate strings will get de-duplicated1842 // string. this is done so that duplicate strings will get de-duplicated
1716 // in string_bytes. It is more likely that there will be remaining bytes1843 // in string_bytes. It is more likely that there will be remaining bytes
1717 // in `.scratch` vs `.id_decode_buf`1844 // in `.scratch` vs `.id_decode_buf`
1718 try t.scratch.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1);1845 try t.scratch.list.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1);
1719 const buf = mem.sliceAsBytes(t.scratch.unusedCapacitySlice());1846 const buf = mem.sliceAsBytes(t.scratch.list.unusedCapacitySlice());
17201847
1721 break :brk Tokenizer.decodeStringBuf(escaped, buf);1848 break :brk Tokenizer.decodeStringBuf(escaped, buf);
1722 } else escaped;1849 } else escaped;
...@@ -1767,7 +1894,8 @@ const mem = std.mem;...@@ -1767,7 +1894,8 @@ const mem = std.mem;
1767const Allocator = mem.Allocator;1894const Allocator = mem.Allocator;
1768const assert = std.debug.assert;1895const assert = std.debug.assert;
17691896
1770const Teg = @import("Teg.zig");1897const core = @import("mod.zig");
1898const Teg = core.Teg;
1771const Inst = Teg.Inst;1899const Inst = Teg.Inst;
1772const NullTerminatedString = Teg.NullTerminatedString;1900const NullTerminatedString = Teg.NullTerminatedString;
1773const SlicedString = Teg.SlicedString;1901const SlicedString = Teg.SlicedString;
...@@ -1776,15 +1904,15 @@ const IndexList = Teg.IndexList;...@@ -1776,15 +1904,15 @@ const IndexList = Teg.IndexList;
1776const EncodedData = Teg.EncodedData;1904const EncodedData = Teg.EncodedData;
1777const ExtraIndex = Teg.ExtraIndex;1905const ExtraIndex = Teg.ExtraIndex;
17781906
1779const dod = @import("array.zig");1907const extra = core.array;
1780const ExtraStore = dod.ExtraStore(.teg, .@"var");1908const ExtraStore = extra.ExtraStore(.teg, .@"var");
1781const Float = dod.Float;1909const Float = extra.Float;
17821910
1783const Tokenizer = @import("Tokenizer.zig");1911const Tokenizer = core.Tokenizer;
1784const Token = Tokenizer.Token;1912const Token = Tokenizer.Token;
1785const ByteOffset = Tokenizer.ByteOffset;1913const ByteOffset = Tokenizer.ByteOffset;
17861914
1787const Ast = @import("Ast.zig");1915const Ast = core.Ast;
1788const Unused = Ast.Unused;1916const Unused = Ast.Unused;
1789const Node = Ast.Node;1917const Node = Ast.Node;
1790const Data = Node.Data;1918const Data = Node.Data;
...@@ -1798,9 +1926,9 @@ const optionalTokenIndex = OptionalTokenIndex.init;...@@ -1798,9 +1926,9 @@ const optionalTokenIndex = OptionalTokenIndex.init;
1798const optionalNodeIndex = OptionalNodeIndex.init;1926const optionalNodeIndex = OptionalNodeIndex.init;
1799const optionalExtraIndex = OptionalExtraIndex.init;1927const optionalExtraIndex = OptionalExtraIndex.init;
18001928
1801const InternPool = @import("InternPool.zig");1929const InternPool = core.InternPool;
18021930
1803const Report = @import("Report.zig");1931const Report = core.Report;
1804const Comp = @import("Comp.zig");1932const Comp = core.Comp;
18051933
1806const tracer = @import("tracer");1934const tracer = @import("tracer");
src/core/main.zig-5
...@@ -22,11 +22,6 @@ pub fn main() !void {...@@ -22,11 +22,6 @@ pub fn main() !void {
2222
23 try unicode_props.init();23 try unicode_props.init();
2424
25 if (builtin.mode == .Debug and std.io.getStdErr().isTty()) {
26 // zig build system doesnt print a newline :/
27 std.debug.print("\r\x1b[K", .{});
28 }
29
30 const use_gpa = builtin.mode == .Debug;25 const use_gpa = builtin.mode == .Debug;
31 var gpa = if (use_gpa)26 var gpa = if (use_gpa)
32 std.heap.GeneralPurposeAllocator(.{27 std.heap.GeneralPurposeAllocator(.{
src/core/mod.zig+3
...@@ -32,6 +32,9 @@ pub const Sema = @import("Sema.zig");...@@ -32,6 +32,9 @@ pub const Sema = @import("Sema.zig");
32pub const Stack = @import("stack.zig").Stack;32pub const Stack = @import("stack.zig").Stack;
33pub const U32Stack = Stack(u32);33pub const U32Stack = Stack(u32);
3434
35pub const StoredTrace = @import("StoredTrace.zig");
36pub const DebugStoredTrace = StoredTrace.DebugStoredTrace;
37
35/// Untyped Intermediate Representation. Flat instruction list38/// Untyped Intermediate Representation. Flat instruction list
36pub const Teg = @import("Teg.zig");39pub const Teg = @import("Teg.zig");
3740