8 files changed, 79 insertions(+), 22 deletions(-)
CHANGELOG.md+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | ## 0.1.7 | 1 | ## 0.1.7 |
| 2 | 2 | ||
| 3 | - feat: allow any expression as a function body | 3 | - feat: allow any expression as a function body |
| 4 | - fix: coerce `break` operands and implicit breaks to result type | ||
| 4 | 5 | ||
| 5 | ## 0.1.6 | 6 | ## 0.1.6 |
| 6 | 7 |
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 block | 538 | assert(s.frame.mode == .run_time); // never emitted in a comptime block |
| 540 | assert(s.owner.kind == .func); // only emitted in function body | 539 | 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 | } |
| 471 | 476 | ||
| 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; |
| 476 | 481 |
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 parameter | 44 | /// 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 | }; |
| 633 | 633 | ||
| 634 | // todo: clarify | ||
| 635 | /// Visit and convert an expression into either a Value with no instructions, or | 634 | /// 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 that | 637 | /// such as `comptime` and `@typeOf`. |
| 639 | /// contents are grouped into a nested block. | ||
| 640 | fn transformToBlock( | 638 | fn 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 | } |
| 676 | 683 | ||
| 677 | const expr = try t.transformExpression( | 684 | const expr = try t.transformExpression( |
| ... | @@ -723,11 +730,27 @@ fn transformToBlock( | ... | @@ -723,11 +730,27 @@ fn transformToBlock( |
| 723 | 730 | ||
| 724 | // The block must end with a break | 731 | // 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 | else | 1427 | else |
| 1405 | .void; | 1428 | .void; |
| 1406 | 1429 | ||
| 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 | }); |
| 1414 | 1449 |
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) + 1 | 7 | hello(n - 1) + 1 |
| 8 | } else { | 8 | } else { |
| 9 | // TODO: fix a missing coercion | ||
| 10 | let i: int = 1; | ||
| 11 | i | 9 | 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 | } |
| 12 | 12 | ||
| 13 | fn f(a: float) -> float { a } | 13 | fn f(a: float) { a } |
| 14 | fn i(a: int) -> int { a } | 14 | fn 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) catch | 211 | defer case.logs = gpa.dupe(u8, logs.items) catch |
| 212 | "[logs failed to allocate]"; | 212 | "[logs failed to allocate]"; |
| 213 | 213 | ||
| 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 | } |
| 499 | 503 | ||
| 504 | threadlocal var current_case: ?Case = null; | ||
| 505 | pub 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 | |||
| 500 | const std = @import("std"); | 512 | const std = @import("std"); |
| 501 | const Dir = std.fs.Dir; | 513 | const Dir = std.fs.Dir; |
| 502 | 514 |