8 files changed, 79 insertions(+), 22 deletions(-)
CHANGELOG.md+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | ## 0.1.7 |
| 2 | 2 | |
| 3 | 3 | - feat: allow any expression as a function body |
| 4 | - fix: coerce `break` operands and implicit breaks to result type | |
| 4 | 5 | |
| 5 | 6 | ## 0.1.6 |
| 6 | 7 |
src/core/Sema.zig+11-6| ... | ... | @@ -312,7 +312,6 @@ const InstMap = struct { |
| 312 | 312 | pub fn getOrNull(map: *const InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref { |
| 313 | 313 | const source_i = @intFromEnum(teg_code); |
| 314 | 314 | const start_i = @intFromEnum(map.start); |
| 315 | assert(source_i >= start_i and source_i <= start_i + map.items.len); | |
| 316 | 315 | if (assertFailed(source_i >= start_i and source_i <= start_i + map.items.len)) { |
| 317 | 316 | std.debug.panic("attempt fetching instruction %{d} which is out of range of %{d}-%{d}", .{ |
| 318 | 317 | source_i, |
| ... | ... | @@ -538,11 +537,17 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 538 | 537 | const param: Teg.DataNode = s.frame.currentData(.param); |
| 539 | 538 | assert(s.frame.mode == .run_time); // never emitted in a comptime block |
| 540 | 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; | |
| 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 | } | |
| 546 | 551 | }, |
| 547 | 552 | .string => { |
| 548 | 553 | const string: Teg.SlicedString = s.frame.currentPayload(.string); |
src/core/Teg.Dump.zig+10-5| ... | ... | @@ -104,8 +104,13 @@ const State = struct { |
| 104 | 104 | .param => { |
| 105 | 105 | const data = teg.instData(.param, inst); |
| 106 | 106 | 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 | } | |
| 109 | 114 | try tty.setColor(writer, .reset); |
| 110 | 115 | try writer.writeAll(")"); |
| 111 | 116 | }, |
| ... | ... | @@ -225,7 +230,7 @@ const State = struct { |
| 225 | 230 | const call: Teg.Call = teg.instData(.call, inst).extra_index.get(teg); |
| 226 | 231 | try writer.writeAll("("); |
| 227 | 232 | try s.dumpRefMarker(writer, call.base); |
| 228 | try writer.writeAll(","); | |
| 233 | try writer.writeAll(", "); | |
| 229 | 234 | try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']'); |
| 230 | 235 | try writer.writeAll(")"); |
| 231 | 236 | }, |
| ... | ... | @@ -233,7 +238,7 @@ const State = struct { |
| 233 | 238 | const call: Teg.JsNew = teg.instData(.js_new, inst).extra_index.get(teg); |
| 234 | 239 | try writer.writeAll("("); |
| 235 | 240 | try s.dumpRefMarker(writer, call.base); |
| 236 | try writer.writeAll(","); | |
| 241 | try writer.writeAll(", "); | |
| 237 | 242 | try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']'); |
| 238 | 243 | try writer.writeAll(")"); |
| 239 | 244 | }, |
| ... | ... | @@ -470,7 +475,7 @@ const State = struct { |
| 470 | 475 | } |
| 471 | 476 | |
| 472 | 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 | 479 | if (list.len > 0) { |
| 475 | 480 | state.indent += 1; |
| 476 | 481 |
src/core/Teg.zig+1| ... | ... | @@ -42,6 +42,7 @@ pub const Tag = enum(u8) { |
| 42 | 42 | /// The resulting Air Ref will be guaranteed to be the coerced type. |
| 43 | 43 | coercion, |
| 44 | 44 | /// Load a parameter |
| 45 | /// if data value is maxInt(u32), the function return type is used | |
| 45 | 46 | param, |
| 46 | 47 | /// Asserts the operand is not void. Used for discard statements. |
| 47 | 48 | discard, |
src/core/Transform.zig+42-7| ... | ... | @@ -631,12 +631,10 @@ const BlockType = enum { |
| 631 | 631 | compile_time, |
| 632 | 632 | }; |
| 633 | 633 | |
| 634 | // todo: clarify | |
| 635 | 634 | /// 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`. | |
| 640 | 638 | fn transformToBlock( |
| 641 | 639 | t: *Transform, |
| 642 | 640 | ctx: BlockContext, |
| ... | ... | @@ -672,6 +670,15 @@ fn transformToBlock( |
| 672 | 670 | }); |
| 673 | 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 | 684 | const expr = try t.transformExpression( |
| ... | ... | @@ -723,11 +730,27 @@ fn transformToBlock( |
| 723 | 730 | |
| 724 | 731 | // The block must end with a break |
| 725 | 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 | 749 | _ = try t.addIndex(.@"break", .{ |
| 727 | 750 | .src_node = ctx.nodeOffset(node), |
| 728 | 751 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 729 | 752 | .block = ret_block, |
| 730 | .operand = result, | |
| 753 | .operand = coerced_result, | |
| 731 | 754 | }), |
| 732 | 755 | }); |
| 733 | 756 | } |
| ... | ... | @@ -1404,11 +1427,23 @@ fn transformExpression( |
| 1404 | 1427 | else |
| 1405 | 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 | 1442 | _ = try t.addIndex(.@"break", .{ |
| 1408 | 1443 | .src_node = ctx.nodeOffset(node), |
| 1409 | 1444 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 1410 | 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 | 6 | if n > 1 { |
| 7 | 7 | hello(n - 1) + 1 |
| 8 | 8 | } else { |
| 9 | // TODO: fix a missing coercion | |
| 10 | let i: int = 1; | |
| 11 | 9 | i |
| 12 | 10 | } |
| 13 | 11 | } |
test/behavior/tuple+2-2| ... | ... | @@ -10,5 +10,5 @@ fn main { |
| 10 | 10 | assertEqual(x.1, f(2.5)); |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | fn f(a: float) -> float { a } | |
| 14 | fn i(a: int) -> int { a } | |
| 13 | fn f(a: float) { a } | |
| 14 | fn i(a: int) { a } |
test/compiler_tests.zig+12| ... | ... | @@ -211,6 +211,10 @@ pub fn runTestCaseWrap( |
| 211 | 211 | defer case.logs = gpa.dupe(u8, logs.items) catch |
| 212 | 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 | 218 | runTestCase( |
| 215 | 219 | alloc, |
| 216 | 220 | case.path, |
| ... | ... | @@ -497,6 +501,14 @@ fn getFlag(name: []const u8, full_arg: []const u8) ?[]const u8 { |
| 497 | 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 | 512 | const std = @import("std"); |
| 501 | 513 | const Dir = std.fs.Dir; |
| 502 | 514 |