authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-08 18:29:12-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-06-08 18:29:12-04:00
logd2278f21560014c635afe4bc247a9096ecb845de
treee8bca82dcc8d09f6f287054b4c2ba171c190bf37
parent0ff5d7b24e2017199566d1bab75254fa5ef68611
parent05d284c842a5ba21cd836c2b212daa24227a9177
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5566 from ziglang/stage2-garbage-collect-decls

Stage2 garbage collect decls

12 files changed, 1043 insertions(+), 445 deletions(-)

lib/std/comptime_string_map.zig+24-24
...@@ -17,18 +17,18 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs: var) type {...@@ -17,18 +17,18 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs: var) type {
17 };17 };
18 var sorted_kvs: [kvs.len]KV = undefined;18 var sorted_kvs: [kvs.len]KV = undefined;
19 const lenAsc = (struct {19 const lenAsc = (struct {
20 fn lenAsc(a: KV, b: KV) bool {20 fn lenAsc(context: void, a: KV, b: KV) bool {
21 return a.key.len < b.key.len;21 return a.key.len < b.key.len;
22 }22 }
23 }).lenAsc;23 }).lenAsc;
24 for (kvs) |kv, i| {24 for (kvs) |kv, i| {
25 if (V != void) {25 if (V != void) {
26 sorted_kvs[i] = .{.key = kv.@"0", .value = kv.@"1"};26 sorted_kvs[i] = .{ .key = kv.@"0", .value = kv.@"1" };
27 } else {27 } else {
28 sorted_kvs[i] = .{.key = kv.@"0", .value = {}};28 sorted_kvs[i] = .{ .key = kv.@"0", .value = {} };
29 }29 }
30 }30 }
31 std.sort.sort(KV, &sorted_kvs, lenAsc);31 std.sort.sort(KV, &sorted_kvs, {}, lenAsc);
32 const min_len = sorted_kvs[0].key.len;32 const min_len = sorted_kvs[0].key.len;
33 const max_len = sorted_kvs[sorted_kvs.len - 1].key.len;33 const max_len = sorted_kvs[sorted_kvs.len - 1].key.len;
34 var len_indexes: [max_len + 1]usize = undefined;34 var len_indexes: [max_len + 1]usize = undefined;
...@@ -83,11 +83,11 @@ const TestEnum = enum {...@@ -83,11 +83,11 @@ const TestEnum = enum {
8383
84test "ComptimeStringMap list literal of list literals" {84test "ComptimeStringMap list literal of list literals" {
85 const map = ComptimeStringMap(TestEnum, .{85 const map = ComptimeStringMap(TestEnum, .{
86 .{"these", .D},86 .{ "these", .D },
87 .{"have", .A},87 .{ "have", .A },
88 .{"nothing", .B},88 .{ "nothing", .B },
89 .{"incommon", .C},89 .{ "incommon", .C },
90 .{"samelen", .E},90 .{ "samelen", .E },
91 });91 });
9292
93 testMap(map);93 testMap(map);
...@@ -99,11 +99,11 @@ test "ComptimeStringMap array of structs" {...@@ -99,11 +99,11 @@ test "ComptimeStringMap array of structs" {
99 @"1": TestEnum,99 @"1": TestEnum,
100 };100 };
101 const map = ComptimeStringMap(TestEnum, [_]KV{101 const map = ComptimeStringMap(TestEnum, [_]KV{
102 .{.@"0" = "these", .@"1" = .D},102 .{ .@"0" = "these", .@"1" = .D },
103 .{.@"0" = "have", .@"1" = .A},103 .{ .@"0" = "have", .@"1" = .A },
104 .{.@"0" = "nothing", .@"1" = .B},104 .{ .@"0" = "nothing", .@"1" = .B },
105 .{.@"0" = "incommon", .@"1" = .C},105 .{ .@"0" = "incommon", .@"1" = .C },
106 .{.@"0" = "samelen", .@"1" = .E},106 .{ .@"0" = "samelen", .@"1" = .E },
107 });107 });
108108
109 testMap(map);109 testMap(map);
...@@ -115,11 +115,11 @@ test "ComptimeStringMap slice of structs" {...@@ -115,11 +115,11 @@ test "ComptimeStringMap slice of structs" {
115 @"1": TestEnum,115 @"1": TestEnum,
116 };116 };
117 const slice: []const KV = &[_]KV{117 const slice: []const KV = &[_]KV{
118 .{.@"0" = "these", .@"1" = .D},118 .{ .@"0" = "these", .@"1" = .D },
119 .{.@"0" = "have", .@"1" = .A},119 .{ .@"0" = "have", .@"1" = .A },
120 .{.@"0" = "nothing", .@"1" = .B},120 .{ .@"0" = "nothing", .@"1" = .B },
121 .{.@"0" = "incommon", .@"1" = .C},121 .{ .@"0" = "incommon", .@"1" = .C },
122 .{.@"0" = "samelen", .@"1" = .E},122 .{ .@"0" = "samelen", .@"1" = .E },
123 };123 };
124 const map = ComptimeStringMap(TestEnum, slice);124 const map = ComptimeStringMap(TestEnum, slice);
125125
...@@ -142,11 +142,11 @@ test "ComptimeStringMap void value type, slice of structs" {...@@ -142,11 +142,11 @@ test "ComptimeStringMap void value type, slice of structs" {
142 @"0": []const u8,142 @"0": []const u8,
143 };143 };
144 const slice: []const KV = &[_]KV{144 const slice: []const KV = &[_]KV{
145 .{.@"0" = "these"},145 .{ .@"0" = "these" },
146 .{.@"0" = "have"},146 .{ .@"0" = "have" },
147 .{.@"0" = "nothing"},147 .{ .@"0" = "nothing" },
148 .{.@"0" = "incommon"},148 .{ .@"0" = "incommon" },
149 .{.@"0" = "samelen"},149 .{ .@"0" = "samelen" },
150 };150 };
151 const map = ComptimeStringMap(void, slice);151 const map = ComptimeStringMap(void, slice);
152152
lib/std/debug.zig+3-3
...@@ -1003,7 +1003,7 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI...@@ -1003,7 +1003,7 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI
1003 // Even though lld emits symbols in ascending order, this debug code1003 // Even though lld emits symbols in ascending order, this debug code
1004 // should work for programs linked in any valid way.1004 // should work for programs linked in any valid way.
1005 // This sort is so that we can binary search later.1005 // This sort is so that we can binary search later.
1006 std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);1006 std.sort.sort(MachoSymbol, symbols, {}, MachoSymbol.addressLessThan);
10071007
1008 return ModuleDebugInfo{1008 return ModuleDebugInfo{
1009 .base_address = undefined,1009 .base_address = undefined,
...@@ -1058,7 +1058,7 @@ const MachoSymbol = struct {...@@ -1058,7 +1058,7 @@ const MachoSymbol = struct {
1058 return self.nlist.n_value;1058 return self.nlist.n_value;
1059 }1059 }
10601060
1061 fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool {1061 fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool {
1062 return lhs.address() < rhs.address();1062 return lhs.address() < rhs.address();
1063 }1063 }
1064};1064};
...@@ -1300,7 +1300,7 @@ pub const DebugInfo = struct {...@@ -1300,7 +1300,7 @@ pub const DebugInfo = struct {
1300 fs.cwd().openFile(ctx.name, .{ .intended_io_mode = .blocking })1300 fs.cwd().openFile(ctx.name, .{ .intended_io_mode = .blocking })
1301 else1301 else
1302 fs.openSelfExe(.{ .intended_io_mode = .blocking });1302 fs.openSelfExe(.{ .intended_io_mode = .blocking });
1303 1303
1304 const elf_file = copy catch |err| switch (err) {1304 const elf_file = copy catch |err| switch (err) {
1305 error.FileNotFound => return error.MissingDebugInfo,1305 error.FileNotFound => return error.MissingDebugInfo,
1306 else => return err,1306 else => return err,
lib/std/http/headers.zig+2-2
...@@ -58,7 +58,7 @@ const HeaderEntry = struct {...@@ -58,7 +58,7 @@ const HeaderEntry = struct {
58 self.never_index = never_index orelse never_index_default(self.name);58 self.never_index = never_index orelse never_index_default(self.name);
59 }59 }
6060
61 fn compare(a: HeaderEntry, b: HeaderEntry) bool {61 fn compare(context: void, a: HeaderEntry, b: HeaderEntry) bool {
62 if (a.name.ptr != b.name.ptr and a.name.len != b.name.len) {62 if (a.name.ptr != b.name.ptr and a.name.len != b.name.len) {
63 // Things beginning with a colon *must* be before others63 // Things beginning with a colon *must* be before others
64 const a_is_colon = a.name[0] == ':';64 const a_is_colon = a.name[0] == ':';
...@@ -342,7 +342,7 @@ pub const Headers = struct {...@@ -342,7 +342,7 @@ pub const Headers = struct {
342 }342 }
343343
344 pub fn sort(self: *Self) void {344 pub fn sort(self: *Self) void {
345 std.sort.sort(HeaderEntry, self.data.items, HeaderEntry.compare);345 std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare);
346 self.rebuild_index();346 self.rebuild_index();
347 }347 }
348348
lib/std/net.zig+2-2
...@@ -836,7 +836,7 @@ fn linuxLookupName(...@@ -836,7 +836,7 @@ fn linuxLookupName(
836 key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT;836 key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT;
837 addr.sortkey = key;837 addr.sortkey = key;
838 }838 }
839 std.sort.sort(LookupAddr, addrs.span(), addrCmpLessThan);839 std.sort.sort(LookupAddr, addrs.span(), {}, addrCmpLessThan);
840}840}
841841
842const Policy = struct {842const Policy = struct {
...@@ -953,7 +953,7 @@ fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool {...@@ -953,7 +953,7 @@ fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool {
953}953}
954954
955// Parameters `b` and `a` swapped to make this descending.955// Parameters `b` and `a` swapped to make this descending.
956fn addrCmpLessThan(b: LookupAddr, a: LookupAddr) bool {956fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
957 return a.sortkey < b.sortkey;957 return a.sortkey < b.sortkey;
958}958}
959959
lib/std/sort.zig+351-219
...@@ -5,7 +5,13 @@ const mem = std.mem;...@@ -5,7 +5,13 @@ const mem = std.mem;
5const math = std.math;5const math = std.math;
6const builtin = @import("builtin");6const builtin = @import("builtin");
77
8pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compareFn: fn (lhs: T, rhs: T) math.Order) ?usize {8pub fn binarySearch(
9 comptime T: type,
10 key: T,
11 items: []const T,
12 context: var,
13 comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order,
14) ?usize {
9 var left: usize = 0;15 var left: usize = 0;
10 var right: usize = items.len;16 var right: usize = items.len;
1117
...@@ -13,7 +19,7 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare...@@ -13,7 +19,7 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare
13 // Avoid overflowing in the midpoint calculation19 // Avoid overflowing in the midpoint calculation
14 const mid = left + (right - left) / 2;20 const mid = left + (right - left) / 2;
15 // Compare the key with the midpoint element21 // Compare the key with the midpoint element
16 switch (compareFn(key, items[mid])) {22 switch (compareFn(context, key, items[mid])) {
17 .eq => return mid,23 .eq => return mid,
18 .gt => left = mid + 1,24 .gt => left = mid + 1,
19 .lt => right = mid,25 .lt => right = mid,
...@@ -23,56 +29,61 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare...@@ -23,56 +29,61 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare
23 return null;29 return null;
24}30}
2531
26test "std.sort.binarySearch" {32test "binarySearch" {
27 const S = struct {33 const S = struct {
28 fn order_u32(lhs: u32, rhs: u32) math.Order {34 fn order_u32(context: void, lhs: u32, rhs: u32) math.Order {
29 return math.order(lhs, rhs);35 return math.order(lhs, rhs);
30 }36 }
31 fn order_i32(lhs: i32, rhs: i32) math.Order {37 fn order_i32(context: void, lhs: i32, rhs: i32) math.Order {
32 return math.order(lhs, rhs);38 return math.order(lhs, rhs);
33 }39 }
34 };40 };
35 testing.expectEqual(41 testing.expectEqual(
36 @as(?usize, null),42 @as(?usize, null),
37 binarySearch(u32, 1, &[_]u32{}, S.order_u32),43 binarySearch(u32, 1, &[_]u32{}, {}, S.order_u32),
38 );44 );
39 testing.expectEqual(45 testing.expectEqual(
40 @as(?usize, 0),46 @as(?usize, 0),
41 binarySearch(u32, 1, &[_]u32{1}, S.order_u32),47 binarySearch(u32, 1, &[_]u32{1}, {}, S.order_u32),
42 );48 );
43 testing.expectEqual(49 testing.expectEqual(
44 @as(?usize, null),50 @as(?usize, null),
45 binarySearch(u32, 1, &[_]u32{0}, S.order_u32),51 binarySearch(u32, 1, &[_]u32{0}, {}, S.order_u32),
46 );52 );
47 testing.expectEqual(53 testing.expectEqual(
48 @as(?usize, null),54 @as(?usize, null),
49 binarySearch(u32, 0, &[_]u32{1}, S.order_u32),55 binarySearch(u32, 0, &[_]u32{1}, {}, S.order_u32),
50 );56 );
51 testing.expectEqual(57 testing.expectEqual(
52 @as(?usize, 4),58 @as(?usize, 4),
53 binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, S.order_u32),59 binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32),
54 );60 );
55 testing.expectEqual(61 testing.expectEqual(
56 @as(?usize, 0),62 @as(?usize, 0),
57 binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, S.order_u32),63 binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32),
58 );64 );
59 testing.expectEqual(65 testing.expectEqual(
60 @as(?usize, 1),66 @as(?usize, 1),
61 binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, S.order_i32),67 binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32),
62 );68 );
63 testing.expectEqual(69 testing.expectEqual(
64 @as(?usize, 3),70 @as(?usize, 3),
65 binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, S.order_i32),71 binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32),
66 );72 );
67}73}
6874
69/// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required).75/// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required).
70pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void {76pub fn insertionSort(
77 comptime T: type,
78 items: []T,
79 context: var,
80 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
81) void {
71 var i: usize = 1;82 var i: usize = 1;
72 while (i < items.len) : (i += 1) {83 while (i < items.len) : (i += 1) {
73 const x = items[i];84 const x = items[i];
74 var j: usize = i;85 var j: usize = i;
75 while (j > 0 and lessThan(x, items[j - 1])) : (j -= 1) {86 while (j > 0 and lessThan(context, x, items[j - 1])) : (j -= 1) {
76 items[j] = items[j - 1];87 items[j] = items[j - 1];
77 }88 }
78 items[j] = x;89 items[j] = x;
...@@ -168,20 +179,25 @@ const Pull = struct {...@@ -168,20 +179,25 @@ const Pull = struct {
168179
169/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).180/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).
170/// Currently implemented as block sort.181/// Currently implemented as block sort.
171pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void {182pub fn sort(
183 comptime T: type,
184 items: []T,
185 context: var,
186 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
187) void {
172 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c188 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c
173 var cache: [512]T = undefined;189 var cache: [512]T = undefined;
174190
175 if (items.len < 4) {191 if (items.len < 4) {
176 if (items.len == 3) {192 if (items.len == 3) {
177 // hard coded insertion sort193 // hard coded insertion sort
178 if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);194 if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]);
179 if (lessThan(items[2], items[1])) {195 if (lessThan(context, items[2], items[1])) {
180 mem.swap(T, &items[1], &items[2]);196 mem.swap(T, &items[1], &items[2]);
181 if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);197 if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]);
182 }198 }
183 } else if (items.len == 2) {199 } else if (items.len == 2) {
184 if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);200 if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]);
185 }201 }
186 return;202 return;
187 }203 }
...@@ -197,75 +213,75 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -197,75 +213,75 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
197 const sliced_items = items[range.start..];213 const sliced_items = items[range.start..];
198 switch (range.length()) {214 switch (range.length()) {
199 8 => {215 8 => {
200 swap(T, sliced_items, lessThan, &order, 0, 1);216 swap(T, sliced_items, context, lessThan, &order, 0, 1);
201 swap(T, sliced_items, lessThan, &order, 2, 3);217 swap(T, sliced_items, context, lessThan, &order, 2, 3);
202 swap(T, sliced_items, lessThan, &order, 4, 5);218 swap(T, sliced_items, context, lessThan, &order, 4, 5);
203 swap(T, sliced_items, lessThan, &order, 6, 7);219 swap(T, sliced_items, context, lessThan, &order, 6, 7);
204 swap(T, sliced_items, lessThan, &order, 0, 2);220 swap(T, sliced_items, context, lessThan, &order, 0, 2);
205 swap(T, sliced_items, lessThan, &order, 1, 3);221 swap(T, sliced_items, context, lessThan, &order, 1, 3);
206 swap(T, sliced_items, lessThan, &order, 4, 6);222 swap(T, sliced_items, context, lessThan, &order, 4, 6);
207 swap(T, sliced_items, lessThan, &order, 5, 7);223 swap(T, sliced_items, context, lessThan, &order, 5, 7);
208 swap(T, sliced_items, lessThan, &order, 1, 2);224 swap(T, sliced_items, context, lessThan, &order, 1, 2);
209 swap(T, sliced_items, lessThan, &order, 5, 6);225 swap(T, sliced_items, context, lessThan, &order, 5, 6);
210 swap(T, sliced_items, lessThan, &order, 0, 4);226 swap(T, sliced_items, context, lessThan, &order, 0, 4);
211 swap(T, sliced_items, lessThan, &order, 3, 7);227 swap(T, sliced_items, context, lessThan, &order, 3, 7);
212 swap(T, sliced_items, lessThan, &order, 1, 5);228 swap(T, sliced_items, context, lessThan, &order, 1, 5);
213 swap(T, sliced_items, lessThan, &order, 2, 6);229 swap(T, sliced_items, context, lessThan, &order, 2, 6);
214 swap(T, sliced_items, lessThan, &order, 1, 4);230 swap(T, sliced_items, context, lessThan, &order, 1, 4);
215 swap(T, sliced_items, lessThan, &order, 3, 6);231 swap(T, sliced_items, context, lessThan, &order, 3, 6);
216 swap(T, sliced_items, lessThan, &order, 2, 4);232 swap(T, sliced_items, context, lessThan, &order, 2, 4);
217 swap(T, sliced_items, lessThan, &order, 3, 5);233 swap(T, sliced_items, context, lessThan, &order, 3, 5);
218 swap(T, sliced_items, lessThan, &order, 3, 4);234 swap(T, sliced_items, context, lessThan, &order, 3, 4);
219 },235 },
220 7 => {236 7 => {
221 swap(T, sliced_items, lessThan, &order, 1, 2);237 swap(T, sliced_items, context, lessThan, &order, 1, 2);
222 swap(T, sliced_items, lessThan, &order, 3, 4);238 swap(T, sliced_items, context, lessThan, &order, 3, 4);
223 swap(T, sliced_items, lessThan, &order, 5, 6);239 swap(T, sliced_items, context, lessThan, &order, 5, 6);
224 swap(T, sliced_items, lessThan, &order, 0, 2);240 swap(T, sliced_items, context, lessThan, &order, 0, 2);
225 swap(T, sliced_items, lessThan, &order, 3, 5);241 swap(T, sliced_items, context, lessThan, &order, 3, 5);
226 swap(T, sliced_items, lessThan, &order, 4, 6);242 swap(T, sliced_items, context, lessThan, &order, 4, 6);
227 swap(T, sliced_items, lessThan, &order, 0, 1);243 swap(T, sliced_items, context, lessThan, &order, 0, 1);
228 swap(T, sliced_items, lessThan, &order, 4, 5);244 swap(T, sliced_items, context, lessThan, &order, 4, 5);
229 swap(T, sliced_items, lessThan, &order, 2, 6);245 swap(T, sliced_items, context, lessThan, &order, 2, 6);
230 swap(T, sliced_items, lessThan, &order, 0, 4);246 swap(T, sliced_items, context, lessThan, &order, 0, 4);
231 swap(T, sliced_items, lessThan, &order, 1, 5);247 swap(T, sliced_items, context, lessThan, &order, 1, 5);
232 swap(T, sliced_items, lessThan, &order, 0, 3);248 swap(T, sliced_items, context, lessThan, &order, 0, 3);
233 swap(T, sliced_items, lessThan, &order, 2, 5);249 swap(T, sliced_items, context, lessThan, &order, 2, 5);
234 swap(T, sliced_items, lessThan, &order, 1, 3);250 swap(T, sliced_items, context, lessThan, &order, 1, 3);
235 swap(T, sliced_items, lessThan, &order, 2, 4);251 swap(T, sliced_items, context, lessThan, &order, 2, 4);
236 swap(T, sliced_items, lessThan, &order, 2, 3);252 swap(T, sliced_items, context, lessThan, &order, 2, 3);
237 },253 },
238 6 => {254 6 => {
239 swap(T, sliced_items, lessThan, &order, 1, 2);255 swap(T, sliced_items, context, lessThan, &order, 1, 2);
240 swap(T, sliced_items, lessThan, &order, 4, 5);256 swap(T, sliced_items, context, lessThan, &order, 4, 5);
241 swap(T, sliced_items, lessThan, &order, 0, 2);257 swap(T, sliced_items, context, lessThan, &order, 0, 2);
242 swap(T, sliced_items, lessThan, &order, 3, 5);258 swap(T, sliced_items, context, lessThan, &order, 3, 5);
243 swap(T, sliced_items, lessThan, &order, 0, 1);259 swap(T, sliced_items, context, lessThan, &order, 0, 1);
244 swap(T, sliced_items, lessThan, &order, 3, 4);260 swap(T, sliced_items, context, lessThan, &order, 3, 4);
245 swap(T, sliced_items, lessThan, &order, 2, 5);261 swap(T, sliced_items, context, lessThan, &order, 2, 5);
246 swap(T, sliced_items, lessThan, &order, 0, 3);262 swap(T, sliced_items, context, lessThan, &order, 0, 3);
247 swap(T, sliced_items, lessThan, &order, 1, 4);263 swap(T, sliced_items, context, lessThan, &order, 1, 4);
248 swap(T, sliced_items, lessThan, &order, 2, 4);264 swap(T, sliced_items, context, lessThan, &order, 2, 4);
249 swap(T, sliced_items, lessThan, &order, 1, 3);265 swap(T, sliced_items, context, lessThan, &order, 1, 3);
250 swap(T, sliced_items, lessThan, &order, 2, 3);266 swap(T, sliced_items, context, lessThan, &order, 2, 3);
251 },267 },
252 5 => {268 5 => {
253 swap(T, sliced_items, lessThan, &order, 0, 1);269 swap(T, sliced_items, context, lessThan, &order, 0, 1);
254 swap(T, sliced_items, lessThan, &order, 3, 4);270 swap(T, sliced_items, context, lessThan, &order, 3, 4);
255 swap(T, sliced_items, lessThan, &order, 2, 4);271 swap(T, sliced_items, context, lessThan, &order, 2, 4);
256 swap(T, sliced_items, lessThan, &order, 2, 3);272 swap(T, sliced_items, context, lessThan, &order, 2, 3);
257 swap(T, sliced_items, lessThan, &order, 1, 4);273 swap(T, sliced_items, context, lessThan, &order, 1, 4);
258 swap(T, sliced_items, lessThan, &order, 0, 3);274 swap(T, sliced_items, context, lessThan, &order, 0, 3);
259 swap(T, sliced_items, lessThan, &order, 0, 2);275 swap(T, sliced_items, context, lessThan, &order, 0, 2);
260 swap(T, sliced_items, lessThan, &order, 1, 3);276 swap(T, sliced_items, context, lessThan, &order, 1, 3);
261 swap(T, sliced_items, lessThan, &order, 1, 2);277 swap(T, sliced_items, context, lessThan, &order, 1, 2);
262 },278 },
263 4 => {279 4 => {
264 swap(T, sliced_items, lessThan, &order, 0, 1);280 swap(T, sliced_items, context, lessThan, &order, 0, 1);
265 swap(T, sliced_items, lessThan, &order, 2, 3);281 swap(T, sliced_items, context, lessThan, &order, 2, 3);
266 swap(T, sliced_items, lessThan, &order, 0, 2);282 swap(T, sliced_items, context, lessThan, &order, 0, 2);
267 swap(T, sliced_items, lessThan, &order, 1, 3);283 swap(T, sliced_items, context, lessThan, &order, 1, 3);
268 swap(T, sliced_items, lessThan, &order, 1, 2);284 swap(T, sliced_items, context, lessThan, &order, 1, 2);
269 },285 },
270 else => {},286 else => {},
271 }287 }
...@@ -288,16 +304,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -288,16 +304,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
288 var A2 = iterator.nextRange();304 var A2 = iterator.nextRange();
289 var B2 = iterator.nextRange();305 var B2 = iterator.nextRange();
290306
291 if (lessThan(items[B1.end - 1], items[A1.start])) {307 if (lessThan(context, items[B1.end - 1], items[A1.start])) {
292 // the two ranges are in reverse order, so copy them in reverse order into the cache308 // the two ranges are in reverse order, so copy them in reverse order into the cache
293 mem.copy(T, cache[B1.length()..], items[A1.start..A1.end]);309 mem.copy(T, cache[B1.length()..], items[A1.start..A1.end]);
294 mem.copy(T, cache[0..], items[B1.start..B1.end]);310 mem.copy(T, cache[0..], items[B1.start..B1.end]);
295 } else if (lessThan(items[B1.start], items[A1.end - 1])) {311 } else if (lessThan(context, items[B1.start], items[A1.end - 1])) {
296 // these two ranges weren't already in order, so merge them into the cache312 // these two ranges weren't already in order, so merge them into the cache
297 mergeInto(T, items, A1, B1, lessThan, cache[0..]);313 mergeInto(T, items, A1, B1, context, lessThan, cache[0..]);
298 } else {314 } else {
299 // if A1, B1, A2, and B2 are all in order, skip doing anything else315 // if A1, B1, A2, and B2 are all in order, skip doing anything else
300 if (!lessThan(items[B2.start], items[A2.end - 1]) and !lessThan(items[A2.start], items[B1.end - 1])) continue;316 if (!lessThan(context, items[B2.start], items[A2.end - 1]) and !lessThan(context, items[A2.start], items[B1.end - 1])) continue;
301317
302 // copy A1 and B1 into the cache in the same order318 // copy A1 and B1 into the cache in the same order
303 mem.copy(T, cache[0..], items[A1.start..A1.end]);319 mem.copy(T, cache[0..], items[A1.start..A1.end]);
...@@ -306,13 +322,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -306,13 +322,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
306 A1 = Range.init(A1.start, B1.end);322 A1 = Range.init(A1.start, B1.end);
307323
308 // merge A2 and B2 into the cache324 // merge A2 and B2 into the cache
309 if (lessThan(items[B2.end - 1], items[A2.start])) {325 if (lessThan(context, items[B2.end - 1], items[A2.start])) {
310 // the two ranges are in reverse order, so copy them in reverse order into the cache326 // the two ranges are in reverse order, so copy them in reverse order into the cache
311 mem.copy(T, cache[A1.length() + B2.length() ..], items[A2.start..A2.end]);327 mem.copy(T, cache[A1.length() + B2.length() ..], items[A2.start..A2.end]);
312 mem.copy(T, cache[A1.length()..], items[B2.start..B2.end]);328 mem.copy(T, cache[A1.length()..], items[B2.start..B2.end]);
313 } else if (lessThan(items[B2.start], items[A2.end - 1])) {329 } else if (lessThan(context, items[B2.start], items[A2.end - 1])) {
314 // these two ranges weren't already in order, so merge them into the cache330 // these two ranges weren't already in order, so merge them into the cache
315 mergeInto(T, items, A2, B2, lessThan, cache[A1.length()..]);331 mergeInto(T, items, A2, B2, context, lessThan, cache[A1.length()..]);
316 } else {332 } else {
317 // copy A2 and B2 into the cache in the same order333 // copy A2 and B2 into the cache in the same order
318 mem.copy(T, cache[A1.length()..], items[A2.start..A2.end]);334 mem.copy(T, cache[A1.length()..], items[A2.start..A2.end]);
...@@ -324,13 +340,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -324,13 +340,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
324 const A3 = Range.init(0, A1.length());340 const A3 = Range.init(0, A1.length());
325 const B3 = Range.init(A1.length(), A1.length() + A2.length());341 const B3 = Range.init(A1.length(), A1.length() + A2.length());
326342
327 if (lessThan(cache[B3.end - 1], cache[A3.start])) {343 if (lessThan(context, cache[B3.end - 1], cache[A3.start])) {
328 // the two ranges are in reverse order, so copy them in reverse order into the items344 // the two ranges are in reverse order, so copy them in reverse order into the items
329 mem.copy(T, items[A1.start + A2.length() ..], cache[A3.start..A3.end]);345 mem.copy(T, items[A1.start + A2.length() ..], cache[A3.start..A3.end]);
330 mem.copy(T, items[A1.start..], cache[B3.start..B3.end]);346 mem.copy(T, items[A1.start..], cache[B3.start..B3.end]);
331 } else if (lessThan(cache[B3.start], cache[A3.end - 1])) {347 } else if (lessThan(context, cache[B3.start], cache[A3.end - 1])) {
332 // these two ranges weren't already in order, so merge them back into the items348 // these two ranges weren't already in order, so merge them back into the items
333 mergeInto(T, cache[0..], A3, B3, lessThan, items[A1.start..]);349 mergeInto(T, cache[0..], A3, B3, context, lessThan, items[A1.start..]);
334 } else {350 } else {
335 // copy A3 and B3 into the items in the same order351 // copy A3 and B3 into the items in the same order
336 mem.copy(T, items[A1.start..], cache[A3.start..A3.end]);352 mem.copy(T, items[A1.start..], cache[A3.start..A3.end]);
...@@ -347,13 +363,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -347,13 +363,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
347 var A = iterator.nextRange();363 var A = iterator.nextRange();
348 var B = iterator.nextRange();364 var B = iterator.nextRange();
349365
350 if (lessThan(items[B.end - 1], items[A.start])) {366 if (lessThan(context, items[B.end - 1], items[A.start])) {
351 // the two ranges are in reverse order, so a simple rotation should fix it367 // the two ranges are in reverse order, so a simple rotation should fix it
352 mem.rotate(T, items[A.start..B.end], A.length());368 mem.rotate(T, items[A.start..B.end], A.length());
353 } else if (lessThan(items[B.start], items[A.end - 1])) {369 } else if (lessThan(context, items[B.start], items[A.end - 1])) {
354 // these two ranges weren't already in order, so we'll need to merge them!370 // these two ranges weren't already in order, so we'll need to merge them!
355 mem.copy(T, cache[0..], items[A.start..A.end]);371 mem.copy(T, cache[0..], items[A.start..A.end]);
356 mergeExternal(T, items, A, B, lessThan, cache[0..]);372 mergeExternal(T, items, A, B, context, lessThan, cache[0..]);
357 }373 }
358 }374 }
359 }375 }
...@@ -435,7 +451,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -435,7 +451,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
435 last = index;451 last = index;
436 count += 1;452 count += 1;
437 }) {453 }) {
438 index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), lessThan, find - count);454 index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), context, lessThan, find - count);
439 if (index == A.end) break;455 if (index == A.end) break;
440 }456 }
441 index = last;457 index = last;
...@@ -493,7 +509,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -493,7 +509,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
493 last = index - 1;509 last = index - 1;
494 count += 1;510 count += 1;
495 }) {511 }) {
496 index = findFirstBackward(T, items, items[last], Range.init(B.start, last), lessThan, find - count);512 index = findFirstBackward(T, items, items[last], Range.init(B.start, last), context, lessThan, find - count);
497 if (index == B.start) break;513 if (index == B.start) break;
498 }514 }
499 index = last;515 index = last;
...@@ -558,7 +574,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -558,7 +574,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
558 index = pull[pull_index].from;574 index = pull[pull_index].from;
559 count = 1;575 count = 1;
560 while (count < length) : (count += 1) {576 while (count < length) : (count += 1) {
561 index = findFirstBackward(T, items, items[index - 1], Range.init(pull[pull_index].to, pull[pull_index].from - (count - 1)), lessThan, length - count);577 index = findFirstBackward(T, items, items[index - 1], Range.init(pull[pull_index].to, pull[pull_index].from - (count - 1)), context, lessThan, length - count);
562 const range = Range.init(index + 1, pull[pull_index].from + 1);578 const range = Range.init(index + 1, pull[pull_index].from + 1);
563 mem.rotate(T, items[range.start..range.end], range.length() - count);579 mem.rotate(T, items[range.start..range.end], range.length() - count);
564 pull[pull_index].from = index + count;580 pull[pull_index].from = index + count;
...@@ -568,7 +584,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -568,7 +584,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
568 index = pull[pull_index].from + 1;584 index = pull[pull_index].from + 1;
569 count = 1;585 count = 1;
570 while (count < length) : (count += 1) {586 while (count < length) : (count += 1) {
571 index = findLastForward(T, items, items[index], Range.init(index, pull[pull_index].to), lessThan, length - count);587 index = findLastForward(T, items, items[index], Range.init(index, pull[pull_index].to), context, lessThan, length - count);
572 const range = Range.init(pull[pull_index].from, index - 1);588 const range = Range.init(pull[pull_index].from, index - 1);
573 mem.rotate(T, items[range.start..range.end], count);589 mem.rotate(T, items[range.start..range.end], count);
574 pull[pull_index].from = index - 1 - count;590 pull[pull_index].from = index - 1 - count;
...@@ -615,10 +631,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -615,10 +631,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
615 }631 }
616 }632 }
617633
618 if (lessThan(items[B.end - 1], items[A.start])) {634 if (lessThan(context, items[B.end - 1], items[A.start])) {
619 // the two ranges are in reverse order, so a simple rotation should fix it635 // the two ranges are in reverse order, so a simple rotation should fix it
620 mem.rotate(T, items[A.start..B.end], A.length());636 mem.rotate(T, items[A.start..B.end], A.length());
621 } else if (lessThan(items[A.end], items[A.end - 1])) {637 } else if (lessThan(context, items[A.end], items[A.end - 1])) {
622 // these two ranges weren't already in order, so we'll need to merge them!638 // these two ranges weren't already in order, so we'll need to merge them!
623 var findA: usize = undefined;639 var findA: usize = undefined;
624640
...@@ -656,16 +672,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -656,16 +672,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
656 while (true) {672 while (true) {
657 // if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block,673 // if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block,
658 // then drop that minimum A block behind. or if there are no B blocks left then keep dropping the remaining A blocks.674 // then drop that minimum A block behind. or if there are no B blocks left then keep dropping the remaining A blocks.
659 if ((lastB.length() > 0 and !lessThan(items[lastB.end - 1], items[indexA])) or blockB.length() == 0) {675 if ((lastB.length() > 0 and !lessThan(context, items[lastB.end - 1], items[indexA])) or blockB.length() == 0) {
660 // figure out where to split the previous B block, and rotate it at the split676 // figure out where to split the previous B block, and rotate it at the split
661 const B_split = binaryFirst(T, items, items[indexA], lastB, lessThan);677 const B_split = binaryFirst(T, items, items[indexA], lastB, context, lessThan);
662 const B_remaining = lastB.end - B_split;678 const B_remaining = lastB.end - B_split;
663679
664 // swap the minimum A block to the beginning of the rolling A blocks680 // swap the minimum A block to the beginning of the rolling A blocks
665 var minA = blockA.start;681 var minA = blockA.start;
666 findA = minA + block_size;682 findA = minA + block_size;
667 while (findA < blockA.end) : (findA += block_size) {683 while (findA < blockA.end) : (findA += block_size) {
668 if (lessThan(items[findA], items[minA])) {684 if (lessThan(context, items[findA], items[minA])) {
669 minA = findA;685 minA = findA;
670 }686 }
671 }687 }
...@@ -681,11 +697,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -681,11 +697,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
681 // or failing that we'll use a strictly in-place merge algorithm (MergeInPlace)697 // or failing that we'll use a strictly in-place merge algorithm (MergeInPlace)
682698
683 if (lastA.length() <= cache.len) {699 if (lastA.length() <= cache.len) {
684 mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, cache[0..]);700 mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan, cache[0..]);
685 } else if (buffer2.length() > 0) {701 } else if (buffer2.length() > 0) {
686 mergeInternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, buffer2);702 mergeInternal(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan, buffer2);
687 } else {703 } else {
688 mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), lessThan);704 mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan);
689 }705 }
690706
691 if (buffer2.length() > 0 or block_size <= cache.len) {707 if (buffer2.length() > 0 or block_size <= cache.len) {
...@@ -741,11 +757,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -741,11 +757,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
741757
742 // merge the last A block with the remaining B values758 // merge the last A block with the remaining B values
743 if (lastA.length() <= cache.len) {759 if (lastA.length() <= cache.len) {
744 mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, cache[0..]);760 mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan, cache[0..]);
745 } else if (buffer2.length() > 0) {761 } else if (buffer2.length() > 0) {
746 mergeInternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, buffer2);762 mergeInternal(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan, buffer2);
747 } else {763 } else {
748 mergeInPlace(T, items, lastA, Range.init(lastA.end, B.end), lessThan);764 mergeInPlace(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan);
749 }765 }
750 }766 }
751 }767 }
...@@ -755,7 +771,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -755,7 +771,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
755771
756 // while an unstable sort like quicksort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort,772 // while an unstable sort like quicksort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort,
757 // even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here773 // even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here
758 insertionSort(T, items[buffer2.start..buffer2.end], lessThan);774 insertionSort(T, items[buffer2.start..buffer2.end], context, lessThan);
759775
760 pull_index = 0;776 pull_index = 0;
761 while (pull_index < 2) : (pull_index += 1) {777 while (pull_index < 2) : (pull_index += 1) {
...@@ -764,7 +780,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -764,7 +780,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
764 // the values were pulled out to the left, so redistribute them back to the right780 // the values were pulled out to the left, so redistribute them back to the right
765 var buffer = Range.init(pull[pull_index].range.start, pull[pull_index].range.start + pull[pull_index].count);781 var buffer = Range.init(pull[pull_index].range.start, pull[pull_index].range.start + pull[pull_index].count);
766 while (buffer.length() > 0) {782 while (buffer.length() > 0) {
767 index = findFirstForward(T, items, items[buffer.start], Range.init(buffer.end, pull[pull_index].range.end), lessThan, unique);783 index = findFirstForward(T, items, items[buffer.start], Range.init(buffer.end, pull[pull_index].range.end), context, lessThan, unique);
768 const amount = index - buffer.end;784 const amount = index - buffer.end;
769 mem.rotate(T, items[buffer.start..index], buffer.length());785 mem.rotate(T, items[buffer.start..index], buffer.length());
770 buffer.start += (amount + 1);786 buffer.start += (amount + 1);
...@@ -775,7 +791,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -775,7 +791,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
775 // the values were pulled out to the right, so redistribute them back to the left791 // the values were pulled out to the right, so redistribute them back to the left
776 var buffer = Range.init(pull[pull_index].range.end - pull[pull_index].count, pull[pull_index].range.end);792 var buffer = Range.init(pull[pull_index].range.end - pull[pull_index].count, pull[pull_index].range.end);
777 while (buffer.length() > 0) {793 while (buffer.length() > 0) {
778 index = findLastBackward(T, items, items[buffer.end - 1], Range.init(pull[pull_index].range.start, buffer.start), lessThan, unique);794 index = findLastBackward(T, items, items[buffer.end - 1], Range.init(pull[pull_index].range.start, buffer.start), context, lessThan, unique);
779 const amount = buffer.start - index;795 const amount = buffer.start - index;
780 mem.rotate(T, items[index..buffer.end], amount);796 mem.rotate(T, items[index..buffer.end], amount);
781 buffer.start -= amount;797 buffer.start -= amount;
...@@ -792,7 +808,14 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo...@@ -792,7 +808,14 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
792}808}
793809
794// merge operation without a buffer810// merge operation without a buffer
795fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessThan: fn (T, T) bool) void {811fn mergeInPlace(
812 comptime T: type,
813 items: []T,
814 A_arg: Range,
815 B_arg: Range,
816 context: var,
817 comptime lessThan: fn (@TypeOf(context), T, T) bool,
818) void {
796 if (A_arg.length() == 0 or B_arg.length() == 0) return;819 if (A_arg.length() == 0 or B_arg.length() == 0) return;
797820
798 // this just repeatedly binary searches into B and rotates A into position.821 // this just repeatedly binary searches into B and rotates A into position.
...@@ -818,7 +841,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh...@@ -818,7 +841,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh
818841
819 while (true) {842 while (true) {
820 // find the first place in B where the first item in A needs to be inserted843 // find the first place in B where the first item in A needs to be inserted
821 const mid = binaryFirst(T, items, items[A.start], B, lessThan);844 const mid = binaryFirst(T, items, items[A.start], B, context, lessThan);
822845
823 // rotate A into place846 // rotate A into place
824 const amount = mid - A.end;847 const amount = mid - A.end;
...@@ -828,13 +851,21 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh...@@ -828,13 +851,21 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh
828 // calculate the new A and B ranges851 // calculate the new A and B ranges
829 B.start = mid;852 B.start = mid;
830 A = Range.init(A.start + amount, B.start);853 A = Range.init(A.start + amount, B.start);
831 A.start = binaryLast(T, items, items[A.start], A, lessThan);854 A.start = binaryLast(T, items, items[A.start], A, context, lessThan);
832 if (A.length() == 0) break;855 if (A.length() == 0) break;
833 }856 }
834}857}
835858
836// merge operation using an internal buffer859// merge operation using an internal buffer
837fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, buffer: Range) void {860fn mergeInternal(
861 comptime T: type,
862 items: []T,
863 A: Range,
864 B: Range,
865 context: var,
866 comptime lessThan: fn (@TypeOf(context), T, T) bool,
867 buffer: Range,
868) void {
838 // whenever we find a value to add to the final array, swap it with the value that's already in that spot869 // whenever we find a value to add to the final array, swap it with the value that's already in that spot
839 // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order870 // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order
840 var A_count: usize = 0;871 var A_count: usize = 0;
...@@ -843,7 +874,7 @@ fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn...@@ -843,7 +874,7 @@ fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn
843874
844 if (B.length() > 0 and A.length() > 0) {875 if (B.length() > 0 and A.length() > 0) {
845 while (true) {876 while (true) {
846 if (!lessThan(items[B.start + B_count], items[buffer.start + A_count])) {877 if (!lessThan(context, items[B.start + B_count], items[buffer.start + A_count])) {
847 mem.swap(T, &items[A.start + insert], &items[buffer.start + A_count]);878 mem.swap(T, &items[A.start + insert], &items[buffer.start + A_count]);
848 A_count += 1;879 A_count += 1;
849 insert += 1;880 insert += 1;
...@@ -870,63 +901,102 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s...@@ -870,63 +901,102 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s
870901
871// combine a linear search with a binary search to reduce the number of comparisons in situations902// combine a linear search with a binary search to reduce the number of comparisons in situations
872// where have some idea as to how many unique values there are and where the next value might be903// where have some idea as to how many unique values there are and where the next value might be
873fn findFirstForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {904fn findFirstForward(
905 comptime T: type,
906 items: []T,
907 value: T,
908 range: Range,
909 context: var,
910 comptime lessThan: fn (@TypeOf(context), T, T) bool,
911 unique: usize,
912) usize {
874 if (range.length() == 0) return range.start;913 if (range.length() == 0) return range.start;
875 const skip = math.max(range.length() / unique, @as(usize, 1));914 const skip = math.max(range.length() / unique, @as(usize, 1));
876915
877 var index = range.start + skip;916 var index = range.start + skip;
878 while (lessThan(items[index - 1], value)) : (index += skip) {917 while (lessThan(context, items[index - 1], value)) : (index += skip) {
879 if (index >= range.end - skip) {918 if (index >= range.end - skip) {
880 return binaryFirst(T, items, value, Range.init(index, range.end), lessThan);919 return binaryFirst(T, items, value, Range.init(index, range.end), context, lessThan);
881 }920 }
882 }921 }
883922
884 return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);923 return binaryFirst(T, items, value, Range.init(index - skip, index), context, lessThan);
885}924}
886925
887fn findFirstBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {926fn findFirstBackward(
927 comptime T: type,
928 items: []T,
929 value: T,
930 range: Range,
931 context: var,
932 comptime lessThan: fn (@TypeOf(context), T, T) bool,
933 unique: usize,
934) usize {
888 if (range.length() == 0) return range.start;935 if (range.length() == 0) return range.start;
889 const skip = math.max(range.length() / unique, @as(usize, 1));936 const skip = math.max(range.length() / unique, @as(usize, 1));
890937
891 var index = range.end - skip;938 var index = range.end - skip;
892 while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) {939 while (index > range.start and !lessThan(context, items[index - 1], value)) : (index -= skip) {
893 if (index < range.start + skip) {940 if (index < range.start + skip) {
894 return binaryFirst(T, items, value, Range.init(range.start, index), lessThan);941 return binaryFirst(T, items, value, Range.init(range.start, index), context, lessThan);
895 }942 }
896 }943 }
897944
898 return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);945 return binaryFirst(T, items, value, Range.init(index, index + skip), context, lessThan);
899}946}
900947
901fn findLastForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {948fn findLastForward(
949 comptime T: type,
950 items: []T,
951 value: T,
952 range: Range,
953 context: var,
954 comptime lessThan: fn (@TypeOf(context), T, T) bool,
955 unique: usize,
956) usize {
902 if (range.length() == 0) return range.start;957 if (range.length() == 0) return range.start;
903 const skip = math.max(range.length() / unique, @as(usize, 1));958 const skip = math.max(range.length() / unique, @as(usize, 1));
904959
905 var index = range.start + skip;960 var index = range.start + skip;
906 while (!lessThan(value, items[index - 1])) : (index += skip) {961 while (!lessThan(context, value, items[index - 1])) : (index += skip) {
907 if (index >= range.end - skip) {962 if (index >= range.end - skip) {
908 return binaryLast(T, items, value, Range.init(index, range.end), lessThan);963 return binaryLast(T, items, value, Range.init(index, range.end), context, lessThan);
909 }964 }
910 }965 }
911966
912 return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);967 return binaryLast(T, items, value, Range.init(index - skip, index), context, lessThan);
913}968}
914969
915fn findLastBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {970fn findLastBackward(
971 comptime T: type,
972 items: []T,
973 value: T,
974 range: Range,
975 context: var,
976 comptime lessThan: fn (@TypeOf(context), T, T) bool,
977 unique: usize,
978) usize {
916 if (range.length() == 0) return range.start;979 if (range.length() == 0) return range.start;
917 const skip = math.max(range.length() / unique, @as(usize, 1));980 const skip = math.max(range.length() / unique, @as(usize, 1));
918981
919 var index = range.end - skip;982 var index = range.end - skip;
920 while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) {983 while (index > range.start and lessThan(context, value, items[index - 1])) : (index -= skip) {
921 if (index < range.start + skip) {984 if (index < range.start + skip) {
922 return binaryLast(T, items, value, Range.init(range.start, index), lessThan);985 return binaryLast(T, items, value, Range.init(range.start, index), context, lessThan);
923 }986 }
924 }987 }
925988
926 return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);989 return binaryLast(T, items, value, Range.init(index, index + skip), context, lessThan);
927}990}
928991
929fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize {992fn binaryFirst(
993 comptime T: type,
994 items: []T,
995 value: T,
996 range: Range,
997 context: var,
998 comptime lessThan: fn (@TypeOf(context), T, T) bool,
999) usize {
930 var curr = range.start;1000 var curr = range.start;
931 var size = range.length();1001 var size = range.length();
932 if (range.start >= range.end) return range.end;1002 if (range.start >= range.end) return range.end;
...@@ -935,14 +1005,21 @@ fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: f...@@ -935,14 +1005,21 @@ fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: f
9351005
936 size /= 2;1006 size /= 2;
937 const mid = items[curr + size];1007 const mid = items[curr + size];
938 if (lessThan(mid, value)) {1008 if (lessThan(context, mid, value)) {
939 curr += size + offset;1009 curr += size + offset;
940 }1010 }
941 }1011 }
942 return curr;1012 return curr;
943}1013}
9441014
945fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize {1015fn binaryLast(
1016 comptime T: type,
1017 items: []T,
1018 value: T,
1019 range: Range,
1020 context: var,
1021 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1022) usize {
946 var curr = range.start;1023 var curr = range.start;
947 var size = range.length();1024 var size = range.length();
948 if (range.start >= range.end) return range.end;1025 if (range.start >= range.end) return range.end;
...@@ -951,14 +1028,22 @@ fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn...@@ -951,14 +1028,22 @@ fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn
9511028
952 size /= 2;1029 size /= 2;
953 const mid = items[curr + size];1030 const mid = items[curr + size];
954 if (!lessThan(value, mid)) {1031 if (!lessThan(context, value, mid)) {
955 curr += size + offset;1032 curr += size + offset;
956 }1033 }
957 }1034 }
958 return curr;1035 return curr;
959}1036}
9601037
961fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T) bool, into: []T) void {1038fn mergeInto(
1039 comptime T: type,
1040 from: []T,
1041 A: Range,
1042 B: Range,
1043 context: var,
1044 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1045 into: []T,
1046) void {
962 var A_index: usize = A.start;1047 var A_index: usize = A.start;
963 var B_index: usize = B.start;1048 var B_index: usize = B.start;
964 const A_last = A.end;1049 const A_last = A.end;
...@@ -966,7 +1051,7 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T...@@ -966,7 +1051,7 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T
966 var insert_index: usize = 0;1051 var insert_index: usize = 0;
9671052
968 while (true) {1053 while (true) {
969 if (!lessThan(from[B_index], from[A_index])) {1054 if (!lessThan(context, from[B_index], from[A_index])) {
970 into[insert_index] = from[A_index];1055 into[insert_index] = from[A_index];
971 A_index += 1;1056 A_index += 1;
972 insert_index += 1;1057 insert_index += 1;
...@@ -988,7 +1073,15 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T...@@ -988,7 +1073,15 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T
988 }1073 }
989}1074}
9901075
991fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, cache: []T) void {1076fn mergeExternal(
1077 comptime T: type,
1078 items: []T,
1079 A: Range,
1080 B: Range,
1081 context: var,
1082 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1083 cache: []T,
1084) void {
992 // A fits into the cache, so use that instead of the internal buffer1085 // A fits into the cache, so use that instead of the internal buffer
993 var A_index: usize = 0;1086 var A_index: usize = 0;
994 var B_index: usize = B.start;1087 var B_index: usize = B.start;
...@@ -998,7 +1091,7 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn...@@ -998,7 +1091,7 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn
9981091
999 if (B.length() > 0 and A.length() > 0) {1092 if (B.length() > 0 and A.length() > 0) {
1000 while (true) {1093 while (true) {
1001 if (!lessThan(items[B_index], cache[A_index])) {1094 if (!lessThan(context, items[B_index], cache[A_index])) {
1002 items[insert_index] = cache[A_index];1095 items[insert_index] = cache[A_index];
1003 A_index += 1;1096 A_index += 1;
1004 insert_index += 1;1097 insert_index += 1;
...@@ -1016,17 +1109,25 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn...@@ -1016,17 +1109,25 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn
1016 mem.copy(T, items[insert_index..], cache[A_index..A_last]);1109 mem.copy(T, items[insert_index..], cache[A_index..A_last]);
1017}1110}
10181111
1019fn swap(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool, order: *[8]u8, x: usize, y: usize) void {1112fn swap(
1020 if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) {1113 comptime T: type,
1114 items: []T,
1115 context: var,
1116 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
1117 order: *[8]u8,
1118 x: usize,
1119 y: usize,
1120) void {
1121 if (lessThan(context, items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(context, items[x], items[y]))) {
1021 mem.swap(T, &items[x], &items[y]);1122 mem.swap(T, &items[x], &items[y]);
1022 mem.swap(u8, &(order.*)[x], &(order.*)[y]);1123 mem.swap(u8, &(order.*)[x], &(order.*)[y]);
1023 }1124 }
1024}1125}
10251126
1026// Use these to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.1127/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
1027pub fn asc(comptime T: type) fn (T, T) bool {1128pub fn asc(comptime T: type) fn (void, T, T) bool {
1028 const impl = struct {1129 const impl = struct {
1029 fn inner(a: T, b: T) bool {1130 fn inner(context: void, a: T, b: T) bool {
1030 return a < b;1131 return a < b;
1031 }1132 }
1032 };1133 };
...@@ -1034,9 +1135,10 @@ pub fn asc(comptime T: type) fn (T, T) bool {...@@ -1034,9 +1135,10 @@ pub fn asc(comptime T: type) fn (T, T) bool {
1034 return impl.inner;1135 return impl.inner;
1035}1136}
10361137
1037pub fn desc(comptime T: type) fn (T, T) bool {1138/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
1139pub fn desc(comptime T: type) fn (void, T, T) bool {
1038 const impl = struct {1140 const impl = struct {
1039 fn inner(a: T, b: T) bool {1141 fn inner(context: void, a: T, b: T) bool {
1040 return a > b;1142 return a > b;
1041 }1143 }
1042 };1144 };
...@@ -1085,7 +1187,7 @@ fn testStableSort() void {...@@ -1085,7 +1187,7 @@ fn testStableSort() void {
1085 },1187 },
1086 };1188 };
1087 for (cases) |*case| {1189 for (cases) |*case| {
1088 insertionSort(IdAndValue, (case.*)[0..], cmpByValue);1190 insertionSort(IdAndValue, (case.*)[0..], {}, cmpByValue);
1089 for (case.*) |item, i| {1191 for (case.*) |item, i| {
1090 testing.expect(item.id == expected[i].id);1192 testing.expect(item.id == expected[i].id);
1091 testing.expect(item.value == expected[i].value);1193 testing.expect(item.value == expected[i].value);
...@@ -1096,11 +1198,16 @@ const IdAndValue = struct {...@@ -1096,11 +1198,16 @@ const IdAndValue = struct {
1096 id: usize,1198 id: usize,
1097 value: i32,1199 value: i32,
1098};1200};
1099fn cmpByValue(a: IdAndValue, b: IdAndValue) bool {1201fn cmpByValue(context: void, a: IdAndValue, b: IdAndValue) bool {
1100 return asc(i32)(a.value, b.value);1202 return asc_i32(context, a.value, b.value);
1101}1203}
11021204
1103test "std.sort" {1205const asc_u8 = asc(u8);
1206const asc_i32 = asc(i32);
1207const desc_u8 = desc(u8);
1208const desc_i32 = desc(i32);
1209
1210test "sort" {
1104 const u8cases = [_][]const []const u8{1211 const u8cases = [_][]const []const u8{
1105 &[_][]const u8{1212 &[_][]const u8{
1106 "",1213 "",
...@@ -1132,7 +1239,7 @@ test "std.sort" {...@@ -1132,7 +1239,7 @@ test "std.sort" {
1132 var buf: [8]u8 = undefined;1239 var buf: [8]u8 = undefined;
1133 const slice = buf[0..case[0].len];1240 const slice = buf[0..case[0].len];
1134 mem.copy(u8, slice, case[0]);1241 mem.copy(u8, slice, case[0]);
1135 sort(u8, slice, asc(u8));1242 sort(u8, slice, {}, asc_u8);
1136 testing.expect(mem.eql(u8, slice, case[1]));1243 testing.expect(mem.eql(u8, slice, case[1]));
1137 }1244 }
11381245
...@@ -1167,12 +1274,12 @@ test "std.sort" {...@@ -1167,12 +1274,12 @@ test "std.sort" {
1167 var buf: [8]i32 = undefined;1274 var buf: [8]i32 = undefined;
1168 const slice = buf[0..case[0].len];1275 const slice = buf[0..case[0].len];
1169 mem.copy(i32, slice, case[0]);1276 mem.copy(i32, slice, case[0]);
1170 sort(i32, slice, asc(i32));1277 sort(i32, slice, {}, asc_i32);
1171 testing.expect(mem.eql(i32, slice, case[1]));1278 testing.expect(mem.eql(i32, slice, case[1]));
1172 }1279 }
1173}1280}
11741281
1175test "std.sort descending" {1282test "sort descending" {
1176 const rev_cases = [_][]const []const i32{1283 const rev_cases = [_][]const []const i32{
1177 &[_][]const i32{1284 &[_][]const i32{
1178 &[_]i32{},1285 &[_]i32{},
...@@ -1204,14 +1311,14 @@ test "std.sort descending" {...@@ -1204,14 +1311,14 @@ test "std.sort descending" {
1204 var buf: [8]i32 = undefined;1311 var buf: [8]i32 = undefined;
1205 const slice = buf[0..case[0].len];1312 const slice = buf[0..case[0].len];
1206 mem.copy(i32, slice, case[0]);1313 mem.copy(i32, slice, case[0]);
1207 sort(i32, slice, desc(i32));1314 sort(i32, slice, {}, desc_i32);
1208 testing.expect(mem.eql(i32, slice, case[1]));1315 testing.expect(mem.eql(i32, slice, case[1]));
1209 }1316 }
1210}1317}
12111318
1212test "another sort case" {1319test "another sort case" {
1213 var arr = [_]i32{ 5, 3, 1, 2, 4 };1320 var arr = [_]i32{ 5, 3, 1, 2, 4 };
1214 sort(i32, arr[0..], asc(i32));1321 sort(i32, arr[0..], {}, asc_i32);
12151322
1216 testing.expect(mem.eql(i32, &arr, &[_]i32{ 1, 2, 3, 4, 5 }));1323 testing.expect(mem.eql(i32, &arr, &[_]i32{ 1, 2, 3, 4, 5 }));
1217}1324}
...@@ -1236,7 +1343,7 @@ fn fuzzTest(rng: *std.rand.Random) !void {...@@ -1236,7 +1343,7 @@ fn fuzzTest(rng: *std.rand.Random) !void {
1236 item.id = index;1343 item.id = index;
1237 item.value = rng.intRangeLessThan(i32, 0, 100);1344 item.value = rng.intRangeLessThan(i32, 0, 100);
1238 }1345 }
1239 sort(IdAndValue, array, cmpByValue);1346 sort(IdAndValue, array, {}, cmpByValue);
12401347
1241 var index: usize = 1;1348 var index: usize = 1;
1242 while (index < array.len) : (index += 1) {1349 while (index < array.len) : (index += 1) {
...@@ -1248,7 +1355,12 @@ fn fuzzTest(rng: *std.rand.Random) !void {...@@ -1248,7 +1355,12 @@ fn fuzzTest(rng: *std.rand.Random) !void {
1248 }1355 }
1249}1356}
12501357
1251pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize {1358pub fn argMin(
1359 comptime T: type,
1360 items: []const T,
1361 context: var,
1362 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
1363) ?usize {
1252 if (items.len == 0) {1364 if (items.len == 0) {
1253 return null;1365 return null;
1254 }1366 }
...@@ -1256,7 +1368,7 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)...@@ -1256,7 +1368,7 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
1256 var smallest = items[0];1368 var smallest = items[0];
1257 var smallest_index: usize = 0;1369 var smallest_index: usize = 0;
1258 for (items[1..]) |item, i| {1370 for (items[1..]) |item, i| {
1259 if (lessThan(item, smallest)) {1371 if (lessThan(context, item, smallest)) {
1260 smallest = item;1372 smallest = item;
1261 smallest_index = i + 1;1373 smallest_index = i + 1;
1262 }1374 }
...@@ -1265,32 +1377,42 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)...@@ -1265,32 +1377,42 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
1265 return smallest_index;1377 return smallest_index;
1266}1378}
12671379
1268test "std.sort.argMin" {1380test "argMin" {
1269 testing.expectEqual(@as(?usize, null), argMin(i32, &[_]i32{}, asc(i32)));1381 testing.expectEqual(@as(?usize, null), argMin(i32, &[_]i32{}, {}, asc_i32));
1270 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{1}, asc(i32)));1382 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{1}, {}, asc_i32));
1271 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));1383 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1272 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));1384 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1273 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));1385 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1274 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));1386 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1275 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));1387 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
1276}1388}
12771389
1278pub fn min(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T {1390pub fn min(
1279 const i = argMin(T, items, lessThan) orelse return null;1391 comptime T: type,
1392 items: []const T,
1393 context: var,
1394 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1395) ?T {
1396 const i = argMin(T, items, context, lessThan) orelse return null;
1280 return items[i];1397 return items[i];
1281}1398}
12821399
1283test "std.sort.min" {1400test "min" {
1284 testing.expectEqual(@as(?i32, null), min(i32, &[_]i32{}, asc(i32)));1401 testing.expectEqual(@as(?i32, null), min(i32, &[_]i32{}, {}, asc_i32));
1285 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{1}, asc(i32)));1402 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{1}, {}, asc_i32));
1286 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));1403 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1287 testing.expectEqual(@as(?i32, 2), min(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));1404 testing.expectEqual(@as(?i32, 2), min(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1288 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));1405 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1289 testing.expectEqual(@as(?i32, -10), min(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));1406 testing.expectEqual(@as(?i32, -10), min(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1290 testing.expectEqual(@as(?i32, 7), min(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));1407 testing.expectEqual(@as(?i32, 7), min(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
1291}1408}
12921409
1293pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize {1410pub fn argMax(
1411 comptime T: type,
1412 items: []const T,
1413 context: var,
1414 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1415) ?usize {
1294 if (items.len == 0) {1416 if (items.len == 0) {
1295 return null;1417 return null;
1296 }1418 }
...@@ -1298,7 +1420,7 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)...@@ -1298,7 +1420,7 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
1298 var biggest = items[0];1420 var biggest = items[0];
1299 var biggest_index: usize = 0;1421 var biggest_index: usize = 0;
1300 for (items[1..]) |item, i| {1422 for (items[1..]) |item, i| {
1301 if (lessThan(biggest, item)) {1423 if (lessThan(context, biggest, item)) {
1302 biggest = item;1424 biggest = item;
1303 biggest_index = i + 1;1425 biggest_index = i + 1;
1304 }1426 }
...@@ -1307,35 +1429,45 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)...@@ -1307,35 +1429,45 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
1307 return biggest_index;1429 return biggest_index;
1308}1430}
13091431
1310test "std.sort.argMax" {1432test "argMax" {
1311 testing.expectEqual(@as(?usize, null), argMax(i32, &[_]i32{}, asc(i32)));1433 testing.expectEqual(@as(?usize, null), argMax(i32, &[_]i32{}, {}, asc_i32));
1312 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{1}, asc(i32)));1434 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{1}, {}, asc_i32));
1313 testing.expectEqual(@as(?usize, 4), argMax(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));1435 testing.expectEqual(@as(?usize, 4), argMax(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1314 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));1436 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1315 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));1437 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1316 testing.expectEqual(@as(?usize, 2), argMax(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));1438 testing.expectEqual(@as(?usize, 2), argMax(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1317 testing.expectEqual(@as(?usize, 1), argMax(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));1439 testing.expectEqual(@as(?usize, 1), argMax(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
1318}1440}
13191441
1320pub fn max(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T {1442pub fn max(
1321 const i = argMax(T, items, lessThan) orelse return null;1443 comptime T: type,
1444 items: []const T,
1445 context: var,
1446 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1447) ?T {
1448 const i = argMax(T, items, context, lessThan) orelse return null;
1322 return items[i];1449 return items[i];
1323}1450}
13241451
1325test "std.sort.max" {1452test "max" {
1326 testing.expectEqual(@as(?i32, null), max(i32, &[_]i32{}, asc(i32)));1453 testing.expectEqual(@as(?i32, null), max(i32, &[_]i32{}, {}, asc_i32));
1327 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{1}, asc(i32)));1454 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{1}, {}, asc_i32));
1328 testing.expectEqual(@as(?i32, 5), max(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));1455 testing.expectEqual(@as(?i32, 5), max(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1329 testing.expectEqual(@as(?i32, 9), max(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));1456 testing.expectEqual(@as(?i32, 9), max(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1330 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));1457 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1331 testing.expectEqual(@as(?i32, 10), max(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));1458 testing.expectEqual(@as(?i32, 10), max(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1332 testing.expectEqual(@as(?i32, 3), max(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));1459 testing.expectEqual(@as(?i32, 3), max(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
1333}1460}
13341461
1335pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) bool {1462pub fn isSorted(
1463 comptime T: type,
1464 items: []const T,
1465 context: var,
1466 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1467) bool {
1336 var i: usize = 1;1468 var i: usize = 1;
1337 while (i < items.len) : (i += 1) {1469 while (i < items.len) : (i += 1) {
1338 if (lessThan(items[i], items[i - 1])) {1470 if (lessThan(context, items[i], items[i - 1])) {
1339 return false;1471 return false;
1340 }1472 }
1341 }1473 }
...@@ -1343,29 +1475,29 @@ pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T...@@ -1343,29 +1475,29 @@ pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T
1343 return true;1475 return true;
1344}1476}
13451477
1346test "std.sort.isSorted" {1478test "isSorted" {
1347 testing.expect(isSorted(i32, &[_]i32{}, asc(i32)));1479 testing.expect(isSorted(i32, &[_]i32{}, {}, asc_i32));
1348 testing.expect(isSorted(i32, &[_]i32{10}, asc(i32)));1480 testing.expect(isSorted(i32, &[_]i32{10}, {}, asc_i32));
1349 testing.expect(isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));1481 testing.expect(isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1350 testing.expect(isSorted(i32, &[_]i32{ -10, 1, 1, 1, 10 }, asc(i32)));1482 testing.expect(isSorted(i32, &[_]i32{ -10, 1, 1, 1, 10 }, {}, asc_i32));
13511483
1352 testing.expect(isSorted(i32, &[_]i32{}, desc(i32)));1484 testing.expect(isSorted(i32, &[_]i32{}, {}, desc_i32));
1353 testing.expect(isSorted(i32, &[_]i32{-20}, desc(i32)));1485 testing.expect(isSorted(i32, &[_]i32{-20}, {}, desc_i32));
1354 testing.expect(isSorted(i32, &[_]i32{ 3, 2, 1, 0, -1 }, desc(i32)));1486 testing.expect(isSorted(i32, &[_]i32{ 3, 2, 1, 0, -1 }, {}, desc_i32));
1355 testing.expect(isSorted(i32, &[_]i32{ 10, -10 }, desc(i32)));1487 testing.expect(isSorted(i32, &[_]i32{ 10, -10 }, {}, desc_i32));
13561488
1357 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));1489 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1358 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, desc(i32)));1490 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, desc_i32));
13591491
1360 testing.expectEqual(false, isSorted(i32, &[_]i32{ 5, 4, 3, 2, 1 }, asc(i32)));1492 testing.expectEqual(false, isSorted(i32, &[_]i32{ 5, 4, 3, 2, 1 }, {}, asc_i32));
1361 testing.expectEqual(false, isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, desc(i32)));1493 testing.expectEqual(false, isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, desc_i32));
13621494
1363 testing.expect(isSorted(u8, "abcd", asc(u8)));1495 testing.expect(isSorted(u8, "abcd", {}, asc_u8));
1364 testing.expect(isSorted(u8, "zyxw", desc(u8)));1496 testing.expect(isSorted(u8, "zyxw", {}, desc_u8));
13651497
1366 testing.expectEqual(false, isSorted(u8, "abcd", desc(u8)));1498 testing.expectEqual(false, isSorted(u8, "abcd", {}, desc_u8));
1367 testing.expectEqual(false, isSorted(u8, "zyxw", asc(u8)));1499 testing.expectEqual(false, isSorted(u8, "zyxw", {}, asc_u8));
13681500
1369 testing.expect(isSorted(u8, "ffff", asc(u8)));1501 testing.expect(isSorted(u8, "ffff", {}, asc_u8));
1370 testing.expect(isSorted(u8, "ffff", desc(u8)));1502 testing.expect(isSorted(u8, "ffff", {}, desc_u8));
1371}1503}
src-self-hosted/Module.zig+57-12
...@@ -576,6 +576,8 @@ pub fn update(self: *Module) !void {...@@ -576,6 +576,8 @@ pub fn update(self: *Module) !void {
576 // TODO Use the cache hash file system to detect which source files changed.576 // TODO Use the cache hash file system to detect which source files changed.
577 // Here we simulate a full cache miss.577 // Here we simulate a full cache miss.
578 // Analyze the root source file now.578 // Analyze the root source file now.
579 // Source files could have been loaded for any reason; to force a refresh we unload now.
580 self.root_scope.unload(self.allocator);
579 self.analyzeRoot(self.root_scope) catch |err| switch (err) {581 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
580 error.AnalysisFail => {582 error.AnalysisFail => {
581 assert(self.totalErrorCount() != 0);583 assert(self.totalErrorCount() != 0);
...@@ -594,8 +596,11 @@ pub fn update(self: *Module) !void {...@@ -594,8 +596,11 @@ pub fn update(self: *Module) !void {
594 try self.deleteDecl(decl);596 try self.deleteDecl(decl);
595 }597 }
596598
597 // Unload all the source files from memory.599 // If there are any errors, we anticipate the source files being loaded
598 self.root_scope.unload(self.allocator);600 // to report error messages. Otherwise we unload all source files to save memory.
601 if (self.totalErrorCount() == 0) {
602 self.root_scope.unload(self.allocator);
603 }
599604
600 try self.bin_file.flush();605 try self.bin_file.flush();
601 self.link_error_flags = self.bin_file.error_flags;606 self.link_error_flags = self.bin_file.error_flags;
...@@ -668,8 +673,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {...@@ -668,8 +673,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
668 assert(errors.items.len == self.totalErrorCount());673 assert(errors.items.len == self.totalErrorCount());
669674
670 return AllErrors{675 return AllErrors{
671 .arena = arena.state,
672 .list = try arena.allocator.dupe(AllErrors.Message, errors.items),676 .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
677 .arena = arena.state,
673 };678 };
674}679}
675680
...@@ -878,11 +883,11 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -878,11 +883,11 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
878 const decl = kv.value;883 const decl = kv.value;
879 deleted_decls.removeAssertDiscard(decl);884 deleted_decls.removeAssertDiscard(decl);
880 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);885 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
886 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
881 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {887 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
882 //std.debug.warn("noticed '{}' source changed\n", .{src_decl.name});888 //std.debug.warn("'{}' {x} => {x}\n", .{ src_decl.name, decl.contents_hash, new_contents_hash });
883 decl.analysis = .outdated;889 try self.markOutdatedDecl(decl);
884 decl.contents_hash = new_contents_hash;890 decl.contents_hash = new_contents_hash;
885 try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
886 }891 }
887 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {892 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
888 try exports_to_resolve.append(&export_inst.base);893 try exports_to_resolve.append(&export_inst.base);
...@@ -905,6 +910,8 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -905,6 +910,8 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
905}910}
906911
907fn deleteDecl(self: *Module, decl: *Decl) !void {912fn deleteDecl(self: *Module, decl: *Decl) !void {
913 try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len);
914
908 //std.debug.warn("deleting decl '{}'\n", .{decl.name});915 //std.debug.warn("deleting decl '{}'\n", .{decl.name});
909 const name_hash = decl.fullyQualifiedNameHash();916 const name_hash = decl.fullyQualifiedNameHash();
910 self.decl_table.removeAssertDiscard(name_hash);917 self.decl_table.removeAssertDiscard(name_hash);
...@@ -916,17 +923,20 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {...@@ -916,17 +923,20 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
916 // another reference to it may turn up.923 // another reference to it may turn up.
917 assert(!dep.deletion_flag);924 assert(!dep.deletion_flag);
918 dep.deletion_flag = true;925 dep.deletion_flag = true;
919 try self.deletion_set.append(self.allocator, dep);926 self.deletion_set.appendAssumeCapacity(dep);
920 }927 }
921 }928 }
922 // Anything that depends on this deleted decl certainly needs to be re-analyzed.929 // Anything that depends on this deleted decl certainly needs to be re-analyzed.
923 for (decl.dependants.items) |dep| {930 for (decl.dependants.items) |dep| {
924 dep.removeDependency(decl);931 dep.removeDependency(decl);
925 if (dep.analysis != .outdated) {932 if (dep.analysis != .outdated) {
926 dep.analysis = .outdated;933 // TODO Move this failure possibility to the top of the function.
927 try self.work_queue.writeItem(.{ .re_analyze_decl = dep });934 try self.markOutdatedDecl(dep);
928 }935 }
929 }936 }
937 if (self.failed_decls.remove(decl)) |entry| {
938 entry.value.destroy(self.allocator);
939 }
930 self.deleteDeclExports(decl);940 self.deleteDeclExports(decl);
931 self.bin_file.freeDecl(decl);941 self.bin_file.freeDecl(decl);
932 decl.destroy(self.allocator);942 decl.destroy(self.allocator);
...@@ -1083,20 +1093,31 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi...@@ -1083,20 +1093,31 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
1083 .codegen_failure_retryable,1093 .codegen_failure_retryable,
1084 .complete,1094 .complete,
1085 => if (dep.generation != self.generation) {1095 => if (dep.generation != self.generation) {
1086 dep.analysis = .outdated;1096 try self.markOutdatedDecl(dep);
1087 try self.work_queue.writeItem(.{ .re_analyze_decl = dep });
1088 },1097 },
1089 }1098 }
1090 }1099 }
1091 }1100 }
1092}1101}
10931102
1103fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1104 //std.debug.warn("mark {} outdated\n", .{decl.name});
1105 try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
1106 if (self.failed_decls.remove(decl)) |entry| {
1107 entry.value.destroy(self.allocator);
1108 }
1109 decl.analysis = .outdated;
1110}
1111
1094fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {1112fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
1095 const hash = Decl.hashSimpleName(old_inst.name);1113 const hash = Decl.hashSimpleName(old_inst.name);
1096 if (self.decl_table.get(hash)) |kv| {1114 if (self.decl_table.get(hash)) |kv| {
1097 const decl = kv.value;1115 const decl = kv.value;
1098 try self.reAnalyzeDecl(decl, old_inst);1116 try self.reAnalyzeDecl(decl, old_inst);
1099 return decl;1117 return decl;
1118 } else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| {
1119 // This is just a named reference to another decl.
1120 return self.analyzeDeclVal(scope, decl_val);
1100 } else {1121 } else {
1101 const new_decl = blk: {1122 const new_decl = blk: {
1102 try self.decl_table.ensureCapacity(self.decl_table.size + 1);1123 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
...@@ -1442,7 +1463,9 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In...@@ -1442,7 +1463,9 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
1442 switch (old_inst.tag) {1463 switch (old_inst.tag) {
1443 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),1464 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),
1444 .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),1465 .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),
1466 .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?),
1445 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),1467 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
1468 .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?),
1446 .str => {1469 .str => {
1447 const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;1470 const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;
1448 // The bytes references memory inside the ZIR module, which can get deallocated1471 // The bytes references memory inside the ZIR module, which can get deallocated
...@@ -1480,6 +1503,10 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In...@@ -1480,6 +1503,10 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
1480 }1503 }
1481}1504}
14821505
1506fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
1507 return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
1508}
1509
1483fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {1510fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
1484 const b = try self.requireRuntimeBlock(scope, inst.base.src);1511 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1485 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});1512 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
...@@ -1501,6 +1528,24 @@ fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) Inn...@@ -1501,6 +1528,24 @@ fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) Inn
1501 return self.analyzeDeclRef(scope, inst.base.src, decl);1528 return self.analyzeDeclRef(scope, inst.base.src, decl);
1502}1529}
15031530
1531fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
1532 const decl_name = inst.positionals.name;
1533 // This will need to get more fleshed out when there are proper structs & namespaces.
1534 const zir_module = scope.namespace();
1535 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
1536 return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
1537
1538 const decl = try self.resolveCompleteDecl(scope, src_decl);
1539
1540 return decl;
1541}
1542
1543fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
1544 const decl = try self.analyzeDeclVal(scope, inst);
1545 const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl);
1546 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
1547}
1548
1504fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {1549fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
1505 const decl_tv = try decl.typedValue();1550 const decl_tv = try decl.typedValue();
1506 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);1551 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
...@@ -1621,7 +1666,7 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn...@@ -1621,7 +1666,7 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn
1621}1666}
16221667
1623fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {1668fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
1624 return self.constType(scope, primitive.base.src, primitive.positionals.tag.toType());1669 return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
1625}1670}
16261671
1627fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst {1672fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst {
src-self-hosted/main.zig+15-1
...@@ -407,7 +407,21 @@ fn buildOutputType(...@@ -407,7 +407,21 @@ fn buildOutputType(
407 std.debug.warn("-fno-emit-bin not supported yet", .{});407 std.debug.warn("-fno-emit-bin not supported yet", .{});
408 process.exit(1);408 process.exit(1);
409 },409 },
410 .yes_default_path => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),410 .yes_default_path => switch (output_mode) {
411 .Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
412 .Lib => blk: {
413 const suffix = switch (link_mode orelse .Static) {
414 .Static => target_info.target.staticLibSuffix(),
415 .Dynamic => target_info.target.dynamicLibSuffix(),
416 };
417 break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{
418 target_info.target.libPrefix(),
419 root_name,
420 suffix,
421 });
422 },
423 .Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }),
424 },
411 .yes => |p| p,425 .yes => |p| p,
412 };426 };
413427
src-self-hosted/test.zig+111-29
...@@ -27,9 +27,32 @@ pub const TestContext = struct {...@@ -27,9 +27,32 @@ pub const TestContext = struct {
2727
28 pub const ZIRTransformCase = struct {28 pub const ZIRTransformCase = struct {
29 name: []const u8,29 name: []const u8,
30 src: [:0]const u8,
31 expected_zir: []const u8,
32 cross_target: std.zig.CrossTarget,30 cross_target: std.zig.CrossTarget,
31 updates: std.ArrayList(Update),
32
33 pub const Update = struct {
34 expected: Expected,
35 src: [:0]const u8,
36 };
37
38 pub const Expected = union(enum) {
39 zir: []const u8,
40 errors: []const []const u8,
41 };
42
43 pub fn addZIR(case: *ZIRTransformCase, src: [:0]const u8, zir_text: []const u8) void {
44 case.updates.append(.{
45 .src = src,
46 .expected = .{ .zir = zir_text },
47 }) catch unreachable;
48 }
49
50 pub fn addError(case: *ZIRTransformCase, src: [:0]const u8, errors: []const []const u8) void {
51 case.updates.append(.{
52 .src = src,
53 .expected = .{ .errors = errors },
54 }) catch unreachable;
55 }
33 };56 };
3457
35 pub fn addZIRCompareOutput(58 pub fn addZIRCompareOutput(
...@@ -52,14 +75,32 @@ pub const TestContext = struct {...@@ -52,14 +75,32 @@ pub const TestContext = struct {
52 src: [:0]const u8,75 src: [:0]const u8,
53 expected_zir: []const u8,76 expected_zir: []const u8,
54 ) void {77 ) void {
55 ctx.zir_transform_cases.append(.{78 const case = ctx.zir_transform_cases.addOne() catch unreachable;
79 case.* = .{
56 .name = name,80 .name = name,
57 .src = src,
58 .expected_zir = expected_zir,
59 .cross_target = cross_target,81 .cross_target = cross_target,
82 .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),
83 };
84 case.updates.append(.{
85 .src = src,
86 .expected = .{ .zir = expected_zir },
60 }) catch unreachable;87 }) catch unreachable;
61 }88 }
6289
90 pub fn addZIRMulti(
91 ctx: *TestContext,
92 name: []const u8,
93 cross_target: std.zig.CrossTarget,
94 ) *ZIRTransformCase {
95 const case = ctx.zir_transform_cases.addOne() catch unreachable;
96 case.* = .{
97 .name = name,
98 .cross_target = cross_target,
99 .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),
100 };
101 return case;
102 }
103
63 fn init(self: *TestContext) !void {104 fn init(self: *TestContext) !void {
64 self.* = .{105 self.* = .{
65 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),106 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
...@@ -178,13 +219,11 @@ pub const TestContext = struct {...@@ -178,13 +219,11 @@ pub const TestContext = struct {
178 var tmp = std.testing.tmpDir(.{});219 var tmp = std.testing.tmpDir(.{});
179 defer tmp.cleanup();220 defer tmp.cleanup();
180221
181 var prg_node = root_node.start(case.name, 3);222 var update_node = root_node.start(case.name, case.updates.items.len);
182 prg_node.activate();223 update_node.activate();
183 defer prg_node.end();224 defer update_node.end();
184225
185 const tmp_src_path = "test-case.zir";226 const tmp_src_path = "test-case.zir";
186 try tmp.dir.writeFile(tmp_src_path, case.src);
187
188 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);227 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
189 defer root_pkg.destroy();228 defer root_pkg.destroy();
190229
...@@ -198,25 +237,68 @@ pub const TestContext = struct {...@@ -198,25 +237,68 @@ pub const TestContext = struct {
198 });237 });
199 defer module.deinit();238 defer module.deinit();
200239
201 var module_node = prg_node.start("parse/analysis/codegen", null);240 for (case.updates.items) |update| {
202 module_node.activate();241 var prg_node = update_node.start("", 3);
203 try module.update();242 prg_node.activate();
204 module_node.end();243 defer prg_node.end();
205244
206 var emit_node = prg_node.start("emit", null);245 try tmp.dir.writeFile(tmp_src_path, update.src);
207 emit_node.activate();246
208 var new_zir_module = try zir.emit(allocator, module);247 var module_node = prg_node.start("parse/analysis/codegen", null);
209 defer new_zir_module.deinit(allocator);248 module_node.activate();
210 emit_node.end();249 try module.update();
211250 module_node.end();
212 var write_node = prg_node.start("write", null);251
213 write_node.activate();252 switch (update.expected) {
214 var out_zir = std.ArrayList(u8).init(allocator);253 .zir => |expected_zir| {
215 defer out_zir.deinit();254 var emit_node = prg_node.start("emit", null);
216 try new_zir_module.writeToStream(allocator, out_zir.outStream());255 emit_node.activate();
217 write_node.end();256 var new_zir_module = try zir.emit(allocator, module);
218257 defer new_zir_module.deinit(allocator);
219 std.testing.expectEqualSlices(u8, case.expected_zir, out_zir.items);258 emit_node.end();
259
260 var write_node = prg_node.start("write", null);
261 write_node.activate();
262 var out_zir = std.ArrayList(u8).init(allocator);
263 defer out_zir.deinit();
264 try new_zir_module.writeToStream(allocator, out_zir.outStream());
265 write_node.end();
266
267 std.testing.expectEqualSlices(u8, expected_zir, out_zir.items);
268 },
269 .errors => |expected_errors| {
270 var all_errors = try module.getAllErrorsAlloc();
271 defer all_errors.deinit(module.allocator);
272 for (expected_errors) |expected_error| {
273 for (all_errors.list) |full_err_msg| {
274 const text = try std.fmt.allocPrint(allocator, ":{}:{}: error: {}", .{
275 full_err_msg.line + 1,
276 full_err_msg.column + 1,
277 full_err_msg.msg,
278 });
279 defer allocator.free(text);
280 if (std.mem.eql(u8, text, expected_error)) {
281 break;
282 }
283 } else {
284 std.debug.warn(
285 "{}\nExpected this error:\n================\n{}\n================\nBut found these errors:\n================\n",
286 .{ case.name, expected_error },
287 );
288 for (all_errors.list) |full_err_msg| {
289 std.debug.warn(":{}:{}: error: {}\n", .{
290 full_err_msg.line + 1,
291 full_err_msg.column + 1,
292 full_err_msg.msg,
293 });
294 }
295 std.debug.warn("================\nTest failed\n", .{});
296 std.process.exit(1);
297 }
298 }
299 },
300 }
301 }
220 }302 }
221};303};
222304
src-self-hosted/type.zig+22
...@@ -51,6 +51,7 @@ pub const Type = extern union {...@@ -51,6 +51,7 @@ pub const Type = extern union {
51 .comptime_float => return .ComptimeFloat,51 .comptime_float => return .ComptimeFloat,
52 .noreturn => return .NoReturn,52 .noreturn => return .NoReturn,
53 .@"null" => return .Null,53 .@"null" => return .Null,
54 .@"undefined" => return .Undefined,
5455
55 .fn_noreturn_no_args => return .Fn,56 .fn_noreturn_no_args => return .Fn,
56 .fn_naked_noreturn_no_args => return .Fn,57 .fn_naked_noreturn_no_args => return .Fn,
...@@ -201,6 +202,7 @@ pub const Type = extern union {...@@ -201,6 +202,7 @@ pub const Type = extern union {
201 => return out_stream.writeAll(@tagName(t)),202 => return out_stream.writeAll(@tagName(t)),
202203
203 .@"null" => return out_stream.writeAll("@TypeOf(null)"),204 .@"null" => return out_stream.writeAll("@TypeOf(null)"),
205 .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"),
204206
205 .const_slice_u8 => return out_stream.writeAll("[]const u8"),207 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
206 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),208 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
...@@ -265,6 +267,7 @@ pub const Type = extern union {...@@ -265,6 +267,7 @@ pub const Type = extern union {
265 .comptime_float => return Value.initTag(.comptime_float_type),267 .comptime_float => return Value.initTag(.comptime_float_type),
266 .noreturn => return Value.initTag(.noreturn_type),268 .noreturn => return Value.initTag(.noreturn_type),
267 .@"null" => return Value.initTag(.null_type),269 .@"null" => return Value.initTag(.null_type),
270 .@"undefined" => return Value.initTag(.undefined_type),
268 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),271 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
269 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),272 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
270 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),273 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
...@@ -318,6 +321,7 @@ pub const Type = extern union {...@@ -318,6 +321,7 @@ pub const Type = extern union {
318 .comptime_float,321 .comptime_float,
319 .noreturn,322 .noreturn,
320 .@"null",323 .@"null",
324 .@"undefined",
321 => false,325 => false,
322 };326 };
323 }327 }
...@@ -378,6 +382,7 @@ pub const Type = extern union {...@@ -378,6 +382,7 @@ pub const Type = extern union {
378 .comptime_float,382 .comptime_float,
379 .noreturn,383 .noreturn,
380 .@"null",384 .@"null",
385 .@"undefined",
381 => unreachable,386 => unreachable,
382 };387 };
383 }388 }
...@@ -410,6 +415,7 @@ pub const Type = extern union {...@@ -410,6 +415,7 @@ pub const Type = extern union {
410 .comptime_float,415 .comptime_float,
411 .noreturn,416 .noreturn,
412 .@"null",417 .@"null",
418 .@"undefined",
413 .array,419 .array,
414 .array_u8_sentinel_0,420 .array_u8_sentinel_0,
415 .const_slice_u8,421 .const_slice_u8,
...@@ -454,6 +460,7 @@ pub const Type = extern union {...@@ -454,6 +460,7 @@ pub const Type = extern union {
454 .comptime_float,460 .comptime_float,
455 .noreturn,461 .noreturn,
456 .@"null",462 .@"null",
463 .@"undefined",
457 .array,464 .array,
458 .array_u8_sentinel_0,465 .array_u8_sentinel_0,
459 .single_const_pointer,466 .single_const_pointer,
...@@ -498,6 +505,7 @@ pub const Type = extern union {...@@ -498,6 +505,7 @@ pub const Type = extern union {
498 .comptime_float,505 .comptime_float,
499 .noreturn,506 .noreturn,
500 .@"null",507 .@"null",
508 .@"undefined",
501 .array,509 .array,
502 .array_u8_sentinel_0,510 .array_u8_sentinel_0,
503 .fn_noreturn_no_args,511 .fn_noreturn_no_args,
...@@ -543,6 +551,7 @@ pub const Type = extern union {...@@ -543,6 +551,7 @@ pub const Type = extern union {
543 .comptime_float,551 .comptime_float,
544 .noreturn,552 .noreturn,
545 .@"null",553 .@"null",
554 .@"undefined",
546 .fn_noreturn_no_args,555 .fn_noreturn_no_args,
547 .fn_naked_noreturn_no_args,556 .fn_naked_noreturn_no_args,
548 .fn_ccc_void_no_args,557 .fn_ccc_void_no_args,
...@@ -586,6 +595,7 @@ pub const Type = extern union {...@@ -586,6 +595,7 @@ pub const Type = extern union {
586 .comptime_float,595 .comptime_float,
587 .noreturn,596 .noreturn,
588 .@"null",597 .@"null",
598 .@"undefined",
589 .fn_noreturn_no_args,599 .fn_noreturn_no_args,
590 .fn_naked_noreturn_no_args,600 .fn_naked_noreturn_no_args,
591 .fn_ccc_void_no_args,601 .fn_ccc_void_no_args,
...@@ -630,6 +640,7 @@ pub const Type = extern union {...@@ -630,6 +640,7 @@ pub const Type = extern union {
630 .comptime_float,640 .comptime_float,
631 .noreturn,641 .noreturn,
632 .@"null",642 .@"null",
643 .@"undefined",
633 .fn_noreturn_no_args,644 .fn_noreturn_no_args,
634 .fn_naked_noreturn_no_args,645 .fn_naked_noreturn_no_args,
635 .fn_ccc_void_no_args,646 .fn_ccc_void_no_args,
...@@ -662,6 +673,7 @@ pub const Type = extern union {...@@ -662,6 +673,7 @@ pub const Type = extern union {
662 .comptime_float,673 .comptime_float,
663 .noreturn,674 .noreturn,
664 .@"null",675 .@"null",
676 .@"undefined",
665 .fn_noreturn_no_args,677 .fn_noreturn_no_args,
666 .fn_naked_noreturn_no_args,678 .fn_naked_noreturn_no_args,
667 .fn_ccc_void_no_args,679 .fn_ccc_void_no_args,
...@@ -707,6 +719,7 @@ pub const Type = extern union {...@@ -707,6 +719,7 @@ pub const Type = extern union {
707 .comptime_float,719 .comptime_float,
708 .noreturn,720 .noreturn,
709 .@"null",721 .@"null",
722 .@"undefined",
710 .fn_noreturn_no_args,723 .fn_noreturn_no_args,
711 .fn_naked_noreturn_no_args,724 .fn_naked_noreturn_no_args,
712 .fn_ccc_void_no_args,725 .fn_ccc_void_no_args,
...@@ -781,6 +794,7 @@ pub const Type = extern union {...@@ -781,6 +794,7 @@ pub const Type = extern union {
781 .comptime_float,794 .comptime_float,
782 .noreturn,795 .noreturn,
783 .@"null",796 .@"null",
797 .@"undefined",
784 .array,798 .array,
785 .single_const_pointer,799 .single_const_pointer,
786 .single_const_pointer_to_comptime_int,800 .single_const_pointer_to_comptime_int,
...@@ -826,6 +840,7 @@ pub const Type = extern union {...@@ -826,6 +840,7 @@ pub const Type = extern union {
826 .comptime_float,840 .comptime_float,
827 .noreturn,841 .noreturn,
828 .@"null",842 .@"null",
843 .@"undefined",
829 .array,844 .array,
830 .single_const_pointer,845 .single_const_pointer,
831 .single_const_pointer_to_comptime_int,846 .single_const_pointer_to_comptime_int,
...@@ -870,6 +885,7 @@ pub const Type = extern union {...@@ -870,6 +885,7 @@ pub const Type = extern union {
870 .comptime_float,885 .comptime_float,
871 .noreturn,886 .noreturn,
872 .@"null",887 .@"null",
888 .@"undefined",
873 .array,889 .array,
874 .single_const_pointer,890 .single_const_pointer,
875 .single_const_pointer_to_comptime_int,891 .single_const_pointer_to_comptime_int,
...@@ -914,6 +930,7 @@ pub const Type = extern union {...@@ -914,6 +930,7 @@ pub const Type = extern union {
914 .comptime_float,930 .comptime_float,
915 .noreturn,931 .noreturn,
916 .@"null",932 .@"null",
933 .@"undefined",
917 .array,934 .array,
918 .single_const_pointer,935 .single_const_pointer,
919 .single_const_pointer_to_comptime_int,936 .single_const_pointer_to_comptime_int,
...@@ -958,6 +975,7 @@ pub const Type = extern union {...@@ -958,6 +975,7 @@ pub const Type = extern union {
958 .comptime_float,975 .comptime_float,
959 .noreturn,976 .noreturn,
960 .@"null",977 .@"null",
978 .@"undefined",
961 .array,979 .array,
962 .single_const_pointer,980 .single_const_pointer,
963 .single_const_pointer_to_comptime_int,981 .single_const_pointer_to_comptime_int,
...@@ -1013,6 +1031,7 @@ pub const Type = extern union {...@@ -1013,6 +1031,7 @@ pub const Type = extern union {
1013 .anyerror,1031 .anyerror,
1014 .noreturn,1032 .noreturn,
1015 .@"null",1033 .@"null",
1034 .@"undefined",
1016 .fn_noreturn_no_args,1035 .fn_noreturn_no_args,
1017 .fn_naked_noreturn_no_args,1036 .fn_naked_noreturn_no_args,
1018 .fn_ccc_void_no_args,1037 .fn_ccc_void_no_args,
...@@ -1062,6 +1081,7 @@ pub const Type = extern union {...@@ -1062,6 +1081,7 @@ pub const Type = extern union {
1062 .void,1081 .void,
1063 .noreturn,1082 .noreturn,
1064 .@"null",1083 .@"null",
1084 .@"undefined",
1065 => return true,1085 => return true,
10661086
1067 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,1087 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
...@@ -1115,6 +1135,7 @@ pub const Type = extern union {...@@ -1115,6 +1135,7 @@ pub const Type = extern union {
1115 .void,1135 .void,
1116 .noreturn,1136 .noreturn,
1117 .@"null",1137 .@"null",
1138 .@"undefined",
1118 .int_unsigned,1139 .int_unsigned,
1119 .int_signed,1140 .int_signed,
1120 .array,1141 .array,
...@@ -1157,6 +1178,7 @@ pub const Type = extern union {...@@ -1157,6 +1178,7 @@ pub const Type = extern union {
1157 comptime_float,1178 comptime_float,
1158 noreturn,1179 noreturn,
1159 @"null",1180 @"null",
1181 @"undefined",
1160 fn_noreturn_no_args,1182 fn_noreturn_no_args,
1161 fn_naked_noreturn_no_args,1183 fn_naked_noreturn_no_args,
1162 fn_ccc_void_no_args,1184 fn_ccc_void_no_args,
src-self-hosted/value.zig+12
...@@ -47,6 +47,7 @@ pub const Value = extern union {...@@ -47,6 +47,7 @@ pub const Value = extern union {
47 comptime_float_type,47 comptime_float_type,
48 noreturn_type,48 noreturn_type,
49 null_type,49 null_type,
50 undefined_type,
50 fn_noreturn_no_args_type,51 fn_noreturn_no_args_type,
51 fn_naked_noreturn_no_args_type,52 fn_naked_noreturn_no_args_type,
52 fn_ccc_void_no_args_type,53 fn_ccc_void_no_args_type,
...@@ -141,6 +142,7 @@ pub const Value = extern union {...@@ -141,6 +142,7 @@ pub const Value = extern union {
141 .comptime_float_type => return out_stream.writeAll("comptime_float"),142 .comptime_float_type => return out_stream.writeAll("comptime_float"),
142 .noreturn_type => return out_stream.writeAll("noreturn"),143 .noreturn_type => return out_stream.writeAll("noreturn"),
143 .null_type => return out_stream.writeAll("@TypeOf(null)"),144 .null_type => return out_stream.writeAll("@TypeOf(null)"),
145 .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
144 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),146 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
145 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),147 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
146 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),148 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
...@@ -225,6 +227,7 @@ pub const Value = extern union {...@@ -225,6 +227,7 @@ pub const Value = extern union {
225 .comptime_float_type => Type.initTag(.comptime_float),227 .comptime_float_type => Type.initTag(.comptime_float),
226 .noreturn_type => Type.initTag(.noreturn),228 .noreturn_type => Type.initTag(.noreturn),
227 .null_type => Type.initTag(.@"null"),229 .null_type => Type.initTag(.@"null"),
230 .undefined_type => Type.initTag(.@"undefined"),
228 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),231 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
229 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),232 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
230 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),233 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
...@@ -281,6 +284,7 @@ pub const Value = extern union {...@@ -281,6 +284,7 @@ pub const Value = extern union {
281 .comptime_float_type,284 .comptime_float_type,
282 .noreturn_type,285 .noreturn_type,
283 .null_type,286 .null_type,
287 .undefined_type,
284 .fn_noreturn_no_args_type,288 .fn_noreturn_no_args_type,
285 .fn_naked_noreturn_no_args_type,289 .fn_naked_noreturn_no_args_type,
286 .fn_ccc_void_no_args_type,290 .fn_ccc_void_no_args_type,
...@@ -339,6 +343,7 @@ pub const Value = extern union {...@@ -339,6 +343,7 @@ pub const Value = extern union {
339 .comptime_float_type,343 .comptime_float_type,
340 .noreturn_type,344 .noreturn_type,
341 .null_type,345 .null_type,
346 .undefined_type,
342 .fn_noreturn_no_args_type,347 .fn_noreturn_no_args_type,
343 .fn_naked_noreturn_no_args_type,348 .fn_naked_noreturn_no_args_type,
344 .fn_ccc_void_no_args_type,349 .fn_ccc_void_no_args_type,
...@@ -398,6 +403,7 @@ pub const Value = extern union {...@@ -398,6 +403,7 @@ pub const Value = extern union {
398 .comptime_float_type,403 .comptime_float_type,
399 .noreturn_type,404 .noreturn_type,
400 .null_type,405 .null_type,
406 .undefined_type,
401 .fn_noreturn_no_args_type,407 .fn_noreturn_no_args_type,
402 .fn_naked_noreturn_no_args_type,408 .fn_naked_noreturn_no_args_type,
403 .fn_ccc_void_no_args_type,409 .fn_ccc_void_no_args_type,
...@@ -462,6 +468,7 @@ pub const Value = extern union {...@@ -462,6 +468,7 @@ pub const Value = extern union {
462 .comptime_float_type,468 .comptime_float_type,
463 .noreturn_type,469 .noreturn_type,
464 .null_type,470 .null_type,
471 .undefined_type,
465 .fn_noreturn_no_args_type,472 .fn_noreturn_no_args_type,
466 .fn_naked_noreturn_no_args_type,473 .fn_naked_noreturn_no_args_type,
467 .fn_ccc_void_no_args_type,474 .fn_ccc_void_no_args_type,
...@@ -555,6 +562,7 @@ pub const Value = extern union {...@@ -555,6 +562,7 @@ pub const Value = extern union {
555 .comptime_float_type,562 .comptime_float_type,
556 .noreturn_type,563 .noreturn_type,
557 .null_type,564 .null_type,
565 .undefined_type,
558 .fn_noreturn_no_args_type,566 .fn_noreturn_no_args_type,
559 .fn_naked_noreturn_no_args_type,567 .fn_naked_noreturn_no_args_type,
560 .fn_ccc_void_no_args_type,568 .fn_ccc_void_no_args_type,
...@@ -610,6 +618,7 @@ pub const Value = extern union {...@@ -610,6 +618,7 @@ pub const Value = extern union {
610 .comptime_float_type,618 .comptime_float_type,
611 .noreturn_type,619 .noreturn_type,
612 .null_type,620 .null_type,
621 .undefined_type,
613 .fn_noreturn_no_args_type,622 .fn_noreturn_no_args_type,
614 .fn_naked_noreturn_no_args_type,623 .fn_naked_noreturn_no_args_type,
615 .fn_ccc_void_no_args_type,624 .fn_ccc_void_no_args_type,
...@@ -710,6 +719,7 @@ pub const Value = extern union {...@@ -710,6 +719,7 @@ pub const Value = extern union {
710 .comptime_float_type,719 .comptime_float_type,
711 .noreturn_type,720 .noreturn_type,
712 .null_type,721 .null_type,
722 .undefined_type,
713 .fn_noreturn_no_args_type,723 .fn_noreturn_no_args_type,
714 .fn_naked_noreturn_no_args_type,724 .fn_naked_noreturn_no_args_type,
715 .fn_ccc_void_no_args_type,725 .fn_ccc_void_no_args_type,
...@@ -771,6 +781,7 @@ pub const Value = extern union {...@@ -771,6 +781,7 @@ pub const Value = extern union {
771 .comptime_float_type,781 .comptime_float_type,
772 .noreturn_type,782 .noreturn_type,
773 .null_type,783 .null_type,
784 .undefined_type,
774 .fn_noreturn_no_args_type,785 .fn_noreturn_no_args_type,
775 .fn_naked_noreturn_no_args_type,786 .fn_naked_noreturn_no_args_type,
776 .fn_ccc_void_no_args_type,787 .fn_ccc_void_no_args_type,
...@@ -849,6 +860,7 @@ pub const Value = extern union {...@@ -849,6 +860,7 @@ pub const Value = extern union {
849 .comptime_float_type,860 .comptime_float_type,
850 .noreturn_type,861 .noreturn_type,
851 .null_type,862 .null_type,
863 .undefined_type,
852 .fn_noreturn_no_args_type,864 .fn_noreturn_no_args_type,
853 .fn_naked_noreturn_no_args_type,865 .fn_naked_noreturn_no_args_type,
854 .fn_ccc_void_no_args_type,866 .fn_ccc_void_no_args_type,
src-self-hosted/zir.zig+302-141
...@@ -27,9 +27,12 @@ pub const Inst = struct {...@@ -27,9 +27,12 @@ pub const Inst = struct {
27 pub const Tag = enum {27 pub const Tag = enum {
28 breakpoint,28 breakpoint,
29 call,29 call,
30 /// Represents a reference to a global decl by name.30 compileerror,
31 /// The syntax `@foo` is equivalent to `declref("foo")`.31 /// Represents a pointer to a global decl by name.
32 declref,32 declref,
33 /// The syntax `@foo` is equivalent to `declval("foo")`.
34 /// declval is equivalent to declref followed by deref.
35 declval,
33 str,36 str,
34 int,37 int,
35 ptrtoint,38 ptrtoint,
...@@ -59,6 +62,8 @@ pub const Inst = struct {...@@ -59,6 +62,8 @@ pub const Inst = struct {
59 .breakpoint => Breakpoint,62 .breakpoint => Breakpoint,
60 .call => Call,63 .call => Call,
61 .declref => DeclRef,64 .declref => DeclRef,
65 .declval => DeclVal,
66 .compileerror => CompileError,
62 .str => Str,67 .str => Str,
63 .int => Int,68 .int => Int,
64 .ptrtoint => PtrToInt,69 .ptrtoint => PtrToInt,
...@@ -122,6 +127,26 @@ pub const Inst = struct {...@@ -122,6 +127,26 @@ pub const Inst = struct {
122 kw_args: struct {},127 kw_args: struct {},
123 };128 };
124129
130 pub const DeclVal = struct {
131 pub const base_tag = Tag.declval;
132 base: Inst,
133
134 positionals: struct {
135 name: []const u8,
136 },
137 kw_args: struct {},
138 };
139
140 pub const CompileError = struct {
141 pub const base_tag = Tag.compileerror;
142 base: Inst,
143
144 positionals: struct {
145 msg: []const u8,
146 },
147 kw_args: struct {},
148 };
149
125 pub const Str = struct {150 pub const Str = struct {
126 pub const base_tag = Tag.str;151 pub const base_tag = Tag.str;
127 base: Inst,152 base: Inst,
...@@ -254,11 +279,11 @@ pub const Inst = struct {...@@ -254,11 +279,11 @@ pub const Inst = struct {
254 base: Inst,279 base: Inst,
255280
256 positionals: struct {281 positionals: struct {
257 tag: BuiltinType,282 tag: Builtin,
258 },283 },
259 kw_args: struct {},284 kw_args: struct {},
260285
261 pub const BuiltinType = enum {286 pub const Builtin = enum {
262 isize,287 isize,
263 usize,288 usize,
264 c_short,289 c_short,
...@@ -282,32 +307,42 @@ pub const Inst = struct {...@@ -282,32 +307,42 @@ pub const Inst = struct {
282 anyerror,307 anyerror,
283 comptime_int,308 comptime_int,
284 comptime_float,309 comptime_float,
310 @"true",
311 @"false",
312 @"null",
313 @"undefined",
314 void_value,
285315
286 pub fn toType(self: BuiltinType) Type {316 pub fn toTypedValue(self: Builtin) TypedValue {
287 return switch (self) {317 return switch (self) {
288 .isize => Type.initTag(.isize),318 .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },
289 .usize => Type.initTag(.usize),319 .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },
290 .c_short => Type.initTag(.c_short),320 .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },
291 .c_ushort => Type.initTag(.c_ushort),321 .c_ushort => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ushort_type) },
292 .c_int => Type.initTag(.c_int),322 .c_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_int_type) },
293 .c_uint => Type.initTag(.c_uint),323 .c_uint => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_uint_type) },
294 .c_long => Type.initTag(.c_long),324 .c_long => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_long_type) },
295 .c_ulong => Type.initTag(.c_ulong),325 .c_ulong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulong_type) },
296 .c_longlong => Type.initTag(.c_longlong),326 .c_longlong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longlong_type) },
297 .c_ulonglong => Type.initTag(.c_ulonglong),327 .c_ulonglong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulonglong_type) },
298 .c_longdouble => Type.initTag(.c_longdouble),328 .c_longdouble => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longdouble_type) },
299 .c_void => Type.initTag(.c_void),329 .c_void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_void_type) },
300 .f16 => Type.initTag(.f16),330 .f16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f16_type) },
301 .f32 => Type.initTag(.f32),331 .f32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f32_type) },
302 .f64 => Type.initTag(.f64),332 .f64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f64_type) },
303 .f128 => Type.initTag(.f128),333 .f128 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f128_type) },
304 .bool => Type.initTag(.bool),334 .bool => .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type) },
305 .void => Type.initTag(.void),335 .void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type) },
306 .noreturn => Type.initTag(.noreturn),336 .noreturn => .{ .ty = Type.initTag(.type), .val = Value.initTag(.noreturn_type) },
307 .type => Type.initTag(.type),337 .type => .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type) },
308 .anyerror => Type.initTag(.anyerror),338 .anyerror => .{ .ty = Type.initTag(.type), .val = Value.initTag(.anyerror_type) },
309 .comptime_int => Type.initTag(.comptime_int),339 .comptime_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_int_type) },
310 .comptime_float => Type.initTag(.comptime_float),340 .comptime_float => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_float_type) },
341 .@"true" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_true) },
342 .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
343 .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
344 .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
345 .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) },
311 };346 };
312 }347 }
313 };348 };
...@@ -440,7 +475,7 @@ pub const Module = struct {...@@ -440,7 +475,7 @@ pub const Module = struct {
440 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};475 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
441 }476 }
442477
443 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });478 const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize });
444479
445 /// TODO Look into making a table to speed this up.480 /// TODO Look into making a table to speed this up.
446 pub fn findDecl(self: Module, name: []const u8) ?*Inst {481 pub fn findDecl(self: Module, name: []const u8) ?*Inst {
...@@ -462,17 +497,17 @@ pub const Module = struct {...@@ -462,17 +497,17 @@ pub const Module = struct {
462 try inst_table.ensureCapacity(self.decls.len);497 try inst_table.ensureCapacity(self.decls.len);
463498
464 for (self.decls) |decl, decl_i| {499 for (self.decls) |decl, decl_i| {
465 try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });500 try inst_table.putNoClobber(decl, .{ .inst = decl, .index = null });
466501
467 if (decl.cast(Inst.Fn)) |fn_inst| {502 if (decl.cast(Inst.Fn)) |fn_inst| {
468 for (fn_inst.positionals.body.instructions) |inst, inst_i| {503 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
469 try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });504 try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i });
470 }505 }
471 }506 }
472 }507 }
473508
474 for (self.decls) |decl, i| {509 for (self.decls) |decl, i| {
475 try stream.print("@{} ", .{i});510 try stream.print("@{} ", .{decl.name});
476 try self.writeInstToStream(stream, decl, &inst_table);511 try self.writeInstToStream(stream, decl, &inst_table);
477 try stream.writeByte('\n');512 try stream.writeByte('\n');
478 }513 }
...@@ -489,6 +524,8 @@ pub const Module = struct {...@@ -489,6 +524,8 @@ pub const Module = struct {
489 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),524 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
490 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),525 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
491 .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),526 .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
527 .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table),
528 .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table),
492 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),529 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
493 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),530 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
494 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),531 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
...@@ -587,9 +624,18 @@ pub const Module = struct {...@@ -587,9 +624,18 @@ pub const Module = struct {
587 }624 }
588625
589 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {626 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
590 const info = inst_table.getValue(inst).?;627 if (inst_table.getValue(inst)) |info| {
591 const prefix = if (info.fn_body == null) "@" else "%";628 if (info.index) |i| {
592 try stream.print("{}{}", .{ prefix, info.index });629 try stream.print("%{}", .{info.index});
630 } else {
631 try stream.print("@{}", .{info.inst.name});
632 }
633 } else if (inst.cast(Inst.DeclVal)) |decl_val| {
634 try stream.print("@{}", .{decl_val.positionals.name});
635 } else {
636 //try stream.print("?", .{});
637 unreachable;
638 }
593 }639 }
594};640};
595641
...@@ -884,6 +930,7 @@ const Parser = struct {...@@ -884,6 +930,7 @@ const Parser = struct {
884 try requireEatBytes(self, ")");930 try requireEatBytes(self, ")");
885931
886 inst_specific.base.contents = self.source[contents_start..self.i];932 inst_specific.base.contents = self.source[contents_start..self.i];
933 //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents });
887934
888 return &inst_specific.base;935 return &inst_specific.base;
889 }936 }
...@@ -964,47 +1011,17 @@ const Parser = struct {...@@ -964,47 +1011,17 @@ const Parser = struct {
964 self.i = src;1011 self.i = src;
965 return self.fail("unrecognized identifier: {}", .{bad_name});1012 return self.fail("unrecognized identifier: {}", .{bad_name});
966 } else {1013 } else {
967 const name_array = try self.arena.allocator.create(Inst.Str);1014 const declval = try self.arena.allocator.create(Inst.DeclVal);
968 name_array.* = .{1015 declval.* = .{
969 .base = .{
970 .name = try self.generateName(),
971 .src = src,
972 .tag = Inst.Str.base_tag,
973 },
974 .positionals = .{ .bytes = ident },
975 .kw_args = .{},
976 };
977 const name = try self.arena.allocator.create(Inst.Ref);
978 name.* = .{
979 .base = .{
980 .name = try self.generateName(),
981 .src = src,
982 .tag = Inst.Ref.base_tag,
983 },
984 .positionals = .{ .operand = &name_array.base },
985 .kw_args = .{},
986 };
987 const declref = try self.arena.allocator.create(Inst.DeclRef);
988 declref.* = .{
989 .base = .{
990 .name = try self.generateName(),
991 .src = src,
992 .tag = Inst.DeclRef.base_tag,
993 },
994 .positionals = .{ .name = &name.base },
995 .kw_args = .{},
996 };
997 const deref = try self.arena.allocator.create(Inst.Deref);
998 deref.* = .{
999 .base = .{1016 .base = .{
1000 .name = try self.generateName(),1017 .name = try self.generateName(),
1001 .src = src,1018 .src = src,
1002 .tag = Inst.Deref.base_tag,1019 .tag = Inst.DeclVal.base_tag,
1003 },1020 },
1004 .positionals = .{ .ptr = &declref.base },1021 .positionals = .{ .name = ident },
1005 .kw_args = .{},1022 .kw_args = .{},
1006 };1023 };
1007 return &deref.base;1024 return &declval.base;
1008 }1025 }
1009 };1026 };
1010 if (local_ref) {1027 if (local_ref) {
...@@ -1025,12 +1042,15 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {...@@ -1025,12 +1042,15 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
1025 var ctx: EmitZIR = .{1042 var ctx: EmitZIR = .{
1026 .allocator = allocator,1043 .allocator = allocator,
1027 .decls = .{},1044 .decls = .{},
1028 .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator),
1029 .arena = std.heap.ArenaAllocator.init(allocator),1045 .arena = std.heap.ArenaAllocator.init(allocator),
1030 .old_module = &old_module,1046 .old_module = &old_module,
1047 .next_auto_name = 0,
1048 .names = std.StringHashMap(void).init(allocator),
1049 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Inst).init(allocator),
1031 };1050 };
1032 defer ctx.decls.deinit(allocator);1051 defer ctx.decls.deinit(allocator);
1033 defer ctx.decl_table.deinit();1052 defer ctx.names.deinit();
1053 defer ctx.primitive_table.deinit();
1034 errdefer ctx.arena.deinit();1054 errdefer ctx.arena.deinit();
10351055
1036 try ctx.emit();1056 try ctx.emit();
...@@ -1046,47 +1066,90 @@ const EmitZIR = struct {...@@ -1046,47 +1066,90 @@ const EmitZIR = struct {
1046 arena: std.heap.ArenaAllocator,1066 arena: std.heap.ArenaAllocator,
1047 old_module: *const IrModule,1067 old_module: *const IrModule,
1048 decls: std.ArrayListUnmanaged(*Inst),1068 decls: std.ArrayListUnmanaged(*Inst),
1049 decl_table: std.AutoHashMap(*ir.Inst, *Inst),1069 names: std.StringHashMap(void),
1070 next_auto_name: usize,
1071 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Inst),
10501072
1051 fn emit(self: *EmitZIR) !void {1073 fn emit(self: *EmitZIR) !void {
1052 var it = self.old_module.decl_exports.iterator();1074 // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
1053 while (it.next()) |kv| {1075 // by the hash table.
1054 const decl = kv.key;1076 var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator);
1055 const exports = kv.value;1077 defer src_decls.deinit();
1056 const export_value = try self.emitTypedValue(decl.src, decl.typed_value.most_recent.typed_value);1078 try src_decls.ensureCapacity(self.old_module.decl_table.size);
1057 for (exports) |module_export| {1079 try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size);
1058 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);1080 try self.names.ensureCapacity(self.old_module.decl_table.size);
1059 const export_inst = try self.arena.allocator.create(Inst.Export);1081
1060 export_inst.* = .{1082 var decl_it = self.old_module.decl_table.iterator();
1061 .base = .{1083 while (decl_it.next()) |kv| {
1062 .name = try self.autoName(),1084 const decl = kv.value;
1063 .src = module_export.src,1085 src_decls.appendAssumeCapacity(decl);
1064 .tag = Inst.Export.base_tag,1086 self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {});
1065 },1087 }
1066 .positionals = .{1088 std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {
1067 .symbol_name = symbol_name,1089 fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {
1068 .value = export_value,1090 return a.src < b.src;
1069 },1091 }
1070 .kw_args = .{},1092 }).lessThan);
1071 };1093
1072 try self.decls.append(self.allocator, &export_inst.base);1094 // Emit all the decls.
1095 for (src_decls.items) |ir_decl| {
1096 if (self.old_module.export_owners.getValue(ir_decl)) |exports| {
1097 for (exports) |module_export| {
1098 const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name));
1099 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
1100 const export_inst = try self.arena.allocator.create(Inst.Export);
1101 export_inst.* = .{
1102 .base = .{
1103 .name = try self.autoName(),
1104 .src = module_export.src,
1105 .tag = Inst.Export.base_tag,
1106 },
1107 .positionals = .{
1108 .symbol_name = symbol_name,
1109 .value = declval,
1110 },
1111 .kw_args = .{},
1112 };
1113 try self.decls.append(self.allocator, &export_inst.base);
1114 }
1115 } else {
1116 const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value);
1117 new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
1073 }1118 }
1074 }1119 }
1075 }1120 }
10761121
1077 fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {1122 fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
1078 if (inst.cast(ir.Inst.Constant)) |const_inst| {1123 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1079 if (self.decl_table.getValue(inst)) |decl| {1124 const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
1080 return decl;1125 const owner_decl = func_pl.func.owner_decl;
1081 }1126 break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
1082 const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });1127 } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
1083 try self.decl_table.putNoClobber(inst, new_decl);1128 break :blk try self.emitDeclRef(inst.src, declref.decl);
1129 } else blk: {
1130 break :blk try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1131 };
1132 try inst_table.putNoClobber(inst, new_decl);
1084 return new_decl;1133 return new_decl;
1085 } else {1134 } else {
1086 return inst_table.getValue(inst).?;1135 return inst_table.getValue(inst).?;
1087 }1136 }
1088 }1137 }
10891138
1139 fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst {
1140 const declval = try self.arena.allocator.create(Inst.DeclVal);
1141 declval.* = .{
1142 .base = .{
1143 .name = try self.autoName(),
1144 .src = src,
1145 .tag = Inst.DeclVal.base_tag,
1146 },
1147 .positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) },
1148 .kw_args = .{},
1149 };
1150 return &declval.base;
1151 }
1152
1090 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {1153 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
1091 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);1154 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
1092 const int_inst = try self.arena.allocator.create(Inst.Int);1155 const int_inst = try self.arena.allocator.create(Inst.Int);
...@@ -1105,8 +1168,31 @@ const EmitZIR = struct {...@@ -1105,8 +1168,31 @@ const EmitZIR = struct {
1105 return &int_inst.base;1168 return &int_inst.base;
1106 }1169 }
11071170
1171 fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst {
1172 const declval = try self.emitDeclVal(src, mem.spanZ(decl.name));
1173 const ref_inst = try self.arena.allocator.create(Inst.Ref);
1174 ref_inst.* = .{
1175 .base = .{
1176 .name = try self.autoName(),
1177 .src = src,
1178 .tag = Inst.Ref.base_tag,
1179 },
1180 .positionals = .{
1181 .operand = declval,
1182 },
1183 .kw_args = .{},
1184 };
1185 try self.decls.append(self.allocator, &ref_inst.base);
1186
1187 return &ref_inst.base;
1188 }
1189
1108 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {1190 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {
1109 const allocator = &self.arena.allocator;1191 const allocator = &self.arena.allocator;
1192 if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
1193 const decl = decl_ref.decl;
1194 return self.emitDeclRef(src, decl);
1195 }
1110 switch (typed_value.ty.zigTypeTag()) {1196 switch (typed_value.ty.zigTypeTag()) {
1111 .Pointer => {1197 .Pointer => {
1112 const ptr_elem_type = typed_value.ty.elemType();1198 const ptr_elem_type = typed_value.ty.elemType();
...@@ -1142,7 +1228,6 @@ const EmitZIR = struct {...@@ -1142,7 +1228,6 @@ const EmitZIR = struct {
1142 },1228 },
1143 .kw_args = .{},1229 .kw_args = .{},
1144 };1230 };
1145 try self.decls.append(self.allocator, &as_inst.base);
11461231
1147 return &as_inst.base;1232 return &as_inst.base;
1148 },1233 },
...@@ -1159,7 +1244,44 @@ const EmitZIR = struct {...@@ -1159,7 +1244,44 @@ const EmitZIR = struct {
1159 var instructions = std.ArrayList(*Inst).init(self.allocator);1244 var instructions = std.ArrayList(*Inst).init(self.allocator);
1160 defer instructions.deinit();1245 defer instructions.deinit();
11611246
1162 try self.emitBody(module_fn.analysis.success, &inst_table, &instructions);1247 switch (module_fn.analysis) {
1248 .queued => unreachable,
1249 .in_progress => unreachable,
1250 .success => |body| {
1251 try self.emitBody(body, &inst_table, &instructions);
1252 },
1253 .sema_failure => {
1254 const err_msg = self.old_module.failed_decls.getValue(module_fn.owner_decl).?;
1255 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1256 fail_inst.* = .{
1257 .base = .{
1258 .name = try self.autoName(),
1259 .src = src,
1260 .tag = Inst.CompileError.base_tag,
1261 },
1262 .positionals = .{
1263 .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
1264 },
1265 .kw_args = .{},
1266 };
1267 try instructions.append(&fail_inst.base);
1268 },
1269 .dependency_failure => {
1270 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1271 fail_inst.* = .{
1272 .base = .{
1273 .name = try self.autoName(),
1274 .src = src,
1275 .tag = Inst.CompileError.base_tag,
1276 },
1277 .positionals = .{
1278 .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"),
1279 },
1280 .kw_args = .{},
1281 };
1282 try instructions.append(&fail_inst.base);
1283 },
1284 }
11631285
1164 const fn_type = try self.emitType(src, module_fn.fn_type);1286 const fn_type = try self.emitType(src, module_fn.fn_type);
11651287
...@@ -1182,6 +1304,33 @@ const EmitZIR = struct {...@@ -1182,6 +1304,33 @@ const EmitZIR = struct {
1182 try self.decls.append(self.allocator, &fn_inst.base);1304 try self.decls.append(self.allocator, &fn_inst.base);
1183 return &fn_inst.base;1305 return &fn_inst.base;
1184 },1306 },
1307 .Array => {
1308 // TODO more checks to make sure this can be emitted as a string literal
1309 //const array_elem_type = ptr_elem_type.elemType();
1310 //if (array_elem_type.eql(Type.initTag(.u8)) and
1311 // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
1312 //{
1313 //}
1314 const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
1315 error.AnalysisFail => unreachable,
1316 else => |e| return e,
1317 };
1318 const str_inst = try self.arena.allocator.create(Inst.Str);
1319 str_inst.* = .{
1320 .base = .{
1321 .name = try self.autoName(),
1322 .src = src,
1323 .tag = Inst.Str.base_tag,
1324 },
1325 .positionals = .{
1326 .bytes = bytes,
1327 },
1328 .kw_args = .{},
1329 };
1330 try self.decls.append(self.allocator, &str_inst.base);
1331 return &str_inst.base;
1332 },
1333 .Void => return self.emitPrimitive(src, .void_value),
1185 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),1334 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
1186 }1335 }
1187 }1336 }
...@@ -1395,30 +1544,30 @@ const EmitZIR = struct {...@@ -1395,30 +1544,30 @@ const EmitZIR = struct {
13951544
1396 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {1545 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
1397 switch (ty.tag()) {1546 switch (ty.tag()) {
1398 .isize => return self.emitPrimitiveType(src, .isize),1547 .isize => return self.emitPrimitive(src, .isize),
1399 .usize => return self.emitPrimitiveType(src, .usize),1548 .usize => return self.emitPrimitive(src, .usize),
1400 .c_short => return self.emitPrimitiveType(src, .c_short),1549 .c_short => return self.emitPrimitive(src, .c_short),
1401 .c_ushort => return self.emitPrimitiveType(src, .c_ushort),1550 .c_ushort => return self.emitPrimitive(src, .c_ushort),
1402 .c_int => return self.emitPrimitiveType(src, .c_int),1551 .c_int => return self.emitPrimitive(src, .c_int),
1403 .c_uint => return self.emitPrimitiveType(src, .c_uint),1552 .c_uint => return self.emitPrimitive(src, .c_uint),
1404 .c_long => return self.emitPrimitiveType(src, .c_long),1553 .c_long => return self.emitPrimitive(src, .c_long),
1405 .c_ulong => return self.emitPrimitiveType(src, .c_ulong),1554 .c_ulong => return self.emitPrimitive(src, .c_ulong),
1406 .c_longlong => return self.emitPrimitiveType(src, .c_longlong),1555 .c_longlong => return self.emitPrimitive(src, .c_longlong),
1407 .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong),1556 .c_ulonglong => return self.emitPrimitive(src, .c_ulonglong),
1408 .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble),1557 .c_longdouble => return self.emitPrimitive(src, .c_longdouble),
1409 .c_void => return self.emitPrimitiveType(src, .c_void),1558 .c_void => return self.emitPrimitive(src, .c_void),
1410 .f16 => return self.emitPrimitiveType(src, .f16),1559 .f16 => return self.emitPrimitive(src, .f16),
1411 .f32 => return self.emitPrimitiveType(src, .f32),1560 .f32 => return self.emitPrimitive(src, .f32),
1412 .f64 => return self.emitPrimitiveType(src, .f64),1561 .f64 => return self.emitPrimitive(src, .f64),
1413 .f128 => return self.emitPrimitiveType(src, .f128),1562 .f128 => return self.emitPrimitive(src, .f128),
1414 .anyerror => return self.emitPrimitiveType(src, .anyerror),1563 .anyerror => return self.emitPrimitive(src, .anyerror),
1415 else => switch (ty.zigTypeTag()) {1564 else => switch (ty.zigTypeTag()) {
1416 .Bool => return self.emitPrimitiveType(src, .bool),1565 .Bool => return self.emitPrimitive(src, .bool),
1417 .Void => return self.emitPrimitiveType(src, .void),1566 .Void => return self.emitPrimitive(src, .void),
1418 .NoReturn => return self.emitPrimitiveType(src, .noreturn),1567 .NoReturn => return self.emitPrimitive(src, .noreturn),
1419 .Type => return self.emitPrimitiveType(src, .type),1568 .Type => return self.emitPrimitive(src, .type),
1420 .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int),1569 .ComptimeInt => return self.emitPrimitive(src, .comptime_int),
1421 .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float),1570 .ComptimeFloat => return self.emitPrimitive(src, .comptime_float),
1422 .Fn => {1571 .Fn => {
1423 const param_types = try self.allocator.alloc(Type, ty.fnParamLen());1572 const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
1424 defer self.allocator.free(param_types);1573 defer self.allocator.free(param_types);
...@@ -1453,24 +1602,36 @@ const EmitZIR = struct {...@@ -1453,24 +1602,36 @@ const EmitZIR = struct {
1453 }1602 }
14541603
1455 fn autoName(self: *EmitZIR) ![]u8 {1604 fn autoName(self: *EmitZIR) ![]u8 {
1456 return std.fmt.allocPrint(&self.arena.allocator, "{}", .{self.decls.items.len});1605 while (true) {
1606 const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name});
1607 self.next_auto_name += 1;
1608 const gop = try self.names.getOrPut(proposed_name);
1609 if (!gop.found_existing) {
1610 gop.kv.value = {};
1611 return proposed_name;
1612 }
1613 }
1457 }1614 }
14581615
1459 fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {1616 fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Inst {
1460 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);1617 const gop = try self.primitive_table.getOrPut(tag);
1461 primitive_inst.* = .{1618 if (!gop.found_existing) {
1462 .base = .{1619 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
1463 .name = try self.autoName(),1620 primitive_inst.* = .{
1464 .src = src,1621 .base = .{
1465 .tag = Inst.Primitive.base_tag,1622 .name = try self.autoName(),
1466 },1623 .src = src,
1467 .positionals = .{1624 .tag = Inst.Primitive.base_tag,
1468 .tag = tag,1625 },
1469 },1626 .positionals = .{
1470 .kw_args = .{},1627 .tag = tag,
1471 };1628 },
1472 try self.decls.append(self.allocator, &primitive_inst.base);1629 .kw_args = .{},
1473 return &primitive_inst.base;1630 };
1631 try self.decls.append(self.allocator, &primitive_inst.base);
1632 gop.kv.value = &primitive_inst.base;
1633 }
1634 return gop.kv.value;
1474 }1635 }
14751636
1476 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {1637 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
test/stage2/zir.zig+142-12
...@@ -21,14 +21,17 @@ pub fn addCases(ctx: *TestContext) void {...@@ -21,14 +21,17 @@ pub fn addCases(ctx: *TestContext) void {
21 \\ %11 = return()21 \\ %11 = return()
22 \\})22 \\})
23 ,23 ,
24 \\@0 = primitive(void)24 \\@void = primitive(void)
25 \\@1 = fntype([], @0, cc=C)25 \\@fnty = fntype([], @void, cc=C)
26 \\@2 = fn(@1, {26 \\@9 = str("entry")
27 \\@10 = ref(@9)
28 \\@unnamed$6 = str("entry")
29 \\@unnamed$7 = ref(@unnamed$6)
30 \\@unnamed$8 = export(@unnamed$7, @entry)
31 \\@unnamed$10 = fntype([], @void, cc=C)
32 \\@entry = fn(@unnamed$10, {
27 \\ %0 = return()33 \\ %0 = return()
28 \\})34 \\})
29 \\@3 = str("entry")
30 \\@4 = ref(@3)
31 \\@5 = export(@4, @2)
32 \\35 \\
33 );36 );
34 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,37 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
...@@ -68,17 +71,144 @@ pub fn addCases(ctx: *TestContext) void {...@@ -68,17 +71,144 @@ pub fn addCases(ctx: *TestContext) void {
68 \\@10 = ref(@9)71 \\@10 = ref(@9)
69 \\@11 = export(@10, @entry)72 \\@11 = export(@10, @entry)
70 ,73 ,
71 \\@0 = primitive(void)74 \\@void = primitive(void)
72 \\@1 = fntype([], @0, cc=C)75 \\@fnty = fntype([], @void, cc=C)
73 \\@2 = fn(@1, {76 \\@0 = int(0)
77 \\@1 = int(1)
78 \\@2 = int(2)
79 \\@3 = int(3)
80 \\@unnamed$7 = fntype([], @void, cc=C)
81 \\@entry = fn(@unnamed$7, {
74 \\ %0 = return()82 \\ %0 = return()
75 \\})83 \\})
76 \\@3 = str("entry")84 \\@a = str("2\x08\x01\n")
77 \\@4 = ref(@3)85 \\@9 = str("entry")
78 \\@5 = export(@4, @2)86 \\@10 = ref(@9)
87 \\@unnamed$14 = str("entry")
88 \\@unnamed$15 = ref(@unnamed$14)
89 \\@unnamed$16 = export(@unnamed$15, @entry)
79 \\90 \\
80 );91 );
8192
93 {
94 var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64);
95 case.addZIR(
96 \\@void = primitive(void)
97 \\@fnty = fntype([], @void, cc=C)
98 \\
99 \\@9 = str("entry")
100 \\@10 = ref(@9)
101 \\@11 = export(@10, @entry)
102 \\
103 \\@entry = fn(@fnty, {
104 \\ %0 = call(@a, [])
105 \\ %1 = return()
106 \\})
107 \\
108 \\@a = fn(@fnty, {
109 \\ %0 = call(@b, [])
110 \\ %1 = return()
111 \\})
112 \\
113 \\@b = fn(@fnty, {
114 \\ %0 = call(@a, [])
115 \\ %1 = return()
116 \\})
117 ,
118 \\@void = primitive(void)
119 \\@fnty = fntype([], @void, cc=C)
120 \\@9 = str("entry")
121 \\@10 = ref(@9)
122 \\@unnamed$6 = str("entry")
123 \\@unnamed$7 = ref(@unnamed$6)
124 \\@unnamed$8 = export(@unnamed$7, @entry)
125 \\@unnamed$12 = fntype([], @void, cc=C)
126 \\@entry = fn(@unnamed$12, {
127 \\ %0 = call(@a, [], modifier=auto)
128 \\ %1 = return()
129 \\})
130 \\@unnamed$17 = fntype([], @void, cc=C)
131 \\@a = fn(@unnamed$17, {
132 \\ %0 = call(@b, [], modifier=auto)
133 \\ %1 = return()
134 \\})
135 \\@unnamed$22 = fntype([], @void, cc=C)
136 \\@b = fn(@unnamed$22, {
137 \\ %0 = call(@a, [], modifier=auto)
138 \\ %1 = return()
139 \\})
140 \\
141 );
142 // Now we introduce a compile error
143 case.addError(
144 \\@void = primitive(void)
145 \\@fnty = fntype([], @void, cc=C)
146 \\
147 \\@9 = str("entry")
148 \\@10 = ref(@9)
149 \\@11 = export(@10, @entry)
150 \\
151 \\@entry = fn(@fnty, {
152 \\ %0 = call(@a, [])
153 \\ %1 = return()
154 \\})
155 \\
156 \\@a = fn(@fnty, {
157 \\ %0 = call(@b, [])
158 \\ %1 = return()
159 \\})
160 \\
161 \\@b = fn(@fnty, {
162 \\ %9 = compileerror("message")
163 \\ %0 = call(@a, [])
164 \\ %1 = return()
165 \\})
166 ,
167 &[_][]const u8{
168 ":19:21: error: message",
169 },
170 );
171 // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
172 // referencing either of them. This tests that the cycle is detected, and the error
173 // goes away.
174 case.addZIR(
175 \\@void = primitive(void)
176 \\@fnty = fntype([], @void, cc=C)
177 \\
178 \\@9 = str("entry")
179 \\@10 = ref(@9)
180 \\@11 = export(@10, @entry)
181 \\
182 \\@entry = fn(@fnty, {
183 \\ %1 = return()
184 \\})
185 \\
186 \\@a = fn(@fnty, {
187 \\ %0 = call(@b, [])
188 \\ %1 = return()
189 \\})
190 \\
191 \\@b = fn(@fnty, {
192 \\ %9 = compileerror("message")
193 \\ %0 = call(@a, [])
194 \\ %1 = return()
195 \\})
196 ,
197 \\@void = primitive(void)
198 \\@fnty = fntype([], @void, cc=C)
199 \\@9 = str("entry")
200 \\@10 = ref(@9)
201 \\@unnamed$6 = str("entry")
202 \\@unnamed$7 = ref(@unnamed$6)
203 \\@unnamed$8 = export(@unnamed$7, @entry)
204 \\@unnamed$10 = fntype([], @void, cc=C)
205 \\@entry = fn(@unnamed$10, {
206 \\ %0 = return()
207 \\})
208 \\
209 );
210 }
211
82 if (std.Target.current.os.tag != .linux or212 if (std.Target.current.os.tag != .linux or
83 std.Target.current.cpu.arch != .x86_64)213 std.Target.current.cpu.arch != .x86_64)
84 {214 {