authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-19 23:16:03-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-19 23:16:03-07:00
log9f21c3c63e5825010c82063bee7b3d9164d6b8c2
tree7d42d77acc8ee1b1002be76c1c7d0193e70e6771
parentb0c173910c8131298d9cc74335843fdcf91d4225
signature Commit is signed but in an unrecognized format.

fix: coerce `break` operands and implicit breaks to result type


8 files changed, 79 insertions(+), 22 deletions(-)

CHANGELOG.md+1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1## 0.1.71## 0.1.7
22
3- feat: allow any expression as a function body3- feat: allow any expression as a function body
4- fix: coerce `break` operands and implicit breaks to result type
45
5## 0.1.66## 0.1.6
67
src/core/Sema.zig+11-6
...@@ -312,7 +312,6 @@ const InstMap = struct {...@@ -312,7 +312,6 @@ const InstMap = struct {
312 pub fn getOrNull(map: *const InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref {312 pub fn getOrNull(map: *const InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref {
313 const source_i = @intFromEnum(teg_code);313 const source_i = @intFromEnum(teg_code);
314 const start_i = @intFromEnum(map.start);314 const start_i = @intFromEnum(map.start);
315 assert(source_i >= start_i and source_i <= start_i + map.items.len);
316 if (assertFailed(source_i >= start_i and source_i <= start_i + map.items.len)) {315 if (assertFailed(source_i >= start_i and source_i <= start_i + map.items.len)) {
317 std.debug.panic("attempt fetching instruction %{d} which is out of range of %{d}-%{d}", .{316 std.debug.panic("attempt fetching instruction %{d} which is out of range of %{d}-%{d}", .{
318 source_i,317 source_i,
...@@ -538,11 +537,17 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -538,11 +537,17 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
538 const param: Teg.DataNode = s.frame.currentData(.param);537 const param: Teg.DataNode = s.frame.currentData(.param);
539 assert(s.frame.mode == .run_time); // never emitted in a comptime block538 assert(s.frame.mode == .run_time); // never emitted in a comptime block
540 assert(s.owner.kind == .func); // only emitted in function body539 assert(s.owner.kind == .func); // only emitted in function body
541 const param_types = s.owner.unwrap().func.toKey(s.ip).func.ty.toUnresolvedFunctionPrototype(s.ip).?.param_types;540 const proto = s.owner.unwrap().func.toKey(s.ip).func.ty.toUnresolvedFunctionPrototype(s.ip).?;
542 return s.addRef(.arg, .{541 switch (param.data) {
543 .type = param_types[param.data],542 std.math.maxInt(u32) => {
544 .arg_number = param.data,543 const return_type = proto.return_type orelse unreachable; // never emitted for inferred return types
545 });544 return return_type.value().toAir();
545 },
546 else => return s.addRef(.arg, .{
547 .type = proto.param_types[param.data],
548 .arg_number = param.data,
549 }),
550 }
546 },551 },
547 .string => {552 .string => {
548 const string: Teg.SlicedString = s.frame.currentPayload(.string);553 const string: Teg.SlicedString = s.frame.currentPayload(.string);
src/core/Teg.Dump.zig+10-5
...@@ -104,8 +104,13 @@ const State = struct {...@@ -104,8 +104,13 @@ const State = struct {
104 .param => {104 .param => {
105 const data = teg.instData(.param, inst);105 const data = teg.instData(.param, inst);
106 try writer.writeAll("(");106 try writer.writeAll("(");
107 try tty.setColor(writer, .yellow);107 if (data.data == std.math.maxInt(u32)) {
108 try writer.print("{d}", .{data.data});108 try tty.setColor(writer, .bright_black);
109 try writer.writeAll("<return type>");
110 } else {
111 try tty.setColor(writer, .yellow);
112 try writer.print("{d}", .{data.data});
113 }
109 try tty.setColor(writer, .reset);114 try tty.setColor(writer, .reset);
110 try writer.writeAll(")");115 try writer.writeAll(")");
111 },116 },
...@@ -225,7 +230,7 @@ const State = struct {...@@ -225,7 +230,7 @@ const State = struct {
225 const call: Teg.Call = teg.instData(.call, inst).extra_index.get(teg);230 const call: Teg.Call = teg.instData(.call, inst).extra_index.get(teg);
226 try writer.writeAll("(");231 try writer.writeAll("(");
227 try s.dumpRefMarker(writer, call.base);232 try s.dumpRefMarker(writer, call.base);
228 try writer.writeAll(",");233 try writer.writeAll(", ");
229 try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']');234 try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']');
230 try writer.writeAll(")");235 try writer.writeAll(")");
231 },236 },
...@@ -233,7 +238,7 @@ const State = struct {...@@ -233,7 +238,7 @@ const State = struct {
233 const call: Teg.JsNew = teg.instData(.js_new, inst).extra_index.get(teg);238 const call: Teg.JsNew = teg.instData(.js_new, inst).extra_index.get(teg);
234 try writer.writeAll("(");239 try writer.writeAll("(");
235 try s.dumpRefMarker(writer, call.base);240 try s.dumpRefMarker(writer, call.base);
236 try writer.writeAll(",");241 try writer.writeAll(", ");
237 try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']');242 try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']');
238 try writer.writeAll(")");243 try writer.writeAll(")");
239 },244 },
...@@ -470,7 +475,7 @@ const State = struct {...@@ -470,7 +475,7 @@ const State = struct {
470 }475 }
471476
472 fn dumpRefMarkerList(state: *State, writer: anytype, list: []const Teg.Inst.Ref, l: u8, r: u8) !void {477 fn dumpRefMarkerList(state: *State, writer: anytype, list: []const Teg.Inst.Ref, l: u8, r: u8) !void {
473 try writer.print(" {c}", .{l});478 try writer.print("{c}", .{l});
474 if (list.len > 0) {479 if (list.len > 0) {
475 state.indent += 1;480 state.indent += 1;
476481
src/core/Teg.zig+1
...@@ -42,6 +42,7 @@ pub const Tag = enum(u8) {...@@ -42,6 +42,7 @@ pub const Tag = enum(u8) {
42 /// The resulting Air Ref will be guaranteed to be the coerced type.42 /// The resulting Air Ref will be guaranteed to be the coerced type.
43 coercion,43 coercion,
44 /// Load a parameter44 /// Load a parameter
45 /// if data value is maxInt(u32), the function return type is used
45 param,46 param,
46 /// Asserts the operand is not void. Used for discard statements.47 /// Asserts the operand is not void. Used for discard statements.
47 discard,48 discard,
src/core/Transform.zig+42-7
...@@ -631,12 +631,10 @@ const BlockType = enum {...@@ -631,12 +631,10 @@ const BlockType = enum {
631 compile_time,631 compile_time,
632};632};
633633
634// todo: clarify
635/// Visit and convert an expression into either a Value with no instructions, or634/// Visit and convert an expression into either a Value with no instructions, or
636/// a single instruction.635/// a block instruction containing the expression. This is important for
637///636/// function bodies, loop bodies, branches, and certain mode-switching contexts
638/// This is important for function bodies, loop bodies, and branches, so that637/// such as `comptime` and `@typeOf`.
639/// contents are grouped into a nested block.
640fn transformToBlock(638fn transformToBlock(
641 t: *Transform,639 t: *Transform,
642 ctx: BlockContext,640 ctx: BlockContext,
...@@ -672,6 +670,15 @@ fn transformToBlock(...@@ -672,6 +670,15 @@ fn transformToBlock(
672 });670 });
673 param.value = .{ .immutable = ref };671 param.value = .{ .immutable = ref };
674 }672 }
673
674 if (result_type == .ref and result_type.ref.decode() == .index) {
675 result_type_storage = .{
676 .ref = try t.addRef(.param, .{
677 .src_node = ctx.nodeOffset(node),
678 .data = std.math.maxInt(u32),
679 }),
680 };
681 }
675 }682 }
676683
677 const expr = try t.transformExpression(684 const expr = try t.transformExpression(
...@@ -723,11 +730,27 @@ fn transformToBlock(...@@ -723,11 +730,27 @@ fn transformToBlock(
723730
724 // The block must end with a break731 // The block must end with a break
725 if (expr) |result| {732 if (expr) |result| {
733 const result_type_ptr = if (copies_result_type)
734 &result_type_storage
735 else
736 result_type;
737 const coerced_result = switch (result_type_ptr.*) {
738 .none, .used => result,
739 else => try t.addRef(.coercion, .{
740 .src_node = ctx.nodeOffset(node),
741 .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{
742 .type = try result_type_ptr.consume(t) orelse
743 unreachable, // `.none` already handled
744 .operand = result,
745 }),
746 }),
747 };
748
726 _ = try t.addIndex(.@"break", .{749 _ = try t.addIndex(.@"break", .{
727 .src_node = ctx.nodeOffset(node),750 .src_node = ctx.nodeOffset(node),
728 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{751 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
729 .block = ret_block,752 .block = ret_block,
730 .operand = result,753 .operand = coerced_result,
731 }),754 }),
732 });755 });
733 }756 }
...@@ -1404,11 +1427,23 @@ fn transformExpression(...@@ -1404,11 +1427,23 @@ fn transformExpression(
1404 else1427 else
1405 .void;1428 .void;
14061429
1430 const coerced_operand = switch (break_result) {
1431 .none, .used => operand,
1432 else => try t.addRef(.coercion, .{
1433 .src_node = ctx.nodeOffset(node),
1434 .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{
1435 .type = try break_result.consume(t) orelse
1436 unreachable, // `.none` already handled
1437 .operand = operand,
1438 }),
1439 }),
1440 };
1441
1407 _ = try t.addIndex(.@"break", .{1442 _ = try t.addIndex(.@"break", .{
1408 .src_node = ctx.nodeOffset(node),1443 .src_node = ctx.nodeOffset(node),
1409 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{1444 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
1410 .block = try block.getInst(t),1445 .block = try block.getInst(t),
1411 .operand = operand,1446 .operand = coerced_operand,
1412 }),1447 }),
1413 });1448 });
14141449
test/behavior/function-2
...@@ -6,8 +6,6 @@ fn hello(n: int) -> int {...@@ -6,8 +6,6 @@ fn hello(n: int) -> int {
6 if n > 1 {6 if n > 1 {
7 hello(n - 1) + 17 hello(n - 1) + 1
8 } else {8 } else {
9 // TODO: fix a missing coercion
10 let i: int = 1;
11 i9 i
12 }10 }
13}11}
test/behavior/tuple+2-2
...@@ -10,5 +10,5 @@ fn main {...@@ -10,5 +10,5 @@ fn main {
10 assertEqual(x.1, f(2.5));10 assertEqual(x.1, f(2.5));
11}11}
1212
13fn f(a: float) -> float { a }13fn f(a: float) { a }
14fn i(a: int) -> int { a }14fn i(a: int) { a }
test/compiler_tests.zig+12
...@@ -211,6 +211,10 @@ pub fn runTestCaseWrap(...@@ -211,6 +211,10 @@ pub fn runTestCaseWrap(
211 defer case.logs = gpa.dupe(u8, logs.items) catch211 defer case.logs = gpa.dupe(u8, logs.items) catch
212 "[logs failed to allocate]";212 "[logs failed to allocate]";
213213
214 std.debug.assert(current_case == null);
215 current_case = case.*;
216 defer current_case = null;
217
214 runTestCase(218 runTestCase(
215 alloc,219 alloc,
216 case.path,220 case.path,
...@@ -497,6 +501,14 @@ fn getFlag(name: []const u8, full_arg: []const u8) ?[]const u8 {...@@ -497,6 +501,14 @@ fn getFlag(name: []const u8, full_arg: []const u8) ?[]const u8 {
497 return null;501 return null;
498}502}
499503
504threadlocal var current_case: ?Case = null;
505pub fn panic(msg: []const u8, trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize) noreturn {
506 if (current_case) |case| {
507 std.debug.print("Crash during case: {s}\n", .{case.display});
508 }
509 std.debug.panicImpl(trace, first_trace_addr, msg);
510}
511
500const std = @import("std");512const std = @import("std");
501const Dir = std.fs.Dir;513const Dir = std.fs.Dir;
502514