authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-29 00:59:55-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-29 00:59:55-07:00
logeefe35dc33f3f2354626c64f066bb20d3f595496
tree9e29d50194c8340409e69d32cb16847e9cd77fe3
parent3ff6202073407ee0560d083011736c187448b190
signature Commit is signed but in an unrecognized format.

some more type-checking errors


8 files changed, 319 insertions(+), 60 deletions(-)

CHANGELOG.md+4
...@@ -1,9 +1,13 @@...@@ -1,9 +1,13 @@
1## 0.1.31## 0.1.3
22
3- feat(sema): coersion: numeric_literal -> float3- feat(sema): coersion: numeric_literal -> float
4- feat(sema): prevent passing non-runtime compatible types like `type` to opaque functions
4- feat(transform): lhs of assignments now validated5- feat(transform): lhs of assignments now validated
5- feat(transform): mark useless discard6- feat(transform): mark useless discard
7- feat: implement the `comptime` keyword (many comptime ops are not implemented)
6- fix(sema): properly coerce assignments8- fix(sema): properly coerce assignments
9- fix(transform): empty block generated in top level code tripped sema assertion
10- fix(link): emit parenthesis to preserve operator precedence
711
8## 0.1.212## 0.1.2
913
src/core/InternPool.zig+20
...@@ -336,6 +336,26 @@ pub const Type = enum(u32) {...@@ -336,6 +336,26 @@ pub const Type = enum(u32) {
336 return ip.indexToKey(ty.value()).type_function;336 return ip.indexToKey(ty.value()).type_function;
337 }337 }
338338
339 pub fn hasRuntimeRepresentation(ty: Type, ip: *const InternPool) bool {
340 return switch (ty) {
341 .bool,
342 .int32,
343 .float,
344 .string,
345 .void,
346 .@"opaque",
347 => true,
348 .numeric_literal,
349 .type,
350 .noreturn,
351 => false,
352 else => switch (ty.tag(ip)) {
353 .simple_type => unreachable, // all simple types are already printed
354 .type_function => true,
355 },
356 };
357 }
358
339 pub fn displayName(ty: Type, ip: *const InternPool) std.fmt.Formatter(formatDisplayName) {359 pub fn displayName(ty: Type, ip: *const InternPool) std.fmt.Formatter(formatDisplayName) {
340 return .{ .data = .{ .type = ty, .ip = ip } };360 return .{ .data = .{ .type = ty, .ip = ip } };
341 }361 }
src/core/JsLink.zig+49-4
...@@ -69,7 +69,7 @@ pub fn linkJsCode(...@@ -69,7 +69,7 @@ pub fn linkJsCode(
69 try link.decls.putNoClobber(comp.gpa, decl_index, @enumFromInt(gop.index));69 try link.decls.putNoClobber(comp.gpa, decl_index, @enumFromInt(gop.index));
70 }70 }
7171
72 try writer.print("// Language Compiler v0.1.2. Alpha quality software.\n", .{});72 try writer.print("// Language Compiler v0.1.3. Alpha quality software.\n", .{});
7373
74 // 2. Print every function decl74 // 2. Print every function decl
75 for (decls[1..]) |decl_index| {75 for (decls[1..]) |decl_index| {
...@@ -197,7 +197,22 @@ fn printBlock(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer...@@ -197,7 +197,22 @@ fn printBlock(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer
197 }197 }
198}198}
199199
200fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anytype) @TypeOf(writer).Error!void {200fn printExpr(
201 link: *JsLink,
202 tree: *const JsTree,
203 node: Node.Index,
204 writer: anytype,
205) @TypeOf(writer).Error!void {
206 return link.printExprLevel(tree, node, .comma, writer);
207}
208
209fn printExprLevel(
210 link: *JsLink,
211 tree: *const JsTree,
212 node: Node.Index,
213 level: Level,
214 writer: anytype,
215) @TypeOf(writer).Error!void {
201 var t = tracer.trace(@src());216 var t = tracer.trace(@src());
202 defer t.end();217 defer t.end();
203218
...@@ -314,11 +329,16 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty...@@ -314,11 +329,16 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty
314 => |tag| {329 => |tag| {
315 const bin: JsTree.Bin = tree.nodeData(tag, node);330 const bin: JsTree.Bin = tree.nodeData(tag, node);
316 const op: Operator = @field(Operator, @tagName(tag));331 const op: Operator = @field(Operator, @tagName(tag));
317 try link.printExpr(tree, bin.lhs, writer);332 const inner_level = comptime op.level();
333 if (inner_level.needsParens(level))
334 try writer.writeAll("(");
335 try link.printExprLevel(tree, bin.lhs, inner_level, writer);
318 try writer.writeAll(" ");336 try writer.writeAll(" ");
319 try writer.writeAll(comptime op.symbol());337 try writer.writeAll(comptime op.symbol());
320 try writer.writeAll(" ");338 try writer.writeAll(" ");
321 try link.printExpr(tree, bin.rhs, writer);339 try link.printExprLevel(tree, bin.rhs, inner_level, writer);
340 if (inner_level.needsParens(level))
341 try writer.writeAll(")");
322 },342 },
323343
324 inline .negate,344 inline .negate,
...@@ -411,6 +431,10 @@ const Level = enum {...@@ -411,6 +431,10 @@ const Level = enum {
411 new,431 new,
412 access_and_call,432 access_and_call,
413 member,433 member,
434
435 pub fn needsParens(inner_level: Level, outer_level: Level) bool {
436 return @intFromEnum(outer_level) > @intFromEnum(inner_level);
437 }
414};438};
415439
416const Operator = enum {440const Operator = enum {
...@@ -431,6 +455,27 @@ const Operator = enum {...@@ -431,6 +455,27 @@ const Operator = enum {
431 pre_inc,455 pre_inc,
432 pre_dec,456 pre_dec,
433457
458 pub fn level(op: Operator) Level {
459 return switch (op) {
460 .assign,
461 .assign_add,
462 .assign_sub,
463 .assign_mul,
464 .assign_div,
465 => .assignment,
466 .add, .sub => .additive,
467 .mul, .div => .multiplicative,
468 .eq => .equality,
469 .lt, .gt => .relational,
470
471 .pre_inc,
472 .pre_dec,
473 .bool_not,
474 .negate,
475 => .prefix,
476 };
477 }
478
434 pub fn symbol(op: Operator) []const u8 {479 pub fn symbol(op: Operator) []const u8 {
435 return switch (op) {480 return switch (op) {
436 .assign => "=",481 .assign => "=",
src/core/Report.zig+62-4
...@@ -44,6 +44,16 @@ pub const Err = union(enum) {...@@ -44,6 +44,16 @@ pub const Err = union(enum) {
4444
45 ast: @import("Parse.zig").AstError,45 ast: @import("Parse.zig").AstError,
4646
47 user_compile_error: struct {
48 src: LazyNode,
49 msg: String,
50
51 fn build(data: @This(), ctx: *MsgContext) !void {
52 try ctx.msgNode(try data.src.get(ctx), "{s}", .{
53 data.msg.slice(ctx.report),
54 });
55 }
56 },
47 unknown_identifier: struct {57 unknown_identifier: struct {
48 node: NodeIndex,58 node: NodeIndex,
4959
...@@ -64,6 +74,17 @@ pub const Err = union(enum) {...@@ -64,6 +74,17 @@ pub const Err = union(enum) {
64 );74 );
65 }75 }
66 },76 },
77 builtin_mismatch_args: struct {
78 node: NodeIndex,
79 args: u32,
80
81 fn build(data: @This(), ctx: *MsgContext) !void {
82 try ctx.msgNode(data.node, "built-in function expected {d} arguments, recieved {d}", .{
83 data.args,
84 ctx.ast.extraData(ctx.ast.nodeData(data.node).call.extra, Ast.Call).args.len,
85 });
86 }
87 },
67 duplicate_function: struct {88 duplicate_function: struct {
68 node: NodeIndex,89 node: NodeIndex,
69 prev: NodeIndex,90 prev: NodeIndex,
...@@ -169,6 +190,28 @@ pub const Err = union(enum) {...@@ -169,6 +190,28 @@ pub const Err = union(enum) {
169 });190 });
170 }191 }
171 },192 },
193 runtime_incompatible_type: struct {
194 src: LazyNode,
195 type: Type,
196
197 fn build(data: @This(), ctx: *MsgContext) !void {
198 const node = try data.src.get(ctx);
199 try ctx.msgNode(node, "type \"{}\" cannot be represented as a runtime value", .{
200 data.type.displayName(&ctx.comp.intern_pool),
201 });
202 }
203 },
204 param_comptime_only: struct {
205 src: LazyNode,
206 type: Type,
207
208 fn build(data: @This(), ctx: *MsgContext) !void {
209 const node = try data.src.get(ctx);
210 try ctx.msgNode(node, "parameter of comptime type \"{s}\" must be marked \"comptime\"", .{
211 data.type.displayName(&ctx.comp.intern_pool),
212 });
213 }
214 },
172 assign_builtin: struct {215 assign_builtin: struct {
173 node: NodeIndex,216 node: NodeIndex,
174217
...@@ -279,10 +322,10 @@ pub const Warn = union(enum) {...@@ -279,10 +322,10 @@ pub const Warn = union(enum) {
279 }322 }
280 },323 },
281 numeric_int_var_ambiguity: struct {324 numeric_int_var_ambiguity: struct {
282 node: LazyNode,325 src: LazyNode,
283326
284 fn build(data: @This(), ctx: *MsgContext) !void {327 fn build(data: @This(), ctx: *MsgContext) !void {
285 const node = try data.node.get(ctx);328 const node = try data.src.get(ctx);
286 try ctx.msgNode(node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{});329 try ctx.msgNode(node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{});
287 try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{});330 try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{});
288 }331 }
...@@ -356,7 +399,7 @@ pub const String = struct {...@@ -356,7 +399,7 @@ pub const String = struct {
356 start: u32,399 start: u32,
357 len: u32,400 len: u32,
358401
359 pub fn slice(str: String, report: Report) []const u8 {402 pub fn slice(str: String, report: *const Report) []const u8 {
360 return report.string_bytes[str.start..][0..str.len];403 return report.string_bytes[str.start..][0..str.len];
361 }404 }
362};405};
...@@ -392,7 +435,7 @@ pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype, tty: std.io.tt...@@ -392,7 +435,7 @@ pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype, tty: std.io.tt
392}435}
393436
394pub fn formatItem(437pub fn formatItem(
395 report: Report,438 report: *const Report,
396 item: Item,439 item: Item,
397 item_type: Item.Type,440 item_type: Item.Type,
398 comp: *Comp,441 comp: *Comp,
...@@ -414,6 +457,7 @@ pub fn formatItem(...@@ -414,6 +457,7 @@ pub fn formatItem(
414 .comp = comp,457 .comp = comp,
415 .file = file,458 .file = file,
416 .ast = &ast_data,459 .ast = &ast_data,
460 .report = report,
417 .utf8_source = (try file.getSource(comp.gpa)).code,461 .utf8_source = (try file.getSource(comp.gpa)).code,
418 .components = ArrayListUnmanaged(Component).initCapacity(alloc, 16) catch462 .components = ArrayListUnmanaged(Component).initCapacity(alloc, 16) catch
419 unreachable, // stack-fallback allocator has enough space.463 unreachable, // stack-fallback allocator has enough space.
...@@ -450,7 +494,11 @@ pub fn formatItem(...@@ -450,7 +494,11 @@ pub fn formatItem(
450 try tty.setColor(writer, .dim);494 try tty.setColor(writer, .dim);
451 try writer.writeAll(": ");495 try writer.writeAll(": ");
452 try tty.setColor(writer, .reset);496 try tty.setColor(writer, .reset);
497 if (i == 1)
498 try tty.setColor(writer, .bold);
453 try writer.writeAll(component.message_utf8);499 try writer.writeAll(component.message_utf8);
500 if (i == 1)
501 try tty.setColor(writer, .reset);
454 try writer.writeAll("\n");502 try writer.writeAll("\n");
455503
456 const utf8_source = ctx.utf8_source;504 const utf8_source = ctx.utf8_source;
...@@ -685,6 +733,7 @@ pub const MsgContext = struct {...@@ -685,6 +733,7 @@ pub const MsgContext = struct {
685 file: *File,733 file: *File,
686 comp: *Comp,734 comp: *Comp,
687 ast: *const Ast,735 ast: *const Ast,
736 report: *const Report,
688 utf8_source: [:0]const u8,737 utf8_source: [:0]const u8,
689738
690 /// This arena is is a stack fallback with 16kb, then739 /// This arena is is a stack fallback with 16kb, then
...@@ -799,6 +848,14 @@ pub const LazyNode = struct {...@@ -799,6 +848,14 @@ pub const LazyNode = struct {
799 else => unreachable,848 else => unreachable,
800 }849 }
801 },850 },
851 .declaration => {
852 assert(node_tag == .fn_decl);
853 const fn_decl = ctx.ast.extraData(data.fn_decl.extra, Ast.FnDecl);
854 return switch (lazy.operand) {
855 .return_type => fn_decl.return_type.unwrap().?,
856 else => fn_decl.params.slice(ctx.ast)[lazy.operand.getArgId()],
857 };
858 },
802 else => {},859 else => {},
803 }860 }
804861
...@@ -823,6 +880,7 @@ pub const Operand = enum(u32) {...@@ -823,6 +880,7 @@ pub const Operand = enum(u32) {
823 lhs = 4,880 lhs = 4,
824 /// Right hand side of a binary instruction881 /// Right hand side of a binary instruction
825 rhs = 5,882 rhs = 5,
883 _,
826884
827 /// One of:885 /// One of:
828 /// - An argument of a .call node886 /// - An argument of a .call node
src/core/Sema.zig+94-26
...@@ -144,10 +144,15 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value {...@@ -144,10 +144,15 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value {
144 defer s.gpa.free(s.params);144 defer s.gpa.free(s.params);
145145
146 for (fn_decl.params.slice(&s.code), 0..) |param, i| {146 for (fn_decl.params.slice(&s.code), 0..) |param, i| {
147 s.params[i] = try s.resolveType(147 const src = s.inst_map.start.op(Report.Operand.argument(@intCast(i)));
148 s.inst_map.start.op(Report.Operand.argument(@intCast(i))),148 const ty = try s.resolveType(src, param);
149 param,149 if (!ty.hasRuntimeRepresentation(s.ip)) {
150 );150 return s.fail(.param_comptime_only, .{
151 .src = s.lazyNode(src),
152 .type = ty,
153 });
154 }
155 s.params[i] = ty;
151 }156 }
152157
153 const return_type = try s.resolveType(158 const return_type = try s.resolveType(
...@@ -398,9 +403,11 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -398,9 +403,11 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
398 scratch[i] = coerced;403 scratch[i] = coerced;
399 }404 }
400 } else {405 } else {
401 // just copy the args in without checking
402 for (call.args.slice(&s.code), 0..) |arg, i| {406 for (call.args.slice(&s.code), 0..) |arg, i| {
403 const ref = try s.resolveRefRuntime(arg);407 const ref = try s.ensureValueRuntime(
408 in.op(Report.Operand.argument(@intCast(i))),
409 try s.resolveRefRuntime(arg),
410 );
404 scratch[i] = ref;411 scratch[i] = ref;
405 }412 }
406 }413 }
...@@ -475,23 +482,15 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -475,23 +482,15 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
475 .memory => {482 .memory => {
476 const data = s.code.instData(.memory, in);483 const data = s.code.instData(.memory, in);
477484
478 var initial_value = try s.resolveRefRuntime(data.operand);485 const initial_value = try s.resolveRefRuntime(data.operand);
479486
480 switch (initial_value.decode()) {487 const runtime_known = try s.ensureValueRuntime(
481 .value => |v| switch (v.toKey(s.ip)) {488 in.op(.operand),
482 .comptime_float => |val| {489 initial_value,
483 try s.report.warn(.numeric_int_var_ambiguity, .{490 );
484 .node = s.nodeLoc(in, .operand),
485 });
486 initial_value = (try s.makeValue(.{ .float = val })).toAir();
487 },
488 else => {},
489 },
490 else => {},
491 }
492491
493 return try s.addInstRef(.memory, .{492 return try s.addInstRef(.memory, .{
494 .init = initial_value,493 .init = runtime_known,
495 .ty = s.typeOfRef(initial_value),494 .ty = s.typeOfRef(initial_value),
496 });495 });
497 },496 },
...@@ -518,6 +517,29 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -518,6 +517,29 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
518 const operand = try s.resolveRefRuntime(as.operand);517 const operand = try s.resolveRefRuntime(as.operand);
519 return try s.coerceRef(in.op(.rhs), operand, coerce_to.toType(s.ip).?);518 return try s.coerceRef(in.op(.rhs), operand, coerce_to.toType(s.ip).?);
520 },519 },
520 .@"comptime" => {
521 const node: Teg.Inst.RefNode = s.code.instData(.@"comptime", in);
522 const value = try s.resolveRefComptimeAny(node.operand);
523 return value.toAir();
524 },
525 .type_of => {
526 const node: Teg.Inst.RefNode = s.code.instData(.type_of, in);
527 const value = try s.resolveRefComptimeAny(node.operand);
528 return value.typeOf(s.ip).value().toAir();
529 },
530 .compile_error => {
531 const node: Teg.Inst.RefNode = s.code.instData(.compile_error, in);
532 const value = try s.resolveRefComptimeType(
533 in.op(Report.Operand.argument(0)),
534 node.operand,
535 .string,
536 );
537 const str = value.asString(s.ip).toSlice(s.ip);
538 return s.fail(.user_compile_error, .{
539 .src = s.lazyNode(in.op(.main_node)),
540 .msg = try s.report.addString(str),
541 });
542 },
521 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),543 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),
522 }544 }
523}545}
...@@ -662,6 +684,7 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {...@@ -662,6 +684,7 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
662 }684 }
663 switch (to) {685 switch (to) {
664 .@"opaque" => {686 .@"opaque" => {
687
665 //688 //
666 },689 },
667 .bool => {690 .bool => {
...@@ -699,23 +722,68 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {...@@ -699,23 +722,68 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
699 else => {},722 else => {},
700 },723 },
701 .string => {724 .string => {
702 //725 // TODO:
703 },726 },
704 .noreturn => {727 .noreturn => {
705 //728 // nothing coerces to `noreturn`
706 },729 },
707 .type => {730 .numeric_literal => {
708 if (value == .void) return .void;731 if (value.toFloat(s.ip)) |float| {
732 return try s.makeValue(.{
733 .comptime_float = float,
734 });
735 }
709 },736 },
710 .numeric_literal => {},737 .type => {
711 .void => {
712 if (value == .void) return .void;738 if (value == .void) return .void;
739
740 // non-types do not coerce to types
713 },741 },
742 .void => if (value == .void) return .void,
714 else => {},743 else => {},
715 }744 }
716 return s.coersionError(src, from, to);745 return s.coersionError(src, from, to);
717}746}
718747
748fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref {
749 switch (ref.decode()) {
750 .value => |v| {
751 const ty = v.typeOf(s.ip);
752 switch (ty) {
753 .numeric_literal => {
754 try s.report.warn(.numeric_int_var_ambiguity, .{
755 .src = s.lazyNode(src),
756 });
757 return (try s.makeValue(.{
758 .float = v.toFloat(s.ip) orelse unreachable,
759 })).toAir();
760 },
761 .type,
762 .noreturn,
763 => return s.fail(.runtime_incompatible_type, .{
764 .src = s.lazyNode(src),
765 .type = ty,
766 }),
767 .@"opaque" => @panic("TODO: represent comptime-known opaques better"),
768 // Allowed at runtime
769 .bool,
770 .float,
771 .int32,
772 .string,
773 .void,
774 => {},
775 _ => switch (ty.tag(s.ip)) {
776 .simple_type => unreachable, // handled above
777 // Allowed at runtime
778 .type_function => {},
779 },
780 }
781 },
782 else => {},
783 }
784 return ref;
785}
786
719fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value {787fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value {
720 const decl = s.ip.declPtr(decl_index);788 const decl = s.ip.declPtr(decl_index);
721 switch (decl.analysis) {789 switch (decl.analysis) {
src/core/Teg.Dump.zig+1
...@@ -171,6 +171,7 @@ const State = struct {...@@ -171,6 +171,7 @@ const State = struct {
171 .import,171 .import,
172 .fetch,172 .fetch,
173 .compile_error,173 .compile_error,
174 .type_of,
174 => |comptime_tag| {175 => |comptime_tag| {
175 const data = teg.instData(comptime_tag, inst);176 const data = teg.instData(comptime_tag, inst);
176 try writer.writeAll("(");177 try writer.writeAll("(");
src/core/Teg.zig+8-6
...@@ -78,6 +78,8 @@ pub const Inst = struct {...@@ -78,6 +78,8 @@ pub const Inst = struct {
78 fetch,78 fetch,
79 /// Implements the `@compileError` builtin79 /// Implements the `@compileError` builtin
80 compile_error,80 compile_error,
81 /// Implements the `@typeOf` builtin
82 type_of,
8183
82 pub fn Data(comptime tag: Tag) type {84 pub fn Data(comptime tag: Tag) type {
83 return switch (tag) {85 return switch (tag) {
...@@ -93,7 +95,6 @@ pub const Inst = struct {...@@ -93,7 +95,6 @@ pub const Inst = struct {
93 .load_decl => StrNode,95 .load_decl => StrNode,
94 .param => DataNode,96 .param => DataNode,
95 .store => PlNode(Store),97 .store => PlNode(Store),
96 .@"comptime" => RefNode,
9798
98 // Single instruction operand99 // Single instruction operand
99 .memory,100 .memory,
...@@ -107,6 +108,8 @@ pub const Inst = struct {...@@ -107,6 +108,8 @@ pub const Inst = struct {
107 .import,108 .import,
108 .fetch,109 .fetch,
109 .compile_error,110 .compile_error,
111 .@"comptime",
112 .type_of,
110 => RefNode,113 => RefNode,
111114
112 // Two instruction operands115 // Two instruction operands
...@@ -403,11 +406,10 @@ pub fn instPayload(teg: *const Teg, comptime tag: Inst.Tag, index: Inst.Index) t...@@ -403,11 +406,10 @@ pub fn instPayload(teg: *const Teg, comptime tag: Inst.Tag, index: Inst.Index) t
403}406}
404407
405pub fn instNodeOffset(teg: *const Teg, index: Inst.Index) i32 {408pub fn instNodeOffset(teg: *const Teg, index: Inst.Index) i32 {
406 switch (teg.instTag(index)) {409 return switch (teg.instTag(index)) {
407 inline else => |tag| {410 .declaration => 0,
408 return teg.instData(tag, index).src_node;411 inline else => |tag| teg.instData(tag, index).src_node,
409 },412 };
410 }
411}413}
412414
413pub fn blockInstructions(teg: *const Teg, index: Inst.Index) []const Inst.Index {415pub fn blockInstructions(teg: *const Teg, index: Inst.Index) []const Inst.Index {
src/core/Transform.zig+81-20
...@@ -276,12 +276,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I...@@ -276,12 +276,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
276 };276 };
277 try t.extra.list.appendSlice(t.gpa, @ptrCast(temp));277 try t.extra.list.appendSlice(t.gpa, @ptrCast(temp));
278278
279 const block = try t.addRootInst(.{279 const top_level_code_ref: Inst.Ref = ref: {
280 .tag = .block,
281 .data = undefined,
282 });
283
284 {
285 const scratch_top = t.scratch.items.len;280 const scratch_top = t.scratch.items.len;
286 defer t.scratch.shrinkRetainingCapacity(scratch_top);281 defer t.scratch.shrinkRetainingCapacity(scratch_top);
287282
...@@ -289,11 +284,23 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I...@@ -289,11 +284,23 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
289 try t.transformModuleStatement(decl, statement);284 try t.transformModuleStatement(decl, statement);
290 }285 }
291286
287 // if there are no new instructions, we dont have to create a block context
288 if (scratch_top == t.scratch.items.len) {
289 break :ref .void;
290 }
291
292 const block = try t.addRootInst(.{
293 .tag = .block,
294 .data = undefined,
295 });
296
292 t.getInstData(block).* = EncodedData.encode(.block, .{297 t.getInstData(block).* = EncodedData.encode(.block, .{
293 .src_node = decl.nodeOffset(module_node),298 .src_node = decl.nodeOffset(module_node),
294 .extra_index = try t.extra.append(t.gpa, IndexList, try t.scratchToIndexList(scratch_top)),299 .extra_index = try t.extra.append(t.gpa, IndexList, try t.scratchToIndexList(scratch_top)),
295 });300 });
296 }301
302 break :ref block.toRef();
303 };
297304
298 const decl_extra, var trail = try t.extra.appendTrailing(t.gpa, Inst.Decl, .{305 const decl_extra, var trail = try t.extra.appendTrailing(t.gpa, Inst.Decl, .{
299 .kind = .function,306 .kind = .function,
...@@ -305,7 +312,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I...@@ -305,7 +312,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
305 trail.writeStruct(Inst.DeclFn, .{312 trail.writeStruct(Inst.DeclFn, .{
306 .params = .{ .start = 0, .len = 0 },313 .params = .{ .start = 0, .len = 0 },
307 .return_type = Inst.Ref.void,314 .return_type = Inst.Ref.void,
308 .body = block.toRef(),315 .body = top_level_code_ref,
309 });316 });
310317
311 t.getInstData(top_level_fn).* = EncodedData.encode(.declaration, .{318 t.getInstData(top_level_fn).* = EncodedData.encode(.declaration, .{
...@@ -480,11 +487,12 @@ fn transformExpressionToBlock(...@@ -480,11 +487,12 @@ fn transformExpressionToBlock(
480487
481 // if there are no new instructions, we dont have to create a block context488 // if there are no new instructions, we dont have to create a block context
482 if (scratch_top == t.scratch.items.len) {489 if (scratch_top == t.scratch.items.len) {
483 const result = expr orelse unreachable; // null means we added break or return490 const result = expr orelse unreachable; // null means we *added* break or return
484491
485 // TODO: evaluate if `if` blocks would be fine in sema with instruction refs492 // TODO: evaluate if `if` blocks would be fine in sema with instruction refs
486 // with this extra if check here, we filter out non-blocks.493 // with this extra if check here, we filter out non-blocks.
487 if (result.toIndex() == null) {494 if (result.toValue() != null) {
495 assert(block == null);
488 return result;496 return result;
489 }497 }
490 }498 }
...@@ -519,6 +527,7 @@ fn transformExpressionToBlock(...@@ -519,6 +527,7 @@ fn transformExpressionToBlock(
519 }527 }
520528
521 const content = try t.scratchToIndexList(scratch_top);529 const content = try t.scratchToIndexList(scratch_top);
530 assert(content.len > 0);
522531
523 t.getInstData(ret_block).* = EncodedData.encode(.block, .{532 t.getInstData(ret_block).* = EncodedData.encode(.block, .{
524 .src_node = decl.nodeOffset(node),533 .src_node = decl.nodeOffset(node),
...@@ -554,25 +563,29 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc...@@ -554,25 +563,29 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc
554 return value;563 return value;
555}564}
556565
557const Builtin = enum {566pub const Builtin = enum {
558 js,567 js,
559 import,568 import,
560 fetch,569 fetch,
561 compile_error,570 compile_error,
571 type_of,
562572
563 const map = std.ComptimeStringMap(Builtin, .{573 const map = std.ComptimeStringMap(Builtin, .{
564 .{ "js", .js },574 // sort-lines: start
565 .{ "import", .import },
566 .{ "fetch", .fetch },
567 .{ "compileError", .compile_error },575 .{ "compileError", .compile_error },
576 .{ "fetch", .fetch },
577 .{ "import", .import },
578 .{ "js", .js },
579 .{ "typeOf", .type_of },
580 // sort-lines: end
568 });581 });
569582
570 fn builtin_js(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?Inst.Ref {583 fn builtin_js(t: *Transform, decl: BlockContext, node: NodeIndex, args: []const NodeIndex) !?Inst.Ref {
571 const trace = tracer.trace(@src());584 const trace = tracer.trace(@src());
572 defer trace.end();585 defer trace.end();
573586
574 if (args.len != 1) {587 if (args.len != 1) {
575 // try t.report.err(.invalid_builtin_args, .{ .id = "js" });588 try t.report.err(.builtin_mismatch_args, .{ .node = node, .args = 1 });
576 return .void;589 return .void;
577 }590 }
578591
...@@ -586,6 +599,48 @@ const Builtin = enum {...@@ -586,6 +599,48 @@ const Builtin = enum {
586 }),599 }),
587 });600 });
588 }601 }
602
603 fn builtin_compileError(t: *Transform, decl: BlockContext, node: NodeIndex, args: []const NodeIndex) !?Inst.Ref {
604 const trace = tracer.trace(@src());
605 defer trace.end();
606
607 if (args.len != 1) {
608 try t.report.err(.builtin_mismatch_args, .{ .node = node, .args = 1 });
609 return .void;
610 }
611
612 const source = try t.transformExpression(decl, args[0]) orelse
613 return null;
614
615 return try t.addInstRef(.{
616 .tag = .compile_error,
617 .data = EncodedData.encode(.js, .{
618 .src_node = decl.nodeOffset(node),
619 .operand = source,
620 }),
621 });
622 }
623
624 fn builtin_typeOf(t: *Transform, decl: BlockContext, node: NodeIndex, args: []const NodeIndex) !?Inst.Ref {
625 const trace = tracer.trace(@src());
626 defer trace.end();
627
628 if (args.len != 1) {
629 try t.report.err(.builtin_mismatch_args, .{ .node = node, .args = 1 });
630 return .void;
631 }
632
633 const source = try t.transformExpression(decl, args[0]) orelse
634 return null;
635
636 return try t.addInstRef(.{
637 .tag = .type_of,
638 .data = EncodedData.encode(.js, .{
639 .src_node = decl.nodeOffset(node),
640 .operand = source,
641 }),
642 });
643 }
589};644};
590645
591/// This is the primary transformation function: converting an AST expression into instructions.646/// This is the primary transformation function: converting an AST expression into instructions.
...@@ -874,10 +929,11 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -874,10 +929,11 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
874 const args = base.slice(&t.ast);929 const args = base.slice(&t.ast);
875930
876 return switch (builtin) {931 return switch (builtin) {
877 .js => Builtin.builtin_js(t, decl, args),932 .js => Builtin.builtin_js(t, decl, node, args),
878 // .import => Builtin.import(t, decl, args),933 // .import => Builtin.import(t, decl, node, args),
879 // .fetch => Builtin.fetch(t, decl, args),934 // .fetch => Builtin.fetch(t, decl, node, args),
880 // .compile_error => Builtin.compile_error(t, decl, args),935 .compile_error => Builtin.builtin_compileError(t, decl, node, args),
936 .type_of => Builtin.builtin_typeOf(t, decl, node, args),
881 else => @panic("TODO"),937 else => @panic("TODO"),
882 };938 };
883 },939 },
...@@ -1071,6 +1127,11 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1071,6 +1127,11 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1071 return null; // control flow ends1127 return null; // control flow ends
1072 },1128 },
10731129
1130 .group => {
1131 const group_node = t.ast.nodeData(node).un;
1132 return try t.transformExpression(decl, group_node.expr);
1133 },
1134
1074 else => |tag| std.debug.panic("TODO: expr {s}", .{@tagName(tag)}),1135 else => |tag| std.debug.panic("TODO: expr {s}", .{@tagName(tag)}),
1075 }1136 }
1076}1137}