| author | |
| committer | |
| log | 47090d234ecc3e50937c918b05e6f039a53d880c |
| tree | 2870ab3c20a78ca1e2115442f9ffdb74ddf98675 |
| parent | 9ea4965ceb62ea47569d0676bb68475304aad467 |
3 files changed, 233 insertions(+), 32 deletions(-)
src-self-hosted/Module.zig+3-3| ... | @@ -673,8 +673,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | ... | @@ -673,8 +673,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 673 | assert(errors.items.len == self.totalErrorCount()); | 673 | assert(errors.items.len == self.totalErrorCount()); |
| 674 | 674 | ||
| 675 | return AllErrors{ | 675 | return AllErrors{ |
| 676 | .arena = arena.state, | ||
| 677 | .list = try arena.allocator.dupe(AllErrors.Message, errors.items), | 676 | .list = try arena.allocator.dupe(AllErrors.Message, errors.items), |
| 677 | .arena = arena.state, | ||
| 678 | }; | 678 | }; |
| 679 | } | 679 | } |
| 680 | 680 | ||
| ... | @@ -935,7 +935,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void { | ... | @@ -935,7 +935,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 935 | } | 935 | } |
| 936 | } | 936 | } |
| 937 | if (self.failed_decls.remove(decl)) |entry| { | 937 | if (self.failed_decls.remove(decl)) |entry| { |
| 938 | self.allocator.destroy(entry.value); | 938 | entry.value.destroy(self.allocator); |
| 939 | } | 939 | } |
| 940 | self.deleteDeclExports(decl); | 940 | self.deleteDeclExports(decl); |
| 941 | self.bin_file.freeDecl(decl); | 941 | self.bin_file.freeDecl(decl); |
| ... | @@ -1104,7 +1104,7 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | ... | @@ -1104,7 +1104,7 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void { |
| 1104 | //std.debug.warn("mark {} outdated\n", .{decl.name}); | 1104 | //std.debug.warn("mark {} outdated\n", .{decl.name}); |
| 1105 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); | 1105 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); |
| 1106 | if (self.failed_decls.remove(decl)) |entry| { | 1106 | if (self.failed_decls.remove(decl)) |entry| { |
| 1107 | self.allocator.destroy(entry.value); | 1107 | entry.value.destroy(self.allocator); |
| 1108 | } | 1108 | } |
| 1109 | decl.analysis = .outdated; | 1109 | decl.analysis = .outdated; |
| 1110 | } | 1110 | } |
src-self-hosted/test.zig+111-29| ... | @@ -27,9 +27,32 @@ pub const TestContext = struct { | ... | @@ -27,9 +27,32 @@ pub const TestContext = struct { |
| 27 | 27 | ||
| 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 | }; |
| 34 | 57 | ||
| 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 | } |
| 62 | 89 | ||
| 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(); |
| 180 | 221 | ||
| 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(); |
| 184 | 225 | ||
| 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(); |
| 190 | 229 | ||
| ... | @@ -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(); |
| 200 | 239 | ||
| 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(); |
| 205 | 244 | ||
| 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(); |
| 211 | 250 | 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); |
| 218 | 257 | 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 | }; |
| 222 | 304 |
test/stage2/zir.zig+119| ... | @@ -90,6 +90,125 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -90,6 +90,125 @@ pub fn addCases(ctx: *TestContext) void { |
| 90 | \\ | 90 | \\ |
| 91 | ); | 91 | ); |
| 92 | 92 | ||
| 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 | |||
| 93 | if (std.Target.current.os.tag != .linux or | 212 | if (std.Target.current.os.tag != .linux or |
| 94 | std.Target.current.cpu.arch != .x86_64) | 213 | std.Target.current.cpu.arch != .x86_64) |
| 95 | { | 214 | { |