aboutsummaryrefslogtreecommitdiff
path: root/src-self-hosted
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2018-07-11 00:50:17 -0400
committerGitHub <noreply@github.com>2018-07-11 00:50:17 -0400
commitc620a1fe3d40d25d6b6654a4c828e8d7d7e37c38 (patch)
tree4cab564fe918fcc167bfcd52c3a619607f0fc364 /src-self-hosted
parent8fba0a6ae862993afa2aeca774347adc399b3605 (diff)
parent8197a14ceb2938c64526c7b84e2ac8da343960fa (diff)
downloadzig-c620a1fe3d40d25d6b6654a4c828e8d7d7e37c38.tar.gz
zig-c620a1fe3d40d25d6b6654a4c828e8d7d7e37c38.zip
Merge pull request #1215 from ziglang/self-hosted-first-test
self-hosted: first passing test
Diffstat (limited to 'src-self-hosted')
-rw-r--r--src-self-hosted/errmsg.zig18
-rw-r--r--src-self-hosted/introspect.zig5
-rw-r--r--src-self-hosted/main.zig36
-rw-r--r--src-self-hosted/module.zig98
-rw-r--r--src-self-hosted/test.zig164
5 files changed, 280 insertions, 41 deletions
diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig
index b6fd78d8f6..a92b5145ce 100644
--- a/src-self-hosted/errmsg.zig
+++ b/src-self-hosted/errmsg.zig
@@ -11,11 +11,15 @@ pub const Color = enum {
On,
};
+pub const Span = struct {
+ first: ast.TokenIndex,
+ last: ast.TokenIndex,
+};
+
pub const Msg = struct {
path: []const u8,
text: []u8,
- first_token: TokenIndex,
- last_token: TokenIndex,
+ span: Span,
tree: *ast.Tree,
};
@@ -39,8 +43,10 @@ pub fn createFromParseError(
.tree = tree,
.path = path,
.text = text_buf.toOwnedSlice(),
- .first_token = loc_token,
- .last_token = loc_token,
+ .span = Span{
+ .first = loc_token,
+ .last = loc_token,
+ },
});
errdefer allocator.destroy(msg);
@@ -48,8 +54,8 @@ pub fn createFromParseError(
}
pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
- const first_token = msg.tree.tokens.at(msg.first_token);
- const last_token = msg.tree.tokens.at(msg.last_token);
+ const first_token = msg.tree.tokens.at(msg.span.first);
+ const last_token = msg.tree.tokens.at(msg.span.last);
const start_loc = msg.tree.tokenLocationPtr(0, first_token);
const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
if (!color_on) {
diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig
index 74084b48c6..ecd04c4467 100644
--- a/src-self-hosted/introspect.zig
+++ b/src-self-hosted/introspect.zig
@@ -53,3 +53,8 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
return error.ZigLibDirNotFound;
};
}
+
+/// Caller must free result
+pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 {
+ return std.mem.dupe(allocator, u8, "zig-cache");
+}
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index fe94a4460a..d7ead0ba32 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -481,29 +481,29 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
module.link_out_file = flags.single("out-file");
try module.build();
- const process_build_events_handle = try async<loop.allocator> processBuildEvents(module, true);
+ const process_build_events_handle = try async<loop.allocator> processBuildEvents(module, color);
defer cancel process_build_events_handle;
loop.run();
}
-async fn processBuildEvents(module: *Module, watch: bool) void {
- while (watch) {
- // TODO directly awaiting async should guarantee memory allocation elision
- const build_event = await (async module.events.get() catch unreachable);
+async fn processBuildEvents(module: *Module, color: errmsg.Color) void {
+ // TODO directly awaiting async should guarantee memory allocation elision
+ const build_event = await (async module.events.get() catch unreachable);
- switch (build_event) {
- Module.Event.Ok => {
- std.debug.warn("Build succeeded\n");
- return;
- },
- Module.Event.Error => |err| {
- std.debug.warn("build failed: {}\n", @errorName(err));
- @panic("TODO error return trace");
- },
- Module.Event.Fail => |errs| {
- @panic("TODO print compile error messages");
- },
- }
+ switch (build_event) {
+ Module.Event.Ok => {
+ std.debug.warn("Build succeeded\n");
+ return;
+ },
+ Module.Event.Error => |err| {
+ std.debug.warn("build failed: {}\n", @errorName(err));
+ @panic("TODO error return trace");
+ },
+ Module.Event.Fail => |msgs| {
+ for (msgs) |msg| {
+ errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
+ }
+ },
}
}
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
index 24be228eb8..44954e4cd1 100644
--- a/src-self-hosted/module.zig
+++ b/src-self-hosted/module.zig
@@ -89,12 +89,9 @@ pub const Module = struct {
/// the build is complete.
build_group: event.Group(BuildError!void),
- const BuildErrorsList = std.SegmentedList(BuildErrorDesc, 1);
+ compile_errors: event.Locked(CompileErrList),
- pub const BuildErrorDesc = struct {
- code: BuildError,
- text: []const u8,
- };
+ const CompileErrList = std.ArrayList(*errmsg.Msg);
// TODO handle some of these earlier and report them in a way other than error codes
pub const BuildError = error{
@@ -131,11 +128,12 @@ pub const Module = struct {
NoStdHandles,
Overflow,
NotSupported,
+ BufferTooSmall,
};
pub const Event = union(enum) {
Ok,
- Fail: []errmsg.Msg,
+ Fail: []*errmsg.Msg,
Error: BuildError,
};
@@ -249,6 +247,7 @@ pub const Module = struct {
.link_out_file = null,
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
.build_group = event.Group(BuildError!void).init(loop),
+ .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
});
}
@@ -288,7 +287,17 @@ pub const Module = struct {
await (async self.events.put(Event{ .Error = err }) catch unreachable);
return;
};
- await (async self.events.put(Event.Ok) catch unreachable);
+ const compile_errors = blk: {
+ const held = await (async self.compile_errors.acquire() catch unreachable);
+ defer held.release();
+ break :blk held.value.toOwnedSlice();
+ };
+
+ if (compile_errors.len == 0) {
+ await (async self.events.put(Event.Ok) catch unreachable);
+ } else {
+ await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable);
+ }
// for now we stop after 1
return;
}
@@ -310,10 +319,13 @@ pub const Module = struct {
};
errdefer self.a().free(source_code);
- var parsed_file = ParsedFile{
- .tree = try std.zig.parse(self.a(), source_code),
+ const parsed_file = try self.a().create(ParsedFile{
+ .tree = undefined,
.realpath = root_src_real_path,
- };
+ });
+ errdefer self.a().destroy(parsed_file);
+
+ parsed_file.tree = try std.zig.parse(self.a(), source_code);
errdefer parsed_file.tree.deinit();
const tree = &parsed_file.tree;
@@ -337,7 +349,7 @@ pub const Module = struct {
const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
@panic("TODO add compile error");
//try self.addCompileError(
- // &parsed_file,
+ // parsed_file,
// fn_proto.fn_token,
// fn_proto.fn_token + 1,
// "missing function name",
@@ -357,7 +369,7 @@ pub const Module = struct {
});
errdefer self.a().destroy(fn_decl);
- try decl_group.call(addTopLevelDecl, self, tree, &fn_decl.base);
+ try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base);
},
ast.Node.Id.TestDecl => @panic("TODO"),
else => unreachable,
@@ -367,20 +379,56 @@ pub const Module = struct {
try await (async self.build_group.wait() catch unreachable);
}
- async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void {
- const is_export = decl.isExported(tree);
+ async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void {
+ const is_export = decl.isExported(&parsed_file.tree);
if (is_export) {
- try self.build_group.call(verifyUniqueSymbol, self, decl);
+ try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl);
}
}
- async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void {
+ fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: errmsg.Span, comptime fmt: []const u8, args: ...) !void {
+ const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
+ errdefer self.loop.allocator.free(text);
+
+ try self.build_group.call(addCompileErrorAsync, self, parsed_file, span.first, span.last, text);
+ }
+
+ async fn addCompileErrorAsync(
+ self: *Module,
+ parsed_file: *ParsedFile,
+ first_token: ast.TokenIndex,
+ last_token: ast.TokenIndex,
+ text: []u8,
+ ) !void {
+ const msg = try self.loop.allocator.create(errmsg.Msg{
+ .path = parsed_file.realpath,
+ .text = text,
+ .span = errmsg.Span{
+ .first = first_token,
+ .last = last_token,
+ },
+ .tree = &parsed_file.tree,
+ });
+ errdefer self.loop.allocator.destroy(msg);
+
+ const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
+ defer compile_errors.release();
+
+ try compile_errors.value.append(msg);
+ }
+
+ async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void {
const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable);
defer exported_symbol_names.release();
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
- @panic("TODO report compile error");
+ try self.addCompileError(
+ parsed_file,
+ decl.getSpan(),
+ "exported symbol collision: '{}'",
+ decl.name,
+ );
}
}
@@ -503,6 +551,22 @@ pub const Decl = struct {
}
}
+ pub fn getSpan(base: *const Decl) errmsg.Span {
+ switch (base.id) {
+ Id.Fn => {
+ const fn_decl = @fieldParentPtr(Fn, "base", base);
+ const fn_proto = fn_decl.fn_proto;
+ const start = fn_proto.fn_token;
+ const end = fn_proto.name_token orelse start;
+ return errmsg.Span{
+ .first = start,
+ .last = end + 1,
+ };
+ },
+ else => @panic("TODO"),
+ }
+ }
+
pub const Resolution = enum {
Unresolved,
InProgress,
diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig
new file mode 100644
index 0000000000..01a857f21d
--- /dev/null
+++ b/src-self-hosted/test.zig
@@ -0,0 +1,164 @@
+const std = @import("std");
+const mem = std.mem;
+const builtin = @import("builtin");
+const Target = @import("target.zig").Target;
+const Module = @import("module.zig").Module;
+const introspect = @import("introspect.zig");
+const assertOrPanic = std.debug.assertOrPanic;
+const errmsg = @import("errmsg.zig");
+
+test "compile errors" {
+ var ctx: TestContext = undefined;
+ try ctx.init();
+ defer ctx.deinit();
+
+ try ctx.testCompileError(
+ \\export fn entry() void {}
+ \\export fn entry() void {}
+ , file1, 2, 8, "exported symbol collision: 'entry'");
+
+ try ctx.run();
+}
+
+const file1 = "1.zig";
+const allocator = std.heap.c_allocator;
+
+const TestContext = struct {
+ loop: std.event.Loop,
+ zig_lib_dir: []u8,
+ zig_cache_dir: []u8,
+ file_index: std.atomic.Int(usize),
+ group: std.event.Group(error!void),
+ any_err: error!void,
+
+ const tmp_dir_name = "stage2_test_tmp";
+
+ fn init(self: *TestContext) !void {
+ self.* = TestContext{
+ .any_err = {},
+ .loop = undefined,
+ .zig_lib_dir = undefined,
+ .zig_cache_dir = undefined,
+ .group = undefined,
+ .file_index = std.atomic.Int(usize).init(0),
+ };
+
+ try self.loop.initMultiThreaded(allocator);
+ errdefer self.loop.deinit();
+
+ self.group = std.event.Group(error!void).init(&self.loop);
+ errdefer self.group.cancelAll();
+
+ self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
+ errdefer allocator.free(self.zig_lib_dir);
+
+ self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator);
+ errdefer allocator.free(self.zig_cache_dir);
+
+ try std.os.makePath(allocator, tmp_dir_name);
+ errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
+ }
+
+ fn deinit(self: *TestContext) void {
+ std.os.deleteTree(allocator, tmp_dir_name) catch {};
+ allocator.free(self.zig_cache_dir);
+ allocator.free(self.zig_lib_dir);
+ self.loop.deinit();
+ }
+
+ fn run(self: *TestContext) !void {
+ const handle = try self.loop.call(waitForGroup, self);
+ defer cancel handle;
+ self.loop.run();
+ return self.any_err;
+ }
+
+ async fn waitForGroup(self: *TestContext) void {
+ self.any_err = await (async self.group.wait() catch unreachable);
+ }
+
+ fn testCompileError(
+ self: *TestContext,
+ source: []const u8,
+ path: []const u8,
+ line: usize,
+ column: usize,
+ msg: []const u8,
+ ) !void {
+ var file_index_buf: [20]u8 = undefined;
+ const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.next());
+ const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
+
+ if (std.os.path.dirname(file1_path)) |dirname| {
+ try std.os.makePath(allocator, dirname);
+ }
+
+ // TODO async I/O
+ try std.io.writeFile(allocator, file1_path, source);
+
+ var module = try Module.create(
+ &self.loop,
+ "test",
+ file1_path,
+ Target.Native,
+ Module.Kind.Obj,
+ builtin.Mode.Debug,
+ self.zig_lib_dir,
+ self.zig_cache_dir,
+ );
+ errdefer module.destroy();
+
+ try module.build();
+
+ try self.group.call(getModuleEvent, module, source, path, line, column, msg);
+ }
+
+ async fn getModuleEvent(
+ module: *Module,
+ source: []const u8,
+ path: []const u8,
+ line: usize,
+ column: usize,
+ text: []const u8,
+ ) !void {
+ defer module.destroy();
+ const build_event = await (async module.events.get() catch unreachable);
+
+ switch (build_event) {
+ Module.Event.Ok => {
+ @panic("build incorrectly succeeded");
+ },
+ Module.Event.Error => |err| {
+ @panic("build incorrectly failed");
+ },
+ Module.Event.Fail => |msgs| {
+ assertOrPanic(msgs.len != 0);
+ for (msgs) |msg| {
+ if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {
+ const first_token = msg.tree.tokens.at(msg.span.first);
+ const last_token = msg.tree.tokens.at(msg.span.first);
+ const start_loc = msg.tree.tokenLocationPtr(0, first_token);
+ if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
+ return;
+ }
+ }
+ }
+ std.debug.warn(
+ "\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n",
+ source,
+ path,
+ line,
+ column,
+ text,
+ );
+ std.debug.warn("\n====found:========\n");
+ var stderr = try std.io.getStdErr();
+ for (msgs) |msg| {
+ try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
+ }
+ std.debug.warn("============\n");
+ return error.TestFailed;
+ },
+ }
+ }
+};