authorchloe caruso <git@paperclover.net> 2024-09-19 23:16:03-07:00
committerchloe caruso <git@paperclover.net> 2024-09-19 23:16:03-07:00
log9a877d0c83e47e323a4483986474e0a84200d0e9
tree7d42d77acc8ee1b1002be76c1c7d0193e70e6771
parent6429cda22ae782bcc505da15fcfd42368ff42aac

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


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

CHANGELOG.md+1
......@@ -1,6 +1,7 @@
11## 0.1.7
22
33- feat: allow any expression as a function body
4- fix: coerce `break` operands and implicit breaks to result type
45
56## 0.1.6
67
src/core/Sema.zig+11-6
......@@ -312,7 +312,6 @@ const InstMap = struct {
312312 pub fn getOrNull(map: *const InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref {
313313 const source_i = @intFromEnum(teg_code);
314314 const start_i = @intFromEnum(map.start);
315 assert(source_i >= start_i and source_i <= start_i + map.items.len);
316315 if (assertFailed(source_i >= start_i and source_i <= start_i + map.items.len)) {
317316 std.debug.panic("attempt fetching instruction %{d} which is out of range of %{d}-%{d}", .{
318317 source_i,
......@@ -538,11 +537,17 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
538537 const param: Teg.DataNode = s.frame.currentData(.param);
539538 assert(s.frame.mode == .run_time); // never emitted in a comptime block
540539 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;
542 return s.addRef(.arg, .{
543 .type = param_types[param.data],
544 .arg_number = param.data,
545 });
540 const proto = s.owner.unwrap().func.toKey(s.ip).func.ty.toUnresolvedFunctionPrototype(s.ip).?;
541 switch (param.data) {
542 std.math.maxInt(u32) => {
543 const return_type = proto.return_type orelse unreachable; // never emitted for inferred return types
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 }
546551 },
547552 .string => {
548553 const string: Teg.SlicedString = s.frame.currentPayload(.string);
src/core/Teg.Dump.zig+10-5
......@@ -104,8 +104,13 @@ const State = struct {
104104 .param => {
105105 const data = teg.instData(.param, inst);
106106 try writer.writeAll("(");
107 try tty.setColor(writer, .yellow);
108 try writer.print("{d}", .{data.data});
107 if (data.data == std.math.maxInt(u32)) {
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 }
109114 try tty.setColor(writer, .reset);
110115 try writer.writeAll(")");
111116 },
......@@ -225,7 +230,7 @@ const State = struct {
225230 const call: Teg.Call = teg.instData(.call, inst).extra_index.get(teg);
226231 try writer.writeAll("(");
227232 try s.dumpRefMarker(writer, call.base);
228 try writer.writeAll(",");
233 try writer.writeAll(", ");
229234 try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']');
230235 try writer.writeAll(")");
231236 },
......@@ -233,7 +238,7 @@ const State = struct {
233238 const call: Teg.JsNew = teg.instData(.js_new, inst).extra_index.get(teg);
234239 try writer.writeAll("(");
235240 try s.dumpRefMarker(writer, call.base);
236 try writer.writeAll(",");
241 try writer.writeAll(", ");
237242 try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']');
238243 try writer.writeAll(")");
239244 },
......@@ -470,7 +475,7 @@ const State = struct {
470475 }
471476
472477 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});
474479 if (list.len > 0) {
475480 state.indent += 1;
476481
src/core/Teg.zig+1
......@@ -42,6 +42,7 @@ pub const Tag = enum(u8) {
4242 /// The resulting Air Ref will be guaranteed to be the coerced type.
4343 coercion,
4444 /// Load a parameter
45 /// if data value is maxInt(u32), the function return type is used
4546 param,
4647 /// Asserts the operand is not void. Used for discard statements.
4748 discard,
src/core/Transform.zig+42-7
......@@ -631,12 +631,10 @@ const BlockType = enum {
631631 compile_time,
632632};
633633
634// todo: clarify
635634/// Visit and convert an expression into either a Value with no instructions, or
636/// a single instruction.
637///
638/// This is important for function bodies, loop bodies, and branches, so that
639/// contents are grouped into a nested block.
635/// a block instruction containing the expression. This is important for
636/// function bodies, loop bodies, branches, and certain mode-switching contexts
637/// such as `comptime` and `@typeOf`.
640638fn transformToBlock(
641639 t: *Transform,
642640 ctx: BlockContext,
......@@ -672,6 +670,15 @@ fn transformToBlock(
672670 });
673671 param.value = .{ .immutable = ref };
674672 }
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 }
675682 }
676683
677684 const expr = try t.transformExpression(
......@@ -723,11 +730,27 @@ fn transformToBlock(
723730
724731 // The block must end with a break
725732 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
726749 _ = try t.addIndex(.@"break", .{
727750 .src_node = ctx.nodeOffset(node),
728751 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
729752 .block = ret_block,
730 .operand = result,
753 .operand = coerced_result,
731754 }),
732755 });
733756 }
......@@ -1404,11 +1427,23 @@ fn transformExpression(
14041427 else
14051428 .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
14071442 _ = try t.addIndex(.@"break", .{
14081443 .src_node = ctx.nodeOffset(node),
14091444 .extra_index = try t.extra.append(t.gpa, Teg.Break, .{
14101445 .block = try block.getInst(t),
1411 .operand = operand,
1446 .operand = coerced_operand,
14121447 }),
14131448 });
14141449
test/behavior/function-2
......@@ -6,8 +6,6 @@ fn hello(n: int) -> int {
66 if n > 1 {
77 hello(n - 1) + 1
88 } else {
9 // TODO: fix a missing coercion
10 let i: int = 1;
119 i
1210 }
1311}
test/behavior/tuple+2-2
......@@ -10,5 +10,5 @@ fn main {
1010 assertEqual(x.1, f(2.5));
1111}
1212
13fn f(a: float) -> float { a }
14fn i(a: int) -> int { a }
13fn f(a: float) { a }
14fn i(a: int) { a }
test/compiler_tests.zig+12
......@@ -211,6 +211,10 @@ pub fn runTestCaseWrap(
211211 defer case.logs = gpa.dupe(u8, logs.items) catch
212212 "[logs failed to allocate]";
213213
214 std.debug.assert(current_case == null);
215 current_case = case.*;
216 defer current_case = null;
217
214218 runTestCase(
215219 alloc,
216220 case.path,
......@@ -497,6 +501,14 @@ fn getFlag(name: []const u8, full_arg: []const u8) ?[]const u8 {
497501 return null;
498502}
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
500512const std = @import("std");
501513const Dir = std.fs.Dir;
502514