authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-08 15:15:55-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-08 15:16:40-04:00
log47090d234ecc3e50937c918b05e6f039a53d880c
tree2870ab3c20a78ca1e2115442f9ffdb74ddf98675
parent9ea4965ceb62ea47569d0676bb68475304aad467

stage2: add passing test for compile error in unreferenced cycle


3 files changed, 233 insertions(+), 32 deletions(-)

src-self-hosted/Module.zig+3-3
......@@ -673,8 +673,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
673673 assert(errors.items.len == self.totalErrorCount());
674674
675675 return AllErrors{
676 .arena = arena.state,
677676 .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
677 .arena = arena.state,
678678 };
679679}
680680
......@@ -935,7 +935,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
935935 }
936936 }
937937 if (self.failed_decls.remove(decl)) |entry| {
938 self.allocator.destroy(entry.value);
938 entry.value.destroy(self.allocator);
939939 }
940940 self.deleteDeclExports(decl);
941941 self.bin_file.freeDecl(decl);
......@@ -1104,7 +1104,7 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
11041104 //std.debug.warn("mark {} outdated\n", .{decl.name});
11051105 try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
11061106 if (self.failed_decls.remove(decl)) |entry| {
1107 self.allocator.destroy(entry.value);
1107 entry.value.destroy(self.allocator);
11081108 }
11091109 decl.analysis = .outdated;
11101110}
src-self-hosted/test.zig+111-29
......@@ -27,9 +27,32 @@ pub const TestContext = struct {
2727
2828 pub const ZIRTransformCase = struct {
2929 name: []const u8,
30 src: [:0]const u8,
31 expected_zir: []const u8,
3230 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 }
3356 };
3457
3558 pub fn addZIRCompareOutput(
......@@ -52,14 +75,32 @@ pub const TestContext = struct {
5275 src: [:0]const u8,
5376 expected_zir: []const u8,
5477 ) void {
55 ctx.zir_transform_cases.append(.{
78 const case = ctx.zir_transform_cases.addOne() catch unreachable;
79 case.* = .{
5680 .name = name,
57 .src = src,
58 .expected_zir = expected_zir,
5981 .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 },
6087 }) catch unreachable;
6188 }
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
63104 fn init(self: *TestContext) !void {
64105 self.* = .{
65106 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
......@@ -178,13 +219,11 @@ pub const TestContext = struct {
178219 var tmp = std.testing.tmpDir(.{});
179220 defer tmp.cleanup();
180221
181 var prg_node = root_node.start(case.name, 3);
182 prg_node.activate();
183 defer prg_node.end();
222 var update_node = root_node.start(case.name, case.updates.items.len);
223 update_node.activate();
224 defer update_node.end();
184225
185226 const tmp_src_path = "test-case.zir";
186 try tmp.dir.writeFile(tmp_src_path, case.src);
187
188227 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
189228 defer root_pkg.destroy();
190229
......@@ -198,25 +237,68 @@ pub const TestContext = struct {
198237 });
199238 defer module.deinit();
200239
201 var module_node = prg_node.start("parse/analysis/codegen", null);
202 module_node.activate();
203 try module.update();
204 module_node.end();
205
206 var emit_node = prg_node.start("emit", null);
207 emit_node.activate();
208 var new_zir_module = try zir.emit(allocator, module);
209 defer new_zir_module.deinit(allocator);
210 emit_node.end();
211
212 var write_node = prg_node.start("write", null);
213 write_node.activate();
214 var out_zir = std.ArrayList(u8).init(allocator);
215 defer out_zir.deinit();
216 try new_zir_module.writeToStream(allocator, out_zir.outStream());
217 write_node.end();
218
219 std.testing.expectEqualSlices(u8, case.expected_zir, out_zir.items);
240 for (case.updates.items) |update| {
241 var prg_node = update_node.start("", 3);
242 prg_node.activate();
243 defer prg_node.end();
244
245 try tmp.dir.writeFile(tmp_src_path, update.src);
246
247 var module_node = prg_node.start("parse/analysis/codegen", null);
248 module_node.activate();
249 try module.update();
250 module_node.end();
251
252 switch (update.expected) {
253 .zir => |expected_zir| {
254 var emit_node = prg_node.start("emit", null);
255 emit_node.activate();
256 var new_zir_module = try zir.emit(allocator, module);
257 defer new_zir_module.deinit(allocator);
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 }
220302 }
221303};
222304
test/stage2/zir.zig+119
......@@ -90,6 +90,125 @@ pub fn addCases(ctx: *TestContext) void {
9090 \\
9191 );
9292
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
93212 if (std.Target.current.os.tag != .linux or
94213 std.Target.current.cpu.arch != .x86_64)
95214 {