authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-23 14:28:14-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-23 14:28:14-04:00
log10d2f08d376a302bd79e87d17c06922d3145a939
tree1251086911401d33cda814ad87d0fce50da70184
parentd767fae47e89aef53505191cf11ce7e592a784b2

self-hosted: fix error messages not cleaning up correctly


6 files changed, 255 insertions(+), 112 deletions(-)

src-self-hosted/compilation.zig+30-29
......@@ -24,6 +24,7 @@ const Visib = @import("visib.zig").Visib;
2424const Value = @import("value.zig").Value;
2525const Type = Value.Type;
2626const Span = errmsg.Span;
27const Msg = errmsg.Msg;
2728const codegen = @import("codegen.zig");
2829const Package = @import("package.zig").Package;
2930const link = @import("link.zig").link;
......@@ -40,8 +41,6 @@ pub const EventLoopLocal = struct {
4041
4142 native_libc: event.Future(LibCInstallation),
4243
43 cwd: []const u8,
44
4544 var lazy_init_targets = std.lazyInit(void);
4645
4746 fn init(loop: *event.Loop) !EventLoopLocal {
......@@ -54,21 +53,16 @@ pub const EventLoopLocal = struct {
5453 try std.os.getRandomBytes(seed_bytes[0..]);
5554 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
5655
57 const cwd = try os.getCwd(loop.allocator);
58 errdefer loop.allocator.free(cwd);
59
6056 return EventLoopLocal{
6157 .loop = loop,
6258 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
6359 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
6460 .native_libc = event.Future(LibCInstallation).init(loop),
65 .cwd = cwd,
6661 };
6762 }
6863
6964 /// Must be called only after EventLoop.run completes.
7065 fn deinit(self: *EventLoopLocal) void {
71 self.loop.allocator.free(self.cwd);
7266 while (self.llvm_handle_pool.pop()) |node| {
7367 c.LLVMContextDispose(node.data);
7468 self.loop.allocator.destroy(node);
......@@ -234,7 +228,7 @@ pub const Compilation = struct {
234228 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
235229 const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
236230
237 const CompileErrList = std.ArrayList(*errmsg.Msg);
231 const CompileErrList = std.ArrayList(*Msg);
238232
239233 // TODO handle some of these earlier and report them in a way other than error codes
240234 pub const BuildError = error{
......@@ -286,7 +280,7 @@ pub const Compilation = struct {
286280 pub const Event = union(enum) {
287281 Ok,
288282 Error: BuildError,
289 Fail: []*errmsg.Msg,
283 Fail: []*Msg,
290284 };
291285
292286 pub const DarwinVersionMin = union(enum) {
......@@ -761,21 +755,35 @@ pub const Compilation = struct {
761755 };
762756 errdefer self.gpa().free(source_code);
763757
764 var tree = try std.zig.parse(self.gpa(), source_code);
765 errdefer tree.deinit();
758 const tree = try self.gpa().createOne(ast.Tree);
759 tree.* = try std.zig.parse(self.gpa(), source_code);
760 errdefer {
761 tree.deinit();
762 self.gpa().destroy(tree);
763 }
766764
767765 break :blk try Scope.Root.create(self, tree, root_src_real_path);
768766 };
769767 defer root_scope.base.deref(self);
768 const tree = root_scope.tree;
769
770 var error_it = tree.errors.iterator(0);
771 while (error_it.next()) |parse_error| {
772 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
773 errdefer msg.destroy();
770774
771 const tree = &root_scope.tree;
775 try await (async self.addCompileErrorAsync(msg) catch unreachable);
776 }
777 if (tree.errors.len != 0) {
778 return;
779 }
772780
773781 const decls = try Scope.Decls.create(self, &root_scope.base);
774782 defer decls.base.deref(self);
775783
776784 var decl_group = event.Group(BuildError!void).init(self.loop);
777 // TODO https://github.com/ziglang/zig/issues/1261
778 //errdefer decl_group.cancelAll();
785 var decl_group_consumed = false;
786 errdefer if (!decl_group_consumed) decl_group.cancelAll();
779787
780788 var it = tree.root_node.decls.iterator(0);
781789 while (it.next()) |decl_ptr| {
......@@ -817,6 +825,7 @@ pub const Compilation = struct {
817825 else => unreachable,
818826 }
819827 }
828 decl_group_consumed = true;
820829 try await (async decl_group.wait() catch unreachable);
821830
822831 // Now other code can rely on the decls scope having a complete list of names.
......@@ -897,7 +906,7 @@ pub const Compilation = struct {
897906 }
898907
899908 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
900 const tree = &decl.findRootScope().tree;
909 const tree = decl.findRootScope().tree;
901910 const is_export = decl.isExported(tree);
902911
903912 var add_to_table_resolved = false;
......@@ -927,25 +936,17 @@ pub const Compilation = struct {
927936 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
928937 errdefer self.gpa().free(text);
929938
930 try self.prelink_group.call(addCompileErrorAsync, self, root, span, text);
939 const msg = try Msg.createFromScope(self, root, span, text);
940 errdefer msg.destroy();
941
942 try self.prelink_group.call(addCompileErrorAsync, self, msg);
931943 }
932944
933945 async fn addCompileErrorAsync(
934946 self: *Compilation,
935 root: *Scope.Root,
936 span: Span,
937 text: []u8,
947 msg: *Msg,
938948 ) !void {
939 const relpath = try os.path.relative(self.gpa(), self.event_loop_local.cwd, root.realpath);
940 errdefer self.gpa().free(relpath);
941
942 const msg = try self.gpa().create(errmsg.Msg{
943 .path = if (relpath.len < root.realpath.len) relpath else root.realpath,
944 .text = text,
945 .span = span,
946 .tree = &root.tree,
947 });
948 errdefer self.gpa().destroy(msg);
949 errdefer msg.destroy();
949950
950951 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
951952 defer compile_errors.release();
src-self-hosted/errmsg.zig+195-65
......@@ -4,6 +4,8 @@ const os = std.os;
44const Token = std.zig.Token;
55const ast = std.zig.ast;
66const TokenIndex = std.zig.ast.TokenIndex;
7const Compilation = @import("compilation.zig").Compilation;
8const Scope = @import("scope.zig").Scope;
79
810pub const Color = enum {
911 Auto,
......@@ -31,77 +33,205 @@ pub const Span = struct {
3133};
3234
3335pub const Msg = struct {
34 path: []const u8,
35 text: []u8,
3636 span: Span,
37 tree: *ast.Tree,
38};
37 text: []u8,
38 data: Data,
39
40 const Data = union(enum) {
41 PathAndTree: PathAndTree,
42 ScopeAndComp: ScopeAndComp,
43 };
44
45 const PathAndTree = struct {
46 realpath: []const u8,
47 tree: *ast.Tree,
48 allocator: *mem.Allocator,
49 };
50
51 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,
53 compilation: *Compilation,
54 };
55
56 pub fn destroy(self: *Msg) void {
57 switch (self.data) {
58 Data.PathAndTree => |path_and_tree| {
59 path_and_tree.allocator.free(self.text);
60 path_and_tree.allocator.destroy(self);
61 },
62 Data.ScopeAndComp => |scope_and_comp| {
63 scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
64 scope_and_comp.compilation.gpa().free(self.text);
65 scope_and_comp.compilation.gpa().destroy(self);
66 },
67 }
68 }
69
70 fn getAllocator(self: *const Msg) *mem.Allocator {
71 switch (self.data) {
72 Data.PathAndTree => |path_and_tree| {
73 return path_and_tree.allocator;
74 },
75 Data.ScopeAndComp => |scope_and_comp| {
76 return scope_and_comp.compilation.gpa();
77 },
78 }
79 }
80
81 pub fn getRealPath(self: *const Msg) []const u8 {
82 switch (self.data) {
83 Data.PathAndTree => |path_and_tree| {
84 return path_and_tree.realpath;
85 },
86 Data.ScopeAndComp => |scope_and_comp| {
87 return scope_and_comp.root_scope.realpath;
88 },
89 }
90 }
91
92 pub fn getTree(self: *const Msg) *ast.Tree {
93 switch (self.data) {
94 Data.PathAndTree => |path_and_tree| {
95 return path_and_tree.tree;
96 },
97 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;
99 },
100 }
101 }
102
103 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
106 const msg = try comp.gpa().create(Msg{
107 .text = text,
108 .span = span,
109 .data = Data{
110 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,
112 .compilation = comp,
113 },
114 },
115 });
116 root_scope.base.ref();
117 return msg;
118 }
119
120 pub fn createFromParseErrorAndScope(
121 comp: *Compilation,
122 root_scope: *Scope.Root,
123 parse_error: *const ast.Error,
124 ) !*Msg {
125 const loc_token = parse_error.loc();
126 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127 defer text_buf.deinit();
128
129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130 try parse_error.render(&root_scope.tree.tokens, out_stream);
131
132 const msg = try comp.gpa().create(Msg{
133 .text = undefined,
134 .span = Span{
135 .first = loc_token,
136 .last = loc_token,
137 },
138 .data = Data{
139 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,
141 .compilation = comp,
142 },
143 },
144 });
145 root_scope.base.ref();
146 msg.text = text_buf.toOwnedSlice();
147 return msg;
148 }
149
150 /// `realpath` must outlive the returned Msg
151 /// `tree` must outlive the returned Msg
152 /// Caller owns returned Msg and must free with `allocator`
153 /// allocator will additionally be used for printing messages later.
154 pub fn createFromParseError(
155 allocator: *mem.Allocator,
156 parse_error: *const ast.Error,
157 tree: *ast.Tree,
158 realpath: []const u8,
159 ) !*Msg {
160 const loc_token = parse_error.loc();
161 var text_buf = try std.Buffer.initSize(allocator, 0);
162 defer text_buf.deinit();
163
164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165 try parse_error.render(&tree.tokens, out_stream);
166
167 const msg = try allocator.create(Msg{
168 .text = undefined,
169 .data = Data{
170 .PathAndTree = PathAndTree{
171 .allocator = allocator,
172 .realpath = realpath,
173 .tree = tree,
174 },
175 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180 });
181 msg.text = text_buf.toOwnedSlice();
182 errdefer allocator.destroy(msg);
183
184 return msg;
185 }
186
187 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
188 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190 const tree = msg.getTree();
191
192 const cwd = try os.getCwd(allocator);
193 defer allocator.free(cwd);
194
195 const relpath = try os.path.relative(allocator, cwd, realpath);
196 defer allocator.free(relpath);
197
198 const path = if (relpath.len < realpath.len) relpath else realpath;
199
200 const first_token = tree.tokens.at(msg.span.first);
201 const last_token = tree.tokens.at(msg.span.last);
202 const start_loc = tree.tokenLocationPtr(0, first_token);
203 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204 if (!color_on) {
205 try stream.print(
206 "{}:{}:{}: error: {}\n",
207 path,
208 start_loc.line + 1,
209 start_loc.column + 1,
210 msg.text,
211 );
212 return;
213 }
39214
40/// `path` must outlive the returned Msg
41/// `tree` must outlive the returned Msg
42/// Caller owns returned Msg and must free with `allocator`
43pub fn createFromParseError(
44 allocator: *mem.Allocator,
45 parse_error: *const ast.Error,
46 tree: *ast.Tree,
47 path: []const u8,
48) !*Msg {
49 const loc_token = parse_error.loc();
50 var text_buf = try std.Buffer.initSize(allocator, 0);
51 defer text_buf.deinit();
52
53 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
54 try parse_error.render(&tree.tokens, out_stream);
55
56 const msg = try allocator.create(Msg{
57 .tree = tree,
58 .path = path,
59 .text = text_buf.toOwnedSlice(),
60 .span = Span{
61 .first = loc_token,
62 .last = loc_token,
63 },
64 });
65 errdefer allocator.destroy(msg);
66
67 return msg;
68}
69
70pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
71 const first_token = msg.tree.tokens.at(msg.span.first);
72 const last_token = msg.tree.tokens.at(msg.span.last);
73 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
74 const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
75 if (!color_on) {
76215 try stream.print(
77 "{}:{}:{}: error: {}\n",
78 msg.path,
216 "{}:{}:{}: error: {}\n{}\n",
217 path,
79218 start_loc.line + 1,
80219 start_loc.column + 1,
81220 msg.text,
221 tree.source[start_loc.line_start..start_loc.line_end],
82222 );
83 return;
223 try stream.writeByteNTimes(' ', start_loc.column);
224 try stream.writeByteNTimes('~', last_token.end - first_token.start);
225 try stream.write("\n");
84226 }
85227
86 try stream.print(
87 "{}:{}:{}: error: {}\n{}\n",
88 msg.path,
89 start_loc.line + 1,
90 start_loc.column + 1,
91 msg.text,
92 msg.tree.source[start_loc.line_start..start_loc.line_end],
93 );
94 try stream.writeByteNTimes(' ', start_loc.column);
95 try stream.writeByteNTimes('~', last_token.end - first_token.start);
96 try stream.write("\n");
97}
98
99pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void {
100 const color_on = switch (color) {
101 Color.Auto => file.isTty(),
102 Color.On => true,
103 Color.Off => false,
104 };
105 var stream = &std.io.FileOutStream.init(file).stream;
106 return printToStream(stream, msg, color_on);
107}
228 pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
229 const color_on = switch (color) {
230 Color.Auto => file.isTty(),
231 Color.On => true,
232 Color.Off => false,
233 };
234 var stream = &std.io.FileOutStream.init(file).stream;
235 return msg.printToStream(stream, color_on);
236 }
237};
src-self-hosted/main.zig+8-7
......@@ -485,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
485485 },
486486 Compilation.Event.Fail => |msgs| {
487487 for (msgs) |msg| {
488 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
488 defer msg.destroy();
489 msg.printToFile(&stderr_file, color) catch os.exit(1);
489490 }
490491 },
491492 }
......@@ -646,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
646647
647648 var error_it = tree.errors.iterator(0);
648649 while (error_it.next()) |parse_error| {
649 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
650 defer allocator.destroy(msg);
650 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
651 defer msg.destroy();
651652
652 try errmsg.printToFile(&stderr_file, msg, color);
653 try msg.printToFile(&stderr_file, color);
653654 }
654655 if (tree.errors.len != 0) {
655656 os.exit(1);
......@@ -702,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
702703
703704 var error_it = tree.errors.iterator(0);
704705 while (error_it.next()) |parse_error| {
705 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);
706 defer allocator.destroy(msg);
706 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
707 defer msg.destroy();
707708
708 try errmsg.printToFile(&stderr_file, msg, color);
709 try msg.printToFile(&stderr_file, color);
709710 }
710711 if (tree.errors.len != 0) {
711712 fmt.any_error = true;
src-self-hosted/scope.zig+4-3
......@@ -93,13 +93,13 @@ pub const Scope = struct {
9393
9494 pub const Root = struct {
9595 base: Scope,
96 tree: ast.Tree,
96 tree: *ast.Tree,
9797 realpath: []const u8,
9898
9999 /// Creates a Root scope with 1 reference
100100 /// Takes ownership of realpath
101 /// Caller must set tree
102 pub fn create(comp: *Compilation, tree: ast.Tree, realpath: []u8) !*Root {
101 /// Takes ownership of tree, will deinit and destroy when done.
102 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
103103 const self = try comp.gpa().create(Root{
104104 .base = Scope{
105105 .id = Id.Root,
......@@ -117,6 +117,7 @@ pub const Scope = struct {
117117 pub fn destroy(self: *Root, comp: *Compilation) void {
118118 comp.gpa().free(self.tree.source);
119119 self.tree.deinit();
120 comp.gpa().destroy(self.tree);
120121 comp.gpa().free(self.realpath);
121122 comp.gpa().destroy(self);
122123 }
src-self-hosted/test.zig+8-6
......@@ -184,7 +184,8 @@ pub const TestContext = struct {
184184 var stderr = try std.io.getStdErr();
185185 try stderr.write("build incorrectly failed:\n");
186186 for (msgs) |msg| {
187 try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
187 defer msg.destroy();
188 try msg.printToFile(&stderr, errmsg.Color.Auto);
188189 }
189190 },
190191 }
......@@ -211,10 +212,10 @@ pub const TestContext = struct {
211212 Compilation.Event.Fail => |msgs| {
212213 assertOrPanic(msgs.len != 0);
213214 for (msgs) |msg| {
214 if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {
215 const first_token = msg.tree.tokens.at(msg.span.first);
216 const last_token = msg.tree.tokens.at(msg.span.first);
217 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);
217 const last_token = msg.getTree().tokens.at(msg.span.first);
218 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
218219 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
219220 return;
220221 }
......@@ -231,7 +232,8 @@ pub const TestContext = struct {
231232 std.debug.warn("\n====found:========\n");
232233 var stderr = try std.io.getStdErr();
233234 for (msgs) |msg| {
234 try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
235 defer msg.destroy();
236 try msg.printToFile(&stderr, errmsg.Color.Auto);
235237 }
236238 std.debug.warn("============\n");
237239 return error.TestFailed;
std/mem.zig+10-2
......@@ -35,6 +35,7 @@ pub const Allocator = struct {
3535 freeFn: fn (self: *Allocator, old_mem: []u8) void,
3636
3737 /// Call `destroy` with the result
38 /// TODO this is deprecated. use createOne instead
3839 pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {
3940 const T = @typeOf(init);
4041 if (@sizeOf(T) == 0) return &(T{});
......@@ -44,6 +45,14 @@ pub const Allocator = struct {
4445 return ptr;
4546 }
4647
48 /// Call `destroy` with the result.
49 /// Returns undefined memory.
50 pub fn createOne(self: *Allocator, comptime T: type) Error!*T {
51 if (@sizeOf(T) == 0) return &(T{});
52 const slice = try self.alloc(T, 1);
53 return &slice[0];
54 }
55
4756 /// `ptr` should be the return value of `create`
4857 pub fn destroy(self: *Allocator, ptr: var) void {
4958 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
......@@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
149158 @setRuntimeSafety(false);
150159 assert(dest.len >= source.len);
151160 var i = source.len;
152 while(i > 0){
161 while (i > 0) {
153162 i -= 1;
154163 dest[i] = source[i];
155164 }
156165}
157166
158
159167pub fn set(comptime T: type, dest: []T, value: T) void {
160168 for (dest) |*d|
161169 d.* = value;