Commit 47090d234e
Changed files (3)
src-self-hosted
test
stage2
src-self-hosted/Module.zig
@@ -673,8 +673,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
assert(errors.items.len == self.totalErrorCount());
return AllErrors{
- .arena = arena.state,
.list = try arena.allocator.dupe(AllErrors.Message, errors.items),
+ .arena = arena.state,
};
}
@@ -935,7 +935,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
}
}
if (self.failed_decls.remove(decl)) |entry| {
- self.allocator.destroy(entry.value);
+ entry.value.destroy(self.allocator);
}
self.deleteDeclExports(decl);
self.bin_file.freeDecl(decl);
@@ -1104,7 +1104,7 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
//std.debug.warn("mark {} outdated\n", .{decl.name});
try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
if (self.failed_decls.remove(decl)) |entry| {
- self.allocator.destroy(entry.value);
+ entry.value.destroy(self.allocator);
}
decl.analysis = .outdated;
}
src-self-hosted/test.zig
@@ -27,9 +27,32 @@ pub const TestContext = struct {
pub const ZIRTransformCase = struct {
name: []const u8,
- src: [:0]const u8,
- expected_zir: []const u8,
cross_target: std.zig.CrossTarget,
+ updates: std.ArrayList(Update),
+
+ pub const Update = struct {
+ expected: Expected,
+ src: [:0]const u8,
+ };
+
+ pub const Expected = union(enum) {
+ zir: []const u8,
+ errors: []const []const u8,
+ };
+
+ pub fn addZIR(case: *ZIRTransformCase, src: [:0]const u8, zir_text: []const u8) void {
+ case.updates.append(.{
+ .src = src,
+ .expected = .{ .zir = zir_text },
+ }) catch unreachable;
+ }
+
+ pub fn addError(case: *ZIRTransformCase, src: [:0]const u8, errors: []const []const u8) void {
+ case.updates.append(.{
+ .src = src,
+ .expected = .{ .errors = errors },
+ }) catch unreachable;
+ }
};
pub fn addZIRCompareOutput(
@@ -52,14 +75,32 @@ pub const TestContext = struct {
src: [:0]const u8,
expected_zir: []const u8,
) void {
- ctx.zir_transform_cases.append(.{
+ const case = ctx.zir_transform_cases.addOne() catch unreachable;
+ case.* = .{
.name = name,
- .src = src,
- .expected_zir = expected_zir,
.cross_target = cross_target,
+ .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),
+ };
+ case.updates.append(.{
+ .src = src,
+ .expected = .{ .zir = expected_zir },
}) catch unreachable;
}
+ pub fn addZIRMulti(
+ ctx: *TestContext,
+ name: []const u8,
+ cross_target: std.zig.CrossTarget,
+ ) *ZIRTransformCase {
+ const case = ctx.zir_transform_cases.addOne() catch unreachable;
+ case.* = .{
+ .name = name,
+ .cross_target = cross_target,
+ .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),
+ };
+ return case;
+ }
+
fn init(self: *TestContext) !void {
self.* = .{
.zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
@@ -178,13 +219,11 @@ pub const TestContext = struct {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
- var prg_node = root_node.start(case.name, 3);
- prg_node.activate();
- defer prg_node.end();
+ var update_node = root_node.start(case.name, case.updates.items.len);
+ update_node.activate();
+ defer update_node.end();
const tmp_src_path = "test-case.zir";
- try tmp.dir.writeFile(tmp_src_path, case.src);
-
const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
defer root_pkg.destroy();
@@ -198,25 +237,68 @@ pub const TestContext = struct {
});
defer module.deinit();
- var module_node = prg_node.start("parse/analysis/codegen", null);
- module_node.activate();
- try module.update();
- module_node.end();
-
- var emit_node = prg_node.start("emit", null);
- emit_node.activate();
- var new_zir_module = try zir.emit(allocator, module);
- defer new_zir_module.deinit(allocator);
- emit_node.end();
-
- var write_node = prg_node.start("write", null);
- write_node.activate();
- var out_zir = std.ArrayList(u8).init(allocator);
- defer out_zir.deinit();
- try new_zir_module.writeToStream(allocator, out_zir.outStream());
- write_node.end();
-
- std.testing.expectEqualSlices(u8, case.expected_zir, out_zir.items);
+ for (case.updates.items) |update| {
+ var prg_node = update_node.start("", 3);
+ prg_node.activate();
+ defer prg_node.end();
+
+ try tmp.dir.writeFile(tmp_src_path, update.src);
+
+ var module_node = prg_node.start("parse/analysis/codegen", null);
+ module_node.activate();
+ try module.update();
+ module_node.end();
+
+ switch (update.expected) {
+ .zir => |expected_zir| {
+ var emit_node = prg_node.start("emit", null);
+ emit_node.activate();
+ var new_zir_module = try zir.emit(allocator, module);
+ defer new_zir_module.deinit(allocator);
+ emit_node.end();
+
+ var write_node = prg_node.start("write", null);
+ write_node.activate();
+ var out_zir = std.ArrayList(u8).init(allocator);
+ defer out_zir.deinit();
+ try new_zir_module.writeToStream(allocator, out_zir.outStream());
+ write_node.end();
+
+ std.testing.expectEqualSlices(u8, expected_zir, out_zir.items);
+ },
+ .errors => |expected_errors| {
+ var all_errors = try module.getAllErrorsAlloc();
+ defer all_errors.deinit(module.allocator);
+ for (expected_errors) |expected_error| {
+ for (all_errors.list) |full_err_msg| {
+ const text = try std.fmt.allocPrint(allocator, ":{}:{}: error: {}", .{
+ full_err_msg.line + 1,
+ full_err_msg.column + 1,
+ full_err_msg.msg,
+ });
+ defer allocator.free(text);
+ if (std.mem.eql(u8, text, expected_error)) {
+ break;
+ }
+ } else {
+ std.debug.warn(
+ "{}\nExpected this error:\n================\n{}\n================\nBut found these errors:\n================\n",
+ .{ case.name, expected_error },
+ );
+ for (all_errors.list) |full_err_msg| {
+ std.debug.warn(":{}:{}: error: {}\n", .{
+ full_err_msg.line + 1,
+ full_err_msg.column + 1,
+ full_err_msg.msg,
+ });
+ }
+ std.debug.warn("================\nTest failed\n", .{});
+ std.process.exit(1);
+ }
+ }
+ },
+ }
+ }
}
};
test/stage2/zir.zig
@@ -90,6 +90,125 @@ pub fn addCases(ctx: *TestContext) void {
\\
);
+ {
+ var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64);
+ case.addZIR(
+ \\@void = primitive(void)
+ \\@fnty = fntype([], @void, cc=C)
+ \\
+ \\@9 = str("entry")
+ \\@10 = ref(@9)
+ \\@11 = export(@10, @entry)
+ \\
+ \\@entry = fn(@fnty, {
+ \\ %0 = call(@a, [])
+ \\ %1 = return()
+ \\})
+ \\
+ \\@a = fn(@fnty, {
+ \\ %0 = call(@b, [])
+ \\ %1 = return()
+ \\})
+ \\
+ \\@b = fn(@fnty, {
+ \\ %0 = call(@a, [])
+ \\ %1 = return()
+ \\})
+ ,
+ \\@void = primitive(void)
+ \\@fnty = fntype([], @void, cc=C)
+ \\@9 = str("entry")
+ \\@10 = ref(@9)
+ \\@unnamed$6 = str("entry")
+ \\@unnamed$7 = ref(@unnamed$6)
+ \\@unnamed$8 = export(@unnamed$7, @entry)
+ \\@unnamed$12 = fntype([], @void, cc=C)
+ \\@entry = fn(@unnamed$12, {
+ \\ %0 = call(@a, [], modifier=auto)
+ \\ %1 = return()
+ \\})
+ \\@unnamed$17 = fntype([], @void, cc=C)
+ \\@a = fn(@unnamed$17, {
+ \\ %0 = call(@b, [], modifier=auto)
+ \\ %1 = return()
+ \\})
+ \\@unnamed$22 = fntype([], @void, cc=C)
+ \\@b = fn(@unnamed$22, {
+ \\ %0 = call(@a, [], modifier=auto)
+ \\ %1 = return()
+ \\})
+ \\
+ );
+ // Now we introduce a compile error
+ case.addError(
+ \\@void = primitive(void)
+ \\@fnty = fntype([], @void, cc=C)
+ \\
+ \\@9 = str("entry")
+ \\@10 = ref(@9)
+ \\@11 = export(@10, @entry)
+ \\
+ \\@entry = fn(@fnty, {
+ \\ %0 = call(@a, [])
+ \\ %1 = return()
+ \\})
+ \\
+ \\@a = fn(@fnty, {
+ \\ %0 = call(@b, [])
+ \\ %1 = return()
+ \\})
+ \\
+ \\@b = fn(@fnty, {
+ \\ %9 = compileerror("message")
+ \\ %0 = call(@a, [])
+ \\ %1 = return()
+ \\})
+ ,
+ &[_][]const u8{
+ ":19:21: error: message",
+ },
+ );
+ // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
+ // referencing either of them. This tests that the cycle is detected, and the error
+ // goes away.
+ case.addZIR(
+ \\@void = primitive(void)
+ \\@fnty = fntype([], @void, cc=C)
+ \\
+ \\@9 = str("entry")
+ \\@10 = ref(@9)
+ \\@11 = export(@10, @entry)
+ \\
+ \\@entry = fn(@fnty, {
+ \\ %1 = return()
+ \\})
+ \\
+ \\@a = fn(@fnty, {
+ \\ %0 = call(@b, [])
+ \\ %1 = return()
+ \\})
+ \\
+ \\@b = fn(@fnty, {
+ \\ %9 = compileerror("message")
+ \\ %0 = call(@a, [])
+ \\ %1 = return()
+ \\})
+ ,
+ \\@void = primitive(void)
+ \\@fnty = fntype([], @void, cc=C)
+ \\@9 = str("entry")
+ \\@10 = ref(@9)
+ \\@unnamed$6 = str("entry")
+ \\@unnamed$7 = ref(@unnamed$6)
+ \\@unnamed$8 = export(@unnamed$7, @entry)
+ \\@unnamed$10 = fntype([], @void, cc=C)
+ \\@entry = fn(@unnamed$10, {
+ \\ %0 = return()
+ \\})
+ \\
+ );
+ }
+
if (std.Target.current.os.tag != .linux or
std.Target.current.cpu.arch != .x86_64)
{