authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-03 17:22:17-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-03 17:22:17-04:00
log5dfcd09e496160054d4f333500d084e35f2fbdd2
tree08f4177b091848c1b78ec8e7c1d7973ee230391a
parent7f6e97cb26ffabbc192e7ccdf44aebbbc3be751d

self-hosted: watch files and trigger a rebuild


15 files changed, 756 insertions(+), 330 deletions(-)

src-self-hosted/compilation.zig+112-46
...@@ -230,6 +230,8 @@ pub const Compilation = struct {...@@ -230,6 +230,8 @@ pub const Compilation = struct {
230230
231 c_int_types: [CInt.list.len]*Type.Int,231 c_int_types: [CInt.list.len]*Type.Int,
232232
233 fs_watch: *fs.Watch(*Scope.Root),
234
233 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);235 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
234 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);236 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
235 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);237 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
...@@ -285,6 +287,7 @@ pub const Compilation = struct {...@@ -285,6 +287,7 @@ pub const Compilation = struct {
285 LibCMissingDynamicLinker,287 LibCMissingDynamicLinker,
286 InvalidDarwinVersionString,288 InvalidDarwinVersionString,
287 UnsupportedLinkArchitecture,289 UnsupportedLinkArchitecture,
290 UserResourceLimitReached,
288 };291 };
289292
290 pub const Event = union(enum) {293 pub const Event = union(enum) {
...@@ -331,7 +334,8 @@ pub const Compilation = struct {...@@ -331,7 +334,8 @@ pub const Compilation = struct {
331 zig_lib_dir: []const u8,334 zig_lib_dir: []const u8,
332 ) !*Compilation {335 ) !*Compilation {
333 const loop = event_loop_local.loop;336 const loop = event_loop_local.loop;
334 const comp = try event_loop_local.loop.allocator.create(Compilation{337 const comp = try event_loop_local.loop.allocator.createOne(Compilation);
338 comp.* = Compilation{
335 .loop = loop,339 .loop = loop,
336 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),340 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
337 .event_loop_local = event_loop_local,341 .event_loop_local = event_loop_local,
...@@ -376,7 +380,7 @@ pub const Compilation = struct {...@@ -376,7 +380,7 @@ pub const Compilation = struct {
376 .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),380 .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
377 .windows_subsystem_windows = false,381 .windows_subsystem_windows = false,
378 .windows_subsystem_console = false,382 .windows_subsystem_console = false,
379 .link_libs_list = undefined,383 .link_libs_list = ArrayList(*LinkLib).init(comp.arena()),
380 .libc_link_lib = null,384 .libc_link_lib = null,
381 .err_color = errmsg.Color.Auto,385 .err_color = errmsg.Color.Auto,
382 .darwin_frameworks = [][]const u8{},386 .darwin_frameworks = [][]const u8{},
...@@ -417,8 +421,10 @@ pub const Compilation = struct {...@@ -417,8 +421,10 @@ pub const Compilation = struct {
417 .override_libc = null,421 .override_libc = null,
418 .destroy_handle = undefined,422 .destroy_handle = undefined,
419 .have_err_ret_tracing = false,423 .have_err_ret_tracing = false,
420 .primitive_type_table = undefined,424 .primitive_type_table = TypeTable.init(comp.arena()),
421 });425
426 .fs_watch = undefined,
427 };
422 errdefer {428 errdefer {
423 comp.int_type_table.private_data.deinit();429 comp.int_type_table.private_data.deinit();
424 comp.array_type_table.private_data.deinit();430 comp.array_type_table.private_data.deinit();
...@@ -431,9 +437,7 @@ pub const Compilation = struct {...@@ -431,9 +437,7 @@ pub const Compilation = struct {
431 comp.name = try Buffer.init(comp.arena(), name);437 comp.name = try Buffer.init(comp.arena(), name);
432 comp.llvm_triple = try target.getTriple(comp.arena());438 comp.llvm_triple = try target.getTriple(comp.arena());
433 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);439 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
434 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
435 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");440 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
436 comp.primitive_type_table = TypeTable.init(comp.arena());
437441
438 const opt_level = switch (build_mode) {442 const opt_level = switch (build_mode) {
439 builtin.Mode.Debug => llvm.CodeGenLevelNone,443 builtin.Mode.Debug => llvm.CodeGenLevelNone,
...@@ -485,6 +489,9 @@ pub const Compilation = struct {...@@ -485,6 +489,9 @@ pub const Compilation = struct {
485 comp.root_package = try Package.create(comp.arena(), ".", "");489 comp.root_package = try Package.create(comp.arena(), ".", "");
486 }490 }
487491
492 comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16);
493 errdefer comp.fs_watch.destroy();
494
488 try comp.initTypes();495 try comp.initTypes();
489496
490 comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();497 comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
...@@ -686,6 +693,7 @@ pub const Compilation = struct {...@@ -686,6 +693,7 @@ pub const Compilation = struct {
686 os.deleteTree(self.arena(), tmp_dir) catch {};693 os.deleteTree(self.arena(), tmp_dir) catch {};
687 } else |_| {};694 } else |_| {};
688695
696 self.fs_watch.destroy();
689 self.events.destroy();697 self.events.destroy();
690698
691 llvm.DisposeMessage(self.target_layout_str);699 llvm.DisposeMessage(self.target_layout_str);
...@@ -720,7 +728,9 @@ pub const Compilation = struct {...@@ -720,7 +728,9 @@ pub const Compilation = struct {
720 var build_result = await (async self.initialCompile() catch unreachable);728 var build_result = await (async self.initialCompile() catch unreachable);
721729
722 while (true) {730 while (true) {
723 const link_result = if (build_result) self.maybeLink() else |err| err;731 const link_result = if (build_result) blk: {
732 break :blk await (async self.maybeLink() catch unreachable);
733 } else |err| err;
724 // this makes a handy error return trace and stack trace in debug mode734 // this makes a handy error return trace and stack trace in debug mode
725 if (std.debug.runtime_safety) {735 if (std.debug.runtime_safety) {
726 link_result catch unreachable;736 link_result catch unreachable;
...@@ -745,9 +755,35 @@ pub const Compilation = struct {...@@ -745,9 +755,35 @@ pub const Compilation = struct {
745 await (async self.events.put(Event{ .Error = err }) catch unreachable);755 await (async self.events.put(Event{ .Error = err }) catch unreachable);
746 }756 }
747757
758 // First, get an item from the watch channel, waiting on the channel.
748 var group = event.Group(BuildError!void).init(self.loop);759 var group = event.Group(BuildError!void).init(self.loop);
749 while (self.fs_watch.channel.getOrNull()) |root_scope| {760 {
750 try group.call(rebuildFile, self, root_scope);761 const ev = await (async self.fs_watch.channel.get() catch unreachable);
762 const root_scope = switch (ev) {
763 fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,
764 fs.Watch(*Scope.Root).Event.Err => |err| {
765 build_result = err;
766 continue;
767 },
768 };
769 group.call(rebuildFile, self, root_scope) catch |err| {
770 build_result = err;
771 continue;
772 };
773 }
774 // Next, get all the items from the channel that are buffered up.
775 while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev| {
776 const root_scope = switch (ev) {
777 fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,
778 fs.Watch(*Scope.Root).Event.Err => |err| {
779 build_result = err;
780 continue;
781 },
782 };
783 group.call(rebuildFile, self, root_scope) catch |err| {
784 build_result = err;
785 continue;
786 };
751 }787 }
752 build_result = await (async group.wait() catch unreachable);788 build_result = await (async group.wait() catch unreachable);
753 }789 }
...@@ -757,11 +793,11 @@ pub const Compilation = struct {...@@ -757,11 +793,11 @@ pub const Compilation = struct {
757 const tree_scope = blk: {793 const tree_scope = blk: {
758 const source_code = (await (async fs.readFile(794 const source_code = (await (async fs.readFile(
759 self.loop,795 self.loop,
760 root_src_real_path,796 root_scope.realpath,
761 max_src_size,797 max_src_size,
762 ) catch unreachable)) catch |err| {798 ) catch unreachable)) catch |err| {
763 try printError("unable to open '{}': {}", root_src_real_path, err);799 try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
764 return err;800 return;
765 };801 };
766 errdefer self.gpa().free(source_code);802 errdefer self.gpa().free(source_code);
767803
...@@ -793,9 +829,9 @@ pub const Compilation = struct {...@@ -793,9 +829,9 @@ pub const Compilation = struct {
793 var decl_group = event.Group(BuildError!void).init(self.loop);829 var decl_group = event.Group(BuildError!void).init(self.loop);
794 defer decl_group.deinit();830 defer decl_group.deinit();
795831
796 try self.rebuildChangedDecls(832 try await try async self.rebuildChangedDecls(
797 &decl_group,833 &decl_group,
798 locked_table,834 locked_table.value,
799 root_scope.decls,835 root_scope.decls,
800 &tree_scope.tree.root_node.decls,836 &tree_scope.tree.root_node.decls,
801 tree_scope,837 tree_scope,
...@@ -809,7 +845,7 @@ pub const Compilation = struct {...@@ -809,7 +845,7 @@ pub const Compilation = struct {
809 group: *event.Group(BuildError!void),845 group: *event.Group(BuildError!void),
810 locked_table: *Decl.Table,846 locked_table: *Decl.Table,
811 decl_scope: *Scope.Decls,847 decl_scope: *Scope.Decls,
812 ast_decls: &ast.Node.Root.DeclList,848 ast_decls: *ast.Node.Root.DeclList,
813 tree_scope: *Scope.AstTree,849 tree_scope: *Scope.AstTree,
814 ) !void {850 ) !void {
815 var existing_decls = try locked_table.clone();851 var existing_decls = try locked_table.clone();
...@@ -824,14 +860,14 @@ pub const Compilation = struct {...@@ -824,14 +860,14 @@ pub const Compilation = struct {
824860
825 // TODO connect existing comptime decls to updated source files861 // TODO connect existing comptime decls to updated source files
826862
827 try self.prelink_group.call(addCompTimeBlock, self, &decl_scope.base, comptime_node);863 try self.prelink_group.call(addCompTimeBlock, self, tree_scope, &decl_scope.base, comptime_node);
828 },864 },
829 ast.Node.Id.VarDecl => @panic("TODO"),865 ast.Node.Id.VarDecl => @panic("TODO"),
830 ast.Node.Id.FnProto => {866 ast.Node.Id.FnProto => {
831 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);867 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
832868
833 const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {869 const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
834 try self.addCompileError(root_scope, Span{870 try self.addCompileError(tree_scope, Span{
835 .first = fn_proto.fn_token,871 .first = fn_proto.fn_token,
836 .last = fn_proto.fn_token + 1,872 .last = fn_proto.fn_token + 1,
837 }, "missing function name");873 }, "missing function name");
...@@ -856,10 +892,12 @@ pub const Compilation = struct {...@@ -856,10 +892,12 @@ pub const Compilation = struct {
856 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),892 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
857 .resolution = event.Future(BuildError!void).init(self.loop),893 .resolution = event.Future(BuildError!void).init(self.loop),
858 .parent_scope = &decl_scope.base,894 .parent_scope = &decl_scope.base,
895 .tree_scope = tree_scope,
859 },896 },
860 .value = Decl.Fn.Val{ .Unresolved = {} },897 .value = Decl.Fn.Val{ .Unresolved = {} },
861 .fn_proto = fn_proto,898 .fn_proto = fn_proto,
862 });899 });
900 tree_scope.base.ref();
863 errdefer self.gpa().destroy(fn_decl);901 errdefer self.gpa().destroy(fn_decl);
864902
865 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);903 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
...@@ -883,8 +921,8 @@ pub const Compilation = struct {...@@ -883,8 +921,8 @@ pub const Compilation = struct {
883 const root_scope = blk: {921 const root_scope = blk: {
884 // TODO async/await os.path.real922 // TODO async/await os.path.real
885 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {923 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
886 try printError("unable to get real path '{}': {}", root_src_path, err);924 try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err));
887 return err;925 return;
888 };926 };
889 errdefer self.gpa().free(root_src_real_path);927 errdefer self.gpa().free(root_src_real_path);
890928
...@@ -892,7 +930,8 @@ pub const Compilation = struct {...@@ -892,7 +930,8 @@ pub const Compilation = struct {
892 };930 };
893 defer root_scope.base.deref(self);931 defer root_scope.base.deref(self);
894932
895 try self.rebuildFile(root_scope);933 assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
934 try await try async self.rebuildFile(root_scope);
896 }935 }
897 }936 }
898937
...@@ -917,6 +956,7 @@ pub const Compilation = struct {...@@ -917,6 +956,7 @@ pub const Compilation = struct {
917 /// caller takes ownership of resulting Code956 /// caller takes ownership of resulting Code
918 async fn genAndAnalyzeCode(957 async fn genAndAnalyzeCode(
919 comp: *Compilation,958 comp: *Compilation,
959 tree_scope: *Scope.AstTree,
920 scope: *Scope,960 scope: *Scope,
921 node: *ast.Node,961 node: *ast.Node,
922 expected_type: ?*Type,962 expected_type: ?*Type,
...@@ -924,6 +964,7 @@ pub const Compilation = struct {...@@ -924,6 +964,7 @@ pub const Compilation = struct {
924 const unanalyzed_code = try await (async ir.gen(964 const unanalyzed_code = try await (async ir.gen(
925 comp,965 comp,
926 node,966 node,
967 tree_scope,
927 scope,968 scope,
928 ) catch unreachable);969 ) catch unreachable);
929 defer unanalyzed_code.destroy(comp.gpa());970 defer unanalyzed_code.destroy(comp.gpa());
...@@ -950,6 +991,7 @@ pub const Compilation = struct {...@@ -950,6 +991,7 @@ pub const Compilation = struct {
950991
951 async fn addCompTimeBlock(992 async fn addCompTimeBlock(
952 comp: *Compilation,993 comp: *Compilation,
994 tree_scope: *Scope.AstTree,
953 scope: *Scope,995 scope: *Scope,
954 comptime_node: *ast.Node.Comptime,996 comptime_node: *ast.Node.Comptime,
955 ) !void {997 ) !void {
...@@ -958,6 +1000,7 @@ pub const Compilation = struct {...@@ -958,6 +1000,7 @@ pub const Compilation = struct {
9581000
959 const analyzed_code = (await (async genAndAnalyzeCode(1001 const analyzed_code = (await (async genAndAnalyzeCode(
960 comp,1002 comp,
1003 tree_scope,
961 scope,1004 scope,
962 comptime_node.expr,1005 comptime_node.expr,
963 &void_type.base,1006 &void_type.base,
...@@ -975,25 +1018,37 @@ pub const Compilation = struct {...@@ -975,25 +1018,37 @@ pub const Compilation = struct {
975 decl: *Decl,1018 decl: *Decl,
976 locked_table: *Decl.Table,1019 locked_table: *Decl.Table,
977 ) !void {1020 ) !void {
978 const tree = decl.findRootScope().tree;1021 const is_export = decl.isExported(decl.tree_scope.tree);
979 const is_export = decl.isExported(tree);
9801022
981 if (is_export) {1023 if (is_export) {
982 try self.prelink_group.call(verifyUniqueSymbol, self, decl);1024 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
983 try self.prelink_group.call(resolveDecl, self, decl);1025 try self.prelink_group.call(resolveDecl, self, decl);
984 }1026 }
9851027
986 if (try locked_table.put(decl.name, decl)) |other_decl| {1028 const gop = try locked_table.getOrPut(decl.name);
987 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);1029 if (gop.found_existing) {
1030 try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", decl.name);
988 // TODO note: other definition here1031 // TODO note: other definition here
1032 } else {
1033 gop.kv.value = decl;
989 }1034 }
990 }1035 }
9911036
992 fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {1037 fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void {
1038 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
1039 errdefer self.gpa().free(text);
1040
1041 const msg = try Msg.createFromScope(self, tree_scope, span, text);
1042 errdefer msg.destroy();
1043
1044 try self.prelink_group.call(addCompileErrorAsync, self, msg);
1045 }
1046
1047 fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: ...) !void {
993 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);1048 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
994 errdefer self.gpa().free(text);1049 errdefer self.gpa().free(text);
9951050
996 const msg = try Msg.createFromScope(self, root, span, text);1051 const msg = try Msg.createFromCli(self, realpath, text);
997 errdefer msg.destroy();1052 errdefer msg.destroy();
9981053
999 try self.prelink_group.call(addCompileErrorAsync, self, msg);1054 try self.prelink_group.call(addCompileErrorAsync, self, msg);
...@@ -1017,7 +1072,7 @@ pub const Compilation = struct {...@@ -1017,7 +1072,7 @@ pub const Compilation = struct {
10171072
1018 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {1073 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
1019 try self.addCompileError(1074 try self.addCompileError(
1020 decl.findRootScope(),1075 decl.tree_scope,
1021 decl.getSpan(),1076 decl.getSpan(),
1022 "exported symbol collision: '{}'",1077 "exported symbol collision: '{}'",
1023 decl.name,1078 decl.name,
...@@ -1141,18 +1196,24 @@ pub const Compilation = struct {...@@ -1141,18 +1196,24 @@ pub const Compilation = struct {
1141 }1196 }
11421197
1143 /// Returns a value which has been ref()'d once1198 /// Returns a value which has been ref()'d once
1144 async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {1199 async fn analyzeConstValue(
1145 const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);1200 comp: *Compilation,
1201 tree_scope: *Scope.AstTree,
1202 scope: *Scope,
1203 node: *ast.Node,
1204 expected_type: *Type,
1205 ) !*Value {
1206 const analyzed_code = try await (async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type) catch unreachable);
1146 defer analyzed_code.destroy(comp.gpa());1207 defer analyzed_code.destroy(comp.gpa());
11471208
1148 return analyzed_code.getCompTimeResult(comp);1209 return analyzed_code.getCompTimeResult(comp);
1149 }1210 }
11501211
1151 async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {1212 async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
1152 const meta_type = &Type.MetaType.get(comp).base;1213 const meta_type = &Type.MetaType.get(comp).base;
1153 defer meta_type.base.deref(comp);1214 defer meta_type.base.deref(comp);
11541215
1155 const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);1216 const result_val = try await (async comp.analyzeConstValue(tree_scope, scope, node, meta_type) catch unreachable);
1156 errdefer result_val.base.deref(comp);1217 errdefer result_val.base.deref(comp);
11571218
1158 return result_val.cast(Type).?;1219 return result_val.cast(Type).?;
...@@ -1168,13 +1229,6 @@ pub const Compilation = struct {...@@ -1168,13 +1229,6 @@ pub const Compilation = struct {
1168 }1229 }
1169};1230};
11701231
1171fn printError(comptime format: []const u8, args: ...) !void {
1172 var stderr_file = try std.io.getStdErr();
1173 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
1174 const out_stream = &stderr_file_out_stream.stream;
1175 try out_stream.print(format, args);
1176}
1177
1178fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {1232fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
1179 if (optional_token_index) |token_index| {1233 if (optional_token_index) |token_index| {
1180 const token = tree.tokens.at(token_index);1234 const token = tree.tokens.at(token_index);
...@@ -1198,12 +1252,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {...@@ -1198,12 +1252,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
1198}1252}
11991253
1200async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {1254async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1255 const tree_scope = fn_decl.base.tree_scope;
1256
1201 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);1257 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
12021258
1203 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);1259 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
1204 defer fndef_scope.base.deref(comp);1260 defer fndef_scope.base.deref(comp);
12051261
1206 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);1262 const fn_type = try await (async analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
1207 defer fn_type.base.base.deref(comp);1263 defer fn_type.base.base.deref(comp);
12081264
1209 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);1265 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
...@@ -1216,18 +1272,17 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {...@@ -1216,18 +1272,17 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1216 symbol_name_consumed = true;1272 symbol_name_consumed = true;
12171273
1218 // Define local parameter variables1274 // Define local parameter variables
1219 const root_scope = fn_decl.base.findRootScope();
1220 for (fn_type.key.data.Normal.params) |param, i| {1275 for (fn_type.key.data.Normal.params) |param, i| {
1221 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);1276 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1222 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);1277 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
1223 const name_token = param_decl.name_token orelse {1278 const name_token = param_decl.name_token orelse {
1224 try comp.addCompileError(root_scope, Span{1279 try comp.addCompileError(tree_scope, Span{
1225 .first = param_decl.firstToken(),1280 .first = param_decl.firstToken(),
1226 .last = param_decl.type_node.firstToken(),1281 .last = param_decl.type_node.firstToken(),
1227 }, "missing parameter name");1282 }, "missing parameter name");
1228 return error.SemanticAnalysisFailed;1283 return error.SemanticAnalysisFailed;
1229 };1284 };
1230 const param_name = root_scope.tree.tokenSlice(name_token);1285 const param_name = tree_scope.tree.tokenSlice(name_token);
12311286
1232 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {1287 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1233 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));1288 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
...@@ -1249,6 +1304,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {...@@ -1249,6 +1304,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1249 }1304 }
12501305
1251 const analyzed_code = try await (async comp.genAndAnalyzeCode(1306 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1307 tree_scope,
1252 fn_val.child_scope,1308 fn_val.child_scope,
1253 body_node,1309 body_node,
1254 fn_type.key.data.Normal.return_type,1310 fn_type.key.data.Normal.return_type,
...@@ -1279,12 +1335,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {...@@ -1279,12 +1335,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
1279 return os.getAppDataDir(allocator, "zig");1335 return os.getAppDataDir(allocator, "zig");
1280}1336}
12811337
1282async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {1338async fn analyzeFnType(
1339 comp: *Compilation,
1340 tree_scope: *Scope.AstTree,
1341 scope: *Scope,
1342 fn_proto: *ast.Node.FnProto,
1343) !*Type.Fn {
1283 const return_type_node = switch (fn_proto.return_type) {1344 const return_type_node = switch (fn_proto.return_type) {
1284 ast.Node.FnProto.ReturnType.Explicit => |n| n,1345 ast.Node.FnProto.ReturnType.Explicit => |n| n,
1285 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,1346 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
1286 };1347 };
1287 const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);1348 const return_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, return_type_node) catch unreachable);
1288 return_type.base.deref(comp);1349 return_type.base.deref(comp);
12891350
1290 var params = ArrayList(Type.Fn.Param).init(comp.gpa());1351 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
...@@ -1300,7 +1361,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn...@@ -1300,7 +1361,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
1300 var it = fn_proto.params.iterator(0);1361 var it = fn_proto.params.iterator(0);
1301 while (it.next()) |param_node_ptr| {1362 while (it.next()) |param_node_ptr| {
1302 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;1363 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
1303 const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);1364 const param_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node) catch unreachable);
1304 errdefer param_type.base.deref(comp);1365 errdefer param_type.base.deref(comp);
1305 try params.append(Type.Fn.Param{1366 try params.append(Type.Fn.Param{
1306 .typ = param_type,1367 .typ = param_type,
...@@ -1337,7 +1398,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn...@@ -1337,7 +1398,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
1337}1398}
13381399
1339async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {1400async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1340 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);1401 const fn_type = try await (async analyzeFnType(
1402 comp,
1403 fn_decl.base.tree_scope,
1404 fn_decl.base.parent_scope,
1405 fn_decl.fn_proto,
1406 ) catch unreachable);
1341 defer fn_type.base.base.deref(comp);1407 defer fn_type.base.base.deref(comp);
13421408
1343 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);1409 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
src-self-hosted/decl.zig+2
...@@ -16,6 +16,8 @@ pub const Decl = struct {...@@ -16,6 +16,8 @@ pub const Decl = struct {
16 visib: Visib,16 visib: Visib,
17 resolution: event.Future(Compilation.BuildError!void),17 resolution: event.Future(Compilation.BuildError!void),
18 parent_scope: *Scope,18 parent_scope: *Scope,
19
20 // TODO when we destroy the decl, deref the tree scope
19 tree_scope: *Scope.AstTree,21 tree_scope: *Scope.AstTree,
2022
21 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);23 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
src-self-hosted/errmsg.zig+75-28
...@@ -33,35 +33,48 @@ pub const Span = struct {...@@ -33,35 +33,48 @@ pub const Span = struct {
33};33};
3434
35pub const Msg = struct {35pub const Msg = struct {
36 span: Span,
37 text: []u8,36 text: []u8,
37 realpath: []u8,
38 data: Data,38 data: Data,
3939
40 const Data = union(enum) {40 const Data = union(enum) {
41 Cli: Cli,
41 PathAndTree: PathAndTree,42 PathAndTree: PathAndTree,
42 ScopeAndComp: ScopeAndComp,43 ScopeAndComp: ScopeAndComp,
43 };44 };
4445
45 const PathAndTree = struct {46 const PathAndTree = struct {
46 realpath: []const u8,47 span: Span,
47 tree: *ast.Tree,48 tree: *ast.Tree,
48 allocator: *mem.Allocator,49 allocator: *mem.Allocator,
49 };50 };
5051
51 const ScopeAndComp = struct {52 const ScopeAndComp = struct {
53 span: Span,
52 tree_scope: *Scope.AstTree,54 tree_scope: *Scope.AstTree,
53 compilation: *Compilation,55 compilation: *Compilation,
54 };56 };
5557
58 const Cli = struct {
59 allocator: *mem.Allocator,
60 };
61
56 pub fn destroy(self: *Msg) void {62 pub fn destroy(self: *Msg) void {
57 switch (self.data) {63 switch (self.data) {
64 Data.Cli => |cli| {
65 cli.allocator.free(self.text);
66 cli.allocator.free(self.realpath);
67 cli.allocator.destroy(self);
68 },
58 Data.PathAndTree => |path_and_tree| {69 Data.PathAndTree => |path_and_tree| {
59 path_and_tree.allocator.free(self.text);70 path_and_tree.allocator.free(self.text);
71 path_and_tree.allocator.free(self.realpath);
60 path_and_tree.allocator.destroy(self);72 path_and_tree.allocator.destroy(self);
61 },73 },
62 Data.ScopeAndComp => |scope_and_comp| {74 Data.ScopeAndComp => |scope_and_comp| {
63 scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);75 scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
64 scope_and_comp.compilation.gpa().free(self.text);76 scope_and_comp.compilation.gpa().free(self.text);
77 scope_and_comp.compilation.gpa().free(self.realpath);
65 scope_and_comp.compilation.gpa().destroy(self);78 scope_and_comp.compilation.gpa().destroy(self);
66 },79 },
67 }80 }
...@@ -69,6 +82,7 @@ pub const Msg = struct {...@@ -69,6 +82,7 @@ pub const Msg = struct {
6982
70 fn getAllocator(self: *const Msg) *mem.Allocator {83 fn getAllocator(self: *const Msg) *mem.Allocator {
71 switch (self.data) {84 switch (self.data) {
85 Data.Cli => |cli| return cli.allocator,
72 Data.PathAndTree => |path_and_tree| {86 Data.PathAndTree => |path_and_tree| {
73 return path_and_tree.allocator;87 return path_and_tree.allocator;
74 },88 },
...@@ -78,19 +92,9 @@ pub const Msg = struct {...@@ -78,19 +92,9 @@ pub const Msg = struct {
78 }92 }
79 }93 }
8094
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.tree_scope.root().realpath;
88 },
89 }
90 }
91
92 pub fn getTree(self: *const Msg) *ast.Tree {95 pub fn getTree(self: *const Msg) *ast.Tree {
93 switch (self.data) {96 switch (self.data) {
97 Data.Cli => unreachable,
94 Data.PathAndTree => |path_and_tree| {98 Data.PathAndTree => |path_and_tree| {
95 return path_and_tree.tree;99 return path_and_tree.tree;
96 },100 },
...@@ -100,16 +104,28 @@ pub const Msg = struct {...@@ -100,16 +104,28 @@ pub const Msg = struct {
100 }104 }
101 }105 }
102106
107 pub fn getSpan(self: *const Msg) Span {
108 return switch (self.data) {
109 Data.Cli => unreachable,
110 Data.PathAndTree => |path_and_tree| path_and_tree.span,
111 Data.ScopeAndComp => |scope_and_comp| scope_and_comp.span,
112 };
113 }
114
103 /// Takes ownership of text115 /// Takes ownership of text
104 /// References tree_scope, and derefs when the msg is freed116 /// References tree_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {117 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
118 const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
119 errdefer comp.gpa().free(realpath);
120
106 const msg = try comp.gpa().create(Msg{121 const msg = try comp.gpa().create(Msg{
107 .text = text,122 .text = text,
108 .span = span,123 .realpath = realpath,
109 .data = Data{124 .data = Data{
110 .ScopeAndComp = ScopeAndComp{125 .ScopeAndComp = ScopeAndComp{
111 .tree_scope = tree_scope,126 .tree_scope = tree_scope,
112 .compilation = comp,127 .compilation = comp,
128 .span = span,
113 },129 },
114 },130 },
115 });131 });
...@@ -117,6 +133,22 @@ pub const Msg = struct {...@@ -117,6 +133,22 @@ pub const Msg = struct {
117 return msg;133 return msg;
118 }134 }
119135
136 /// Caller owns returned Msg and must free with `allocator`
137 /// allocator will additionally be used for printing messages later.
138 pub fn createFromCli(comp: *Compilation, realpath: []const u8, text: []u8) !*Msg {
139 const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
140 errdefer comp.gpa().free(realpath_copy);
141
142 const msg = try comp.gpa().create(Msg{
143 .text = text,
144 .realpath = realpath_copy,
145 .data = Data{
146 .Cli = Cli{ .allocator = comp.gpa() },
147 },
148 });
149 return msg;
150 }
151
120 pub fn createFromParseErrorAndScope(152 pub fn createFromParseErrorAndScope(
121 comp: *Compilation,153 comp: *Compilation,
122 tree_scope: *Scope.AstTree,154 tree_scope: *Scope.AstTree,
...@@ -126,19 +158,23 @@ pub const Msg = struct {...@@ -126,19 +158,23 @@ pub const Msg = struct {
126 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);158 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127 defer text_buf.deinit();159 defer text_buf.deinit();
128160
161 const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
162 errdefer comp.gpa().free(realpath_copy);
163
129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130 try parse_error.render(&tree_scope.tree.tokens, out_stream);165 try parse_error.render(&tree_scope.tree.tokens, out_stream);
131166
132 const msg = try comp.gpa().create(Msg{167 const msg = try comp.gpa().create(Msg{
133 .text = undefined,168 .text = undefined,
134 .span = Span{169 .realpath = realpath_copy,
135 .first = loc_token,
136 .last = loc_token,
137 },
138 .data = Data{170 .data = Data{
139 .ScopeAndComp = ScopeAndComp{171 .ScopeAndComp = ScopeAndComp{
140 .tree_scope = tree_scope,172 .tree_scope = tree_scope,
141 .compilation = comp,173 .compilation = comp,
174 .span = Span{
175 .first = loc_token,
176 .last = loc_token,
177 },
142 },178 },
143 },179 },
144 });180 });
...@@ -161,22 +197,25 @@ pub const Msg = struct {...@@ -161,22 +197,25 @@ pub const Msg = struct {
161 var text_buf = try std.Buffer.initSize(allocator, 0);197 var text_buf = try std.Buffer.initSize(allocator, 0);
162 defer text_buf.deinit();198 defer text_buf.deinit();
163199
200 const realpath_copy = try mem.dupe(allocator, u8, realpath);
201 errdefer allocator.free(realpath_copy);
202
164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;203 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165 try parse_error.render(&tree.tokens, out_stream);204 try parse_error.render(&tree.tokens, out_stream);
166205
167 const msg = try allocator.create(Msg{206 const msg = try allocator.create(Msg{
168 .text = undefined,207 .text = undefined,
208 .realpath = realpath_copy,
169 .data = Data{209 .data = Data{
170 .PathAndTree = PathAndTree{210 .PathAndTree = PathAndTree{
171 .allocator = allocator,211 .allocator = allocator,
172 .realpath = realpath,
173 .tree = tree,212 .tree = tree,
213 .span = Span{
214 .first = loc_token,
215 .last = loc_token,
216 },
174 },217 },
175 },218 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180 });219 });
181 msg.text = text_buf.toOwnedSlice();220 msg.text = text_buf.toOwnedSlice();
182 errdefer allocator.destroy(msg);221 errdefer allocator.destroy(msg);
...@@ -185,20 +224,28 @@ pub const Msg = struct {...@@ -185,20 +224,28 @@ pub const Msg = struct {
185 }224 }
186225
187 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {226 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
227 switch (msg.data) {
228 Data.Cli => {
229 try stream.print("{}:-:-: error: {}\n", msg.realpath, msg.text);
230 return;
231 },
232 else => {},
233 }
234
188 const allocator = msg.getAllocator();235 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190 const tree = msg.getTree();236 const tree = msg.getTree();
191237
192 const cwd = try os.getCwd(allocator);238 const cwd = try os.getCwd(allocator);
193 defer allocator.free(cwd);239 defer allocator.free(cwd);
194240
195 const relpath = try os.path.relative(allocator, cwd, realpath);241 const relpath = try os.path.relative(allocator, cwd, msg.realpath);
196 defer allocator.free(relpath);242 defer allocator.free(relpath);
197243
198 const path = if (relpath.len < realpath.len) relpath else realpath;244 const path = if (relpath.len < msg.realpath.len) relpath else msg.realpath;
245 const span = msg.getSpan();
199246
200 const first_token = tree.tokens.at(msg.span.first);247 const first_token = tree.tokens.at(span.first);
201 const last_token = tree.tokens.at(msg.span.last);248 const last_token = tree.tokens.at(span.last);
202 const start_loc = tree.tokenLocationPtr(0, first_token);249 const start_loc = tree.tokenLocationPtr(0, first_token);
203 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);250 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204 if (!color_on) {251 if (!color_on) {
src-self-hosted/ir.zig+23-20
...@@ -961,6 +961,7 @@ pub const Code = struct {...@@ -961,6 +961,7 @@ pub const Code = struct {
961 basic_block_list: std.ArrayList(*BasicBlock),961 basic_block_list: std.ArrayList(*BasicBlock),
962 arena: std.heap.ArenaAllocator,962 arena: std.heap.ArenaAllocator,
963 return_type: ?*Type,963 return_type: ?*Type,
964 tree_scope: *Scope.AstTree,
964965
965 /// allocator is comp.gpa()966 /// allocator is comp.gpa()
966 pub fn destroy(self: *Code, allocator: *Allocator) void {967 pub fn destroy(self: *Code, allocator: *Allocator) void {
...@@ -990,14 +991,14 @@ pub const Code = struct {...@@ -990,14 +991,14 @@ pub const Code = struct {
990 return ret_value.val.KnownValue.getRef();991 return ret_value.val.KnownValue.getRef();
991 }992 }
992 try comp.addCompileError(993 try comp.addCompileError(
993 ret_value.scope.findRoot(),994 self.tree_scope,
994 ret_value.span,995 ret_value.span,
995 "unable to evaluate constant expression",996 "unable to evaluate constant expression",
996 );997 );
997 return error.SemanticAnalysisFailed;998 return error.SemanticAnalysisFailed;
998 } else if (inst.hasSideEffects()) {999 } else if (inst.hasSideEffects()) {
999 try comp.addCompileError(1000 try comp.addCompileError(
1000 inst.scope.findRoot(),1001 self.tree_scope,
1001 inst.span,1002 inst.span,
1002 "unable to evaluate constant expression",1003 "unable to evaluate constant expression",
1003 );1004 );
...@@ -1013,25 +1014,24 @@ pub const Builder = struct {...@@ -1013,25 +1014,24 @@ pub const Builder = struct {
1013 code: *Code,1014 code: *Code,
1014 current_basic_block: *BasicBlock,1015 current_basic_block: *BasicBlock,
1015 next_debug_id: usize,1016 next_debug_id: usize,
1016 root_scope: *Scope.Root,
1017 is_comptime: bool,1017 is_comptime: bool,
1018 is_async: bool,1018 is_async: bool,
1019 begin_scope: ?*Scope,1019 begin_scope: ?*Scope,
10201020
1021 pub const Error = Analyze.Error;1021 pub const Error = Analyze.Error;
10221022
1023 pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder {1023 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder {
1024 const code = try comp.gpa().create(Code{1024 const code = try comp.gpa().create(Code{
1025 .basic_block_list = undefined,1025 .basic_block_list = undefined,
1026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),1026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
1027 .return_type = null,1027 .return_type = null,
1028 .tree_scope = tree_scope,
1028 });1029 });
1029 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);1030 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
1030 errdefer code.destroy(comp.gpa());1031 errdefer code.destroy(comp.gpa());
10311032
1032 return Builder{1033 return Builder{
1033 .comp = comp,1034 .comp = comp,
1034 .root_scope = root_scope,
1035 .current_basic_block = undefined,1035 .current_basic_block = undefined,
1036 .code = code,1036 .code = code,
1037 .next_debug_id = 0,1037 .next_debug_id = 0,
...@@ -1292,6 +1292,7 @@ pub const Builder = struct {...@@ -1292,6 +1292,7 @@ pub const Builder = struct {
1292 Scope.Id.FnDef => return false,1292 Scope.Id.FnDef => return false,
1293 Scope.Id.Decls => unreachable,1293 Scope.Id.Decls => unreachable,
1294 Scope.Id.Root => unreachable,1294 Scope.Id.Root => unreachable,
1295 Scope.Id.AstTree => unreachable,
1295 Scope.Id.Block,1296 Scope.Id.Block,
1296 Scope.Id.Defer,1297 Scope.Id.Defer,
1297 Scope.Id.DeferExpr,1298 Scope.Id.DeferExpr,
...@@ -1302,7 +1303,7 @@ pub const Builder = struct {...@@ -1302,7 +1303,7 @@ pub const Builder = struct {
1302 }1303 }
13031304
1304 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {1305 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
1305 const int_token = irb.root_scope.tree.tokenSlice(int_lit.token);1306 const int_token = irb.code.tree_scope.tree.tokenSlice(int_lit.token);
13061307
1307 var base: u8 = undefined;1308 var base: u8 = undefined;
1308 var rest: []const u8 = undefined;1309 var rest: []const u8 = undefined;
...@@ -1341,7 +1342,7 @@ pub const Builder = struct {...@@ -1341,7 +1342,7 @@ pub const Builder = struct {
1341 }1342 }
13421343
1343 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {1344 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
1344 const str_token = irb.root_scope.tree.tokenSlice(str_lit.token);1345 const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
1345 const src_span = Span.token(str_lit.token);1346 const src_span = Span.token(str_lit.token);
13461347
1347 var bad_index: usize = undefined;1348 var bad_index: usize = undefined;
...@@ -1349,7 +1350,7 @@ pub const Builder = struct {...@@ -1349,7 +1350,7 @@ pub const Builder = struct {
1349 error.OutOfMemory => return error.OutOfMemory,1350 error.OutOfMemory => return error.OutOfMemory,
1350 error.InvalidCharacter => {1351 error.InvalidCharacter => {
1351 try irb.comp.addCompileError(1352 try irb.comp.addCompileError(
1352 irb.root_scope,1353 irb.code.tree_scope,
1353 src_span,1354 src_span,
1354 "invalid character in string literal: '{c}'",1355 "invalid character in string literal: '{c}'",
1355 str_token[bad_index],1356 str_token[bad_index],
...@@ -1427,7 +1428,7 @@ pub const Builder = struct {...@@ -1427,7 +1428,7 @@ pub const Builder = struct {
14271428
1428 if (statement_node.cast(ast.Node.Defer)) |defer_node| {1429 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
1429 // defer starts a new scope1430 // defer starts a new scope
1430 const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token);1431 const defer_token = irb.code.tree_scope.tree.tokens.at(defer_node.defer_token);
1431 const kind = switch (defer_token.id) {1432 const kind = switch (defer_token.id) {
1432 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,1433 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
1433 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,1434 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
...@@ -1513,7 +1514,7 @@ pub const Builder = struct {...@@ -1513,7 +1514,7 @@ pub const Builder = struct {
1513 const src_span = Span.token(control_flow_expr.ltoken);1514 const src_span = Span.token(control_flow_expr.ltoken);
1514 if (scope.findFnDef() == null) {1515 if (scope.findFnDef() == null) {
1515 try irb.comp.addCompileError(1516 try irb.comp.addCompileError(
1516 irb.root_scope,1517 irb.code.tree_scope,
1517 src_span,1518 src_span,
1518 "return expression outside function definition",1519 "return expression outside function definition",
1519 );1520 );
...@@ -1523,7 +1524,7 @@ pub const Builder = struct {...@@ -1523,7 +1524,7 @@ pub const Builder = struct {
1523 if (scope.findDeferExpr()) |scope_defer_expr| {1524 if (scope.findDeferExpr()) |scope_defer_expr| {
1524 if (!scope_defer_expr.reported_err) {1525 if (!scope_defer_expr.reported_err) {
1525 try irb.comp.addCompileError(1526 try irb.comp.addCompileError(
1526 irb.root_scope,1527 irb.code.tree_scope,
1527 src_span,1528 src_span,
1528 "cannot return from defer expression",1529 "cannot return from defer expression",
1529 );1530 );
...@@ -1599,7 +1600,7 @@ pub const Builder = struct {...@@ -1599,7 +1600,7 @@ pub const Builder = struct {
15991600
1600 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {1601 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
1601 const src_span = Span.token(identifier.token);1602 const src_span = Span.token(identifier.token);
1602 const name = irb.root_scope.tree.tokenSlice(identifier.token);1603 const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
16031604
1604 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {1605 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
1605 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);1606 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
...@@ -1622,7 +1623,7 @@ pub const Builder = struct {...@@ -1622,7 +1623,7 @@ pub const Builder = struct {
1622 }1623 }
1623 } else |err| switch (err) {1624 } else |err| switch (err) {
1624 error.Overflow => {1625 error.Overflow => {
1625 try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large");1626 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large");
1626 return error.SemanticAnalysisFailed;1627 return error.SemanticAnalysisFailed;
1627 },1628 },
1628 error.OutOfMemory => return error.OutOfMemory,1629 error.OutOfMemory => return error.OutOfMemory,
...@@ -1656,7 +1657,7 @@ pub const Builder = struct {...@@ -1656,7 +1657,7 @@ pub const Builder = struct {
1656 // TODO put a variable of same name with invalid type in global scope1657 // TODO put a variable of same name with invalid type in global scope
1657 // so that future references to this same name will find a variable with an invalid type1658 // so that future references to this same name will find a variable with an invalid type
16581659
1659 try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name);1660 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", name);
1660 return error.SemanticAnalysisFailed;1661 return error.SemanticAnalysisFailed;
1661 }1662 }
16621663
...@@ -1689,6 +1690,7 @@ pub const Builder = struct {...@@ -1689,6 +1690,7 @@ pub const Builder = struct {
1689 => scope = scope.parent orelse break,1690 => scope = scope.parent orelse break,
16901691
1691 Scope.Id.DeferExpr => unreachable,1692 Scope.Id.DeferExpr => unreachable,
1693 Scope.Id.AstTree => unreachable,
1692 }1694 }
1693 }1695 }
1694 return result;1696 return result;
...@@ -1740,6 +1742,7 @@ pub const Builder = struct {...@@ -1740,6 +1742,7 @@ pub const Builder = struct {
1740 => scope = scope.parent orelse return is_noreturn,1742 => scope = scope.parent orelse return is_noreturn,
17411743
1742 Scope.Id.DeferExpr => unreachable,1744 Scope.Id.DeferExpr => unreachable,
1745 Scope.Id.AstTree => unreachable,
1743 }1746 }
1744 }1747 }
1745 }1748 }
...@@ -1968,8 +1971,8 @@ const Analyze = struct {...@@ -1968,8 +1971,8 @@ const Analyze = struct {
1968 OutOfMemory,1971 OutOfMemory,
1969 };1972 };
19701973
1971 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {1974 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
1972 var irb = try Builder.init(comp, root_scope, null);1975 var irb = try Builder.init(comp, tree_scope, null);
1973 errdefer irb.abort();1976 errdefer irb.abort();
19741977
1975 return Analyze{1978 return Analyze{
...@@ -2047,7 +2050,7 @@ const Analyze = struct {...@@ -2047,7 +2050,7 @@ const Analyze = struct {
2047 }2050 }
20482051
2049 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {2052 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
2050 return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args);2053 return self.irb.comp.addCompileError(self.irb.code.tree_scope, span, fmt, args);
2051 }2054 }
20522055
2053 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {2056 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
...@@ -2535,9 +2538,10 @@ const Analyze = struct {...@@ -2535,9 +2538,10 @@ const Analyze = struct {
2535pub async fn gen(2538pub async fn gen(
2536 comp: *Compilation,2539 comp: *Compilation,
2537 body_node: *ast.Node,2540 body_node: *ast.Node,
2541 tree_scope: *Scope.AstTree,
2538 scope: *Scope,2542 scope: *Scope,
2539) !*Code {2543) !*Code {
2540 var irb = try Builder.init(comp, scope.findRoot(), scope);2544 var irb = try Builder.init(comp, tree_scope, scope);
2541 errdefer irb.abort();2545 errdefer irb.abort();
25422546
2543 const entry_block = try irb.createBasicBlock(scope, c"Entry");2547 const entry_block = try irb.createBasicBlock(scope, c"Entry");
...@@ -2555,9 +2559,8 @@ pub async fn gen(...@@ -2555,9 +2559,8 @@ pub async fn gen(
25552559
2556pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {2560pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
2557 const old_entry_bb = old_code.basic_block_list.at(0);2561 const old_entry_bb = old_code.basic_block_list.at(0);
2558 const root_scope = old_entry_bb.scope.findRoot();
25592562
2560 var ira = try Analyze.init(comp, root_scope, expected_type);2563 var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
2561 errdefer ira.abort();2564 errdefer ira.abort();
25622565
2563 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);2566 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
src-self-hosted/main.zig+33-27
...@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;...@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;
24var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;24var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
25var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;25var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
2626
27const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
28
27const usage =29const usage =
28 \\usage: zig [command] [options]30 \\usage: zig [command] [options]
29 \\31 \\
...@@ -71,26 +73,26 @@ pub fn main() !void {...@@ -71,26 +73,26 @@ pub fn main() !void {
71 }73 }
7274
73 const commands = []Command{75 const commands = []Command{
74 //Command{76 Command{
75 // .name = "build-exe",77 .name = "build-exe",
76 // .exec = cmdBuildExe,78 .exec = cmdBuildExe,
77 //},79 },
78 //Command{80 Command{
79 // .name = "build-lib",81 .name = "build-lib",
80 // .exec = cmdBuildLib,82 .exec = cmdBuildLib,
81 //},83 },
82 //Command{84 Command{
83 // .name = "build-obj",85 .name = "build-obj",
84 // .exec = cmdBuildObj,86 .exec = cmdBuildObj,
85 //},87 },
86 Command{88 Command{
87 .name = "fmt",89 .name = "fmt",
88 .exec = cmdFmt,90 .exec = cmdFmt,
89 },91 },
90 //Command{92 Command{
91 // .name = "libc",93 .name = "libc",
92 // .exec = cmdLibC,94 .exec = cmdLibC,
93 //},95 },
94 Command{96 Command{
95 .name = "targets",97 .name = "targets",
96 .exec = cmdTargets,98 .exec = cmdTargets,
...@@ -472,16 +474,21 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -472,16 +474,21 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
472}474}
473475
474async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {476async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
477 var count: usize = 0;
475 while (true) {478 while (true) {
476 // TODO directly awaiting async should guarantee memory allocation elision479 // TODO directly awaiting async should guarantee memory allocation elision
477 const build_event = await (async comp.events.get() catch unreachable);480 const build_event = await (async comp.events.get() catch unreachable);
481 count += 1;
478482
479 switch (build_event) {483 switch (build_event) {
480 Compilation.Event.Ok => {},484 Compilation.Event.Ok => {
485 stderr.print("Build {} succeeded\n", count) catch os.exit(1);
486 },
481 Compilation.Event.Error => |err| {487 Compilation.Event.Error => |err| {
482 stderr.print("build failed: {}\n", @errorName(err)) catch os.exit(1);488 stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch os.exit(1);
483 },489 },
484 Compilation.Event.Fail => |msgs| {490 Compilation.Event.Fail => |msgs| {
491 stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
485 for (msgs) |msg| {492 for (msgs) |msg| {
486 defer msg.destroy();493 defer msg.destroy();
487 msg.printToFile(&stderr_file, color) catch os.exit(1);494 msg.printToFile(&stderr_file, color) catch os.exit(1);
...@@ -614,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -614,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
614 var stdin_file = try io.getStdIn();621 var stdin_file = try io.getStdIn();
615 var stdin = io.FileInStream.init(&stdin_file);622 var stdin = io.FileInStream.init(&stdin_file);
616623
617 const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize));624 const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
618 defer allocator.free(source_code);625 defer allocator.free(source_code);
619626
620 var tree = std.zig.parse(allocator, source_code) catch |err| {627 var tree = std.zig.parse(allocator, source_code) catch |err| {
...@@ -697,12 +704,6 @@ async fn asyncFmtMain(...@@ -697,12 +704,6 @@ async fn asyncFmtMain(
697 suspend {704 suspend {
698 resume @handle();705 resume @handle();
699 }706 }
700 // Things we need to make event-based:
701 // * opening the file in the first place - the open()
702 // * read()
703 // * readdir()
704 // * the actual parsing and rendering
705 // * rename()
706 var fmt = Fmt{707 var fmt = Fmt{
707 .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),708 .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
708 .any_error = false,709 .any_error = false,
...@@ -714,7 +715,10 @@ async fn asyncFmtMain(...@@ -714,7 +715,10 @@ async fn asyncFmtMain(
714 for (flags.positionals.toSliceConst()) |file_path| {715 for (flags.positionals.toSliceConst()) |file_path| {
715 try group.call(fmtPath, &fmt, file_path);716 try group.call(fmtPath, &fmt, file_path);
716 }717 }
717 return await (async group.wait() catch unreachable);718 try await (async group.wait() catch unreachable);
719 if (fmt.any_error) {
720 os.exit(1);
721 }
718}722}
719723
720async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {724async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
...@@ -731,9 +735,10 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {...@@ -731,9 +735,10 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
731 const source_code = (await try async event.fs.readFile(735 const source_code = (await try async event.fs.readFile(
732 fmt.loop,736 fmt.loop,
733 file_path,737 file_path,
734 2 * 1024 * 1024 * 1024,738 max_src_size,
735 )) catch |err| switch (err) {739 )) catch |err| switch (err) {
736 error.IsDir => {740 error.IsDir => {
741 // TODO make event based (and dir.next())
737 var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);742 var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
738 defer dir.close();743 defer dir.close();
739744
...@@ -774,6 +779,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {...@@ -774,6 +779,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
774 return;779 return;
775 }780 }
776781
782 // TODO make this evented
777 const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);783 const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
778 defer baf.destroy();784 defer baf.destroy();
779785
src-self-hosted/scope.zig+8-4
...@@ -63,6 +63,8 @@ pub const Scope = struct {...@@ -63,6 +63,8 @@ pub const Scope = struct {
63 Id.CompTime,63 Id.CompTime,
64 Id.Var,64 Id.Var,
65 => scope = scope.parent.?,65 => scope = scope.parent.?,
66
67 Id.AstTree => unreachable,
66 }68 }
67 }69 }
68 }70 }
...@@ -83,6 +85,8 @@ pub const Scope = struct {...@@ -83,6 +85,8 @@ pub const Scope = struct {
83 Id.Root,85 Id.Root,
84 Id.Var,86 Id.Var,
85 => scope = scope.parent orelse return null,87 => scope = scope.parent orelse return null,
88
89 Id.AstTree => unreachable,
86 }90 }
87 }91 }
88 }92 }
...@@ -132,6 +136,7 @@ pub const Scope = struct {...@@ -132,6 +136,7 @@ pub const Scope = struct {
132 }136 }
133137
134 pub fn destroy(self: *Root, comp: *Compilation) void {138 pub fn destroy(self: *Root, comp: *Compilation) void {
139 // TODO comp.fs_watch.removeFile(self.realpath);
135 self.decls.base.deref(comp);140 self.decls.base.deref(comp);
136 comp.gpa().free(self.realpath);141 comp.gpa().free(self.realpath);
137 comp.gpa().destroy(self);142 comp.gpa().destroy(self);
...@@ -144,13 +149,13 @@ pub const Scope = struct {...@@ -144,13 +149,13 @@ pub const Scope = struct {
144149
145 /// Creates a scope with 1 reference150 /// Creates a scope with 1 reference
146 /// Takes ownership of tree, will deinit and destroy when done.151 /// Takes ownership of tree, will deinit and destroy when done.
147 pub fn create(comp: *Compilation, tree: *ast.Tree, root: *Root) !*AstTree {152 pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree {
148 const self = try comp.gpa().createOne(Root);153 const self = try comp.gpa().createOne(AstTree);
149 self.* = AstTree{154 self.* = AstTree{
150 .base = undefined,155 .base = undefined,
151 .tree = tree,156 .tree = tree,
152 };157 };
153 self.base.init(Id.AstTree, &root.base);158 self.base.init(Id.AstTree, &root_scope.base);
154159
155 return self;160 return self;
156 }161 }
...@@ -181,7 +186,6 @@ pub const Scope = struct {...@@ -181,7 +186,6 @@ pub const Scope = struct {
181 self.* = Decls{186 self.* = Decls{
182 .base = undefined,187 .base = undefined,
183 .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),188 .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
184 .name_future = event.Future(void).init(comp.loop),
185 };189 };
186 self.base.init(Id.Decls, parent);190 self.base.init(Id.Decls, parent);
187 return self;191 return self;
src-self-hosted/test.zig+4-3
...@@ -212,9 +212,10 @@ pub const TestContext = struct {...@@ -212,9 +212,10 @@ pub const TestContext = struct {
212 Compilation.Event.Fail => |msgs| {212 Compilation.Event.Fail => |msgs| {
213 assertOrPanic(msgs.len != 0);213 assertOrPanic(msgs.len != 0);
214 for (msgs) |msg| {214 for (msgs) |msg| {
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {215 if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);216 const span = msg.getSpan();
217 const last_token = msg.getTree().tokens.at(msg.span.first);217 const first_token = msg.getTree().tokens.at(span.first);
218 const last_token = msg.getTree().tokens.at(span.first);
218 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);219 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
219 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {220 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
220 return;221 return;
src/ir.cpp+10-1
...@@ -9614,6 +9614,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un...@@ -9614,6 +9614,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
9614 case ConstValSpecialStatic:9614 case ConstValSpecialStatic:
9615 return &value->value;9615 return &value->value;
9616 case ConstValSpecialRuntime:9616 case ConstValSpecialRuntime:
9617 if (!type_has_bits(value->value.type)) {
9618 return &value->value;
9619 }
9617 ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));9620 ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
9618 return nullptr;9621 return nullptr;
9619 case ConstValSpecialUndef:9622 case ConstValSpecialUndef:
...@@ -16115,8 +16118,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir...@@ -16115,8 +16118,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir
16115 if (casted_field_value == ira->codegen->invalid_instruction)16118 if (casted_field_value == ira->codegen->invalid_instruction)
16116 return ira->codegen->builtin_types.entry_invalid;16119 return ira->codegen->builtin_types.entry_invalid;
1611716120
16121 type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type);
16122 if (type_is_invalid(casted_field_value->value.type))
16123 return ira->codegen->builtin_types.entry_invalid;
16124
16118 bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);16125 bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
16119 if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {16126 if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime ||
16127 !type_has_bits(casted_field_value->value.type))
16128 {
16120 ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);16129 ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
16121 if (!field_val)16130 if (!field_val)
16122 return ira->codegen->builtin_types.entry_invalid;16131 return ira->codegen->builtin_types.entry_invalid;
std/build.zig+59-50
...@@ -424,60 +424,69 @@ pub const Builder = struct {...@@ -424,60 +424,69 @@ pub const Builder = struct {
424 return mode;424 return mode;
425 }425 }
426426
427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool {427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
428 if (self.user_input_options.put(name, UserInputOption{428 const gop = try self.user_input_options.getOrPut(name);
429 .name = name,429 if (!gop.found_existing) {
430 .value = UserValue{ .Scalar = value },430 gop.kv.value = UserInputOption{
431 .used = false,431 .name = name,
432 }) catch unreachable) |*prev_value| {432 .value = UserValue{ .Scalar = value },
433 // option already exists433 .used = false,
434 switch (prev_value.value) {434 };
435 UserValue.Scalar => |s| {435 return false;
436 // turn it into a list436 }
437 var list = ArrayList([]const u8).init(self.allocator);437
438 list.append(s) catch unreachable;438 // option already exists
439 list.append(value) catch unreachable;439 switch (gop.kv.value.value) {
440 _ = self.user_input_options.put(name, UserInputOption{440 UserValue.Scalar => |s| {
441 .name = name,441 // turn it into a list
442 .value = UserValue{ .List = list },442 var list = ArrayList([]const u8).init(self.allocator);
443 .used = false,443 list.append(s) catch unreachable;
444 }) catch unreachable;444 list.append(value) catch unreachable;
445 },445 _ = self.user_input_options.put(name, UserInputOption{
446 UserValue.List => |*list| {446 .name = name,
447 // append to the list447 .value = UserValue{ .List = list },
448 list.append(value) catch unreachable;448 .used = false,
449 _ = self.user_input_options.put(name, UserInputOption{449 }) catch unreachable;
450 .name = name,450 },
451 .value = UserValue{ .List = list.* },451 UserValue.List => |*list| {
452 .used = false,452 // append to the list
453 }) catch unreachable;453 list.append(value) catch unreachable;
454 },454 _ = self.user_input_options.put(name, UserInputOption{
455 UserValue.Flag => {455 .name = name,
456 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);456 .value = UserValue{ .List = list.* },
457 return true;457 .used = false,
458 },458 }) catch unreachable;
459 }459 },
460 UserValue.Flag => {
461 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
462 return true;
463 },
460 }464 }
461 return false;465 return false;
462 }466 }
463467
464 pub fn addUserInputFlag(self: *Builder, name: []const u8) bool {468 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
465 if (self.user_input_options.put(name, UserInputOption{469 const gop = try self.user_input_options.getOrPut(name);
466 .name = name,470 if (!gop.found_existing) {
467 .value = UserValue{ .Flag = {} },471 gop.kv.value = UserInputOption{
468 .used = false,472 .name = name,
469 }) catch unreachable) |*prev_value| {473 .value = UserValue{ .Flag = {} },
470 switch (prev_value.value) {474 .used = false,
471 UserValue.Scalar => |s| {475 };
472 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);476 return false;
473 return true;477 }
474 },478
475 UserValue.List => {479 // option already exists
476 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);480 switch (gop.kv.value.value) {
477 return true;481 UserValue.Scalar => |s| {
478 },482 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
479 UserValue.Flag => {},483 return true;
480 }484 },
485 UserValue.List => {
486 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
487 return true;
488 },
489 UserValue.Flag => {},
481 }490 }
482 return false;491 return false;
483 }492 }
std/event/fs.zig+180-94
...@@ -367,109 +367,193 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)...@@ -367,109 +367,193 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)
367 }367 }
368}368}
369369
370pub const Watch = struct {370pub fn Watch(comptime V: type) type {
371 channel: *event.Channel(Event),371 return struct {
372 putter: promise,372 channel: *event.Channel(Event),
373373 putter: promise,
374 pub const Event = union(enum) {374 wd_table: WdTable,
375 CloseWrite,375 table_lock: event.Lock,
376 Err: Error,376 inotify_fd: i32,
377 };377
378378 const WdTable = std.AutoHashMap(i32, Dir);
379 pub const Error = error{379 const FileTable = std.AutoHashMap([]const u8, V);
380 UserResourceLimitReached,380
381 SystemResources,381 const Self = this;
382 };382
383383 const Dir = struct {
384 pub fn destroy(self: *Watch) void {384 dirname: []const u8,
385 // TODO https://github.com/ziglang/zig/issues/1261385 file_table: FileTable,
386 cancel self.putter;386 };
387 }
388};
389
390pub fn watchFile(loop: *event.Loop, file_path: []const u8) !*Watch {
391 const path_with_null = try std.cstr.addNullByte(loop.allocator, file_path);
392 defer loop.allocator.free(path_with_null);
393387
394 const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);388 pub const Event = union(enum) {
395 errdefer os.close(inotify_fd);389 CloseWrite: V,
390 Err: Error,
396391
397 const wd = try os.linuxINotifyAddWatchC(inotify_fd, path_with_null.ptr, os.linux.IN_CLOSE_WRITE);392 pub const Error = error{
398 errdefer os.close(wd);393 UserResourceLimitReached,
394 SystemResources,
395 };
396 };
399397
400 const channel = try event.Channel(Watch.Event).create(loop, 0);398 pub fn create(loop: *event.Loop, event_buf_count: usize) !*Self {
401 errdefer channel.destroy();399 const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
400 errdefer os.close(inotify_fd);
402401
403 var result: *Watch = undefined;402 const channel = try event.Channel(Self.Event).create(loop, event_buf_count);
404 _ = try async<loop.allocator> watchEventPutter(inotify_fd, wd, channel, &result);403 errdefer channel.destroy();
405 return result;
406}
407
408async fn watchEventPutter(inotify_fd: i32, wd: i32, channel: *event.Channel(Watch.Event), out_watch: **Watch) void {
409 // TODO https://github.com/ziglang/zig/issues/1194
410 suspend {
411 resume @handle();
412 }
413404
414 var watch = Watch{405 var result: *Self = undefined;
415 .putter = @handle(),406 _ = try async<loop.allocator> eventPutter(inotify_fd, channel, &result);
416 .channel = channel,407 return result;
417 };408 }
418 out_watch.* = &watch;
419409
420 const loop = channel.loop;410 pub fn destroy(self: *Self) void {
421 loop.beginOneEvent();411 cancel self.putter;
412 }
422413
423 defer {414 pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
424 channel.destroy();415 const dirname = os.path.dirname(file_path) orelse ".";
425 os.close(wd);416 const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);
426 os.close(inotify_fd);417 var dirname_with_null_consumed = false;
427 loop.finishOneEvent();418 defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);
428 }419
420 const basename = os.path.basename(file_path);
421 const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);
422 var basename_with_null_consumed = false;
423 defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);
424
425 const wd = try os.linuxINotifyAddWatchC(
426 self.inotify_fd,
427 dirname_with_null.ptr,
428 os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
429 );
430 // wd is either a newly created watch or an existing one.
431
432 const held = await (async self.table_lock.acquire() catch unreachable);
433 defer held.release();
434
435 const gop = try self.wd_table.getOrPut(wd);
436 if (!gop.found_existing) {
437 gop.kv.value = Dir{
438 .dirname = dirname_with_null,
439 .file_table = FileTable.init(self.channel.loop.allocator),
440 };
441 dirname_with_null_consumed = true;
442 }
443 const dir = &gop.kv.value;
444
445 const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
446 if (file_table_gop.found_existing) {
447 const prev_value = file_table_gop.kv.value;
448 file_table_gop.kv.value = value;
449 return prev_value;
450 } else {
451 file_table_gop.kv.value = value;
452 basename_with_null_consumed = true;
453 return null;
454 }
455 }
429456
430 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;457 pub async fn removeFile(self: *Self, file_path: []const u8) ?V {
458 @panic("TODO");
459 }
431460
432 while (true) {461 async fn eventPutter(inotify_fd: i32, channel: *event.Channel(Event), out_watch: **Self) void {
433 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);462 // TODO https://github.com/ziglang/zig/issues/1194
434 const errno = os.linux.getErrno(rc);463 suspend {
435 switch (errno) {464 resume @handle();
436 0 => {465 }
437 // can't use @bytesToSlice because of the special variable length name field466
438 var ptr = event_buf[0..].ptr;467 const loop = channel.loop;
439 const end_ptr = ptr + event_buf.len;468
440 var ev: *os.linux.inotify_event = undefined;469 var watch = Self{
441 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {470 .putter = @handle(),
442 ev = @ptrCast(*os.linux.inotify_event, ptr);471 .channel = channel,
443 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {472 .wd_table = WdTable.init(loop.allocator),
444 await (async channel.put(Watch.Event.CloseWrite) catch unreachable);473 .table_lock = event.Lock.init(loop),
474 .inotify_fd = inotify_fd,
475 };
476 out_watch.* = &watch;
477
478 loop.beginOneEvent();
479
480 defer {
481 watch.table_lock.deinit();
482 {
483 var wd_it = watch.wd_table.iterator();
484 while (wd_it.next()) |wd_entry| {
485 var file_it = wd_entry.value.file_table.iterator();
486 while (file_it.next()) |file_entry| {
487 loop.allocator.free(file_entry.key);
488 }
489 loop.allocator.free(wd_entry.value.dirname);
445 }490 }
446 }491 }
447 },492 loop.finishOneEvent();
448 os.linux.EINTR => continue,493 os.close(inotify_fd);
449 os.linux.EINVAL => unreachable,494 channel.destroy();
450 os.linux.EFAULT => unreachable,495 }
451 os.linux.EAGAIN => {496
452 (await (async loop.linuxWaitFd(497 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
453 inotify_fd,498
454 os.linux.EPOLLET | os.linux.EPOLLIN,499 while (true) {
455 ) catch unreachable)) catch |err| {500 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
456 const transformed_err = switch (err) {501 const errno = os.linux.getErrno(rc);
457 error.InvalidFileDescriptor => unreachable,502 switch (errno) {
458 error.FileDescriptorAlreadyPresentInSet => unreachable,503 0 => {
459 error.InvalidSyscall => unreachable,504 // can't use @bytesToSlice because of the special variable length name field
460 error.OperationCausesCircularLoop => unreachable,505 var ptr = event_buf[0..].ptr;
461 error.FileDescriptorNotRegistered => unreachable,506 const end_ptr = ptr + event_buf.len;
462 error.SystemResources => error.SystemResources,507 var ev: *os.linux.inotify_event = undefined;
463 error.UserResourceLimitReached => error.UserResourceLimitReached,508 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
464 error.FileDescriptorIncompatibleWithEpoll => unreachable,509 ev = @ptrCast(*os.linux.inotify_event, ptr);
465 error.Unexpected => unreachable,510 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
466 };511 const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
467 await (async channel.put(Watch.Event{ .Err = transformed_err }) catch unreachable);512 const basename_with_null = basename_ptr[0 .. std.cstr.len(basename_ptr) + 1];
468 };513 const user_value = blk: {
469 },514 const held = await (async watch.table_lock.acquire() catch unreachable);
470 else => unreachable,515 defer held.release();
516
517 const dir = &watch.wd_table.get(ev.wd).?.value;
518 if (dir.file_table.get(basename_with_null)) |entry| {
519 break :blk entry.value;
520 } else {
521 break :blk null;
522 }
523 };
524 if (user_value) |v| {
525 await (async channel.put(Self.Event{ .CloseWrite = v }) catch unreachable);
526 }
527 }
528 }
529 },
530 os.linux.EINTR => continue,
531 os.linux.EINVAL => unreachable,
532 os.linux.EFAULT => unreachable,
533 os.linux.EAGAIN => {
534 (await (async loop.linuxWaitFd(
535 inotify_fd,
536 os.linux.EPOLLET | os.linux.EPOLLIN,
537 ) catch unreachable)) catch |err| {
538 const transformed_err = switch (err) {
539 error.InvalidFileDescriptor => unreachable,
540 error.FileDescriptorAlreadyPresentInSet => unreachable,
541 error.InvalidSyscall => unreachable,
542 error.OperationCausesCircularLoop => unreachable,
543 error.FileDescriptorNotRegistered => unreachable,
544 error.SystemResources => error.SystemResources,
545 error.UserResourceLimitReached => error.UserResourceLimitReached,
546 error.FileDescriptorIncompatibleWithEpoll => unreachable,
547 error.Unexpected => unreachable,
548 };
549 await (async channel.put(Self.Event{ .Err = transformed_err }) catch unreachable);
550 };
551 },
552 else => unreachable,
553 }
554 }
471 }555 }
472 }556 };
473}557}
474558
475const test_tmp_dir = "std_event_fs_test";559const test_tmp_dir = "std_event_fs_test";
...@@ -517,9 +601,11 @@ async fn testFsWatch(loop: *event.Loop) !void {...@@ -517,9 +601,11 @@ async fn testFsWatch(loop: *event.Loop) !void {
517 assert(mem.eql(u8, read_contents, contents));601 assert(mem.eql(u8, read_contents, contents));
518602
519 // now watch the file603 // now watch the file
520 var watch = try watchFile(loop, file_path);604 var watch = try Watch(void).create(loop, 0);
521 defer watch.destroy();605 defer watch.destroy();
522606
607 assert((try await try async watch.addFile(file_path, {})) == null);
608
523 const ev = try async watch.channel.get();609 const ev = try async watch.channel.get();
524 var ev_consumed = false;610 var ev_consumed = false;
525 defer if (!ev_consumed) cancel ev;611 defer if (!ev_consumed) cancel ev;
...@@ -534,8 +620,8 @@ async fn testFsWatch(loop: *event.Loop) !void {...@@ -534,8 +620,8 @@ async fn testFsWatch(loop: *event.Loop) !void {
534620
535 ev_consumed = true;621 ev_consumed = true;
536 switch (await ev) {622 switch (await ev) {
537 Watch.Event.CloseWrite => {},623 Watch(void).Event.CloseWrite => {},
538 Watch.Event.Err => |err| return err,624 Watch(void).Event.Err => |err| return err,
539 }625 }
540626
541 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);627 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
std/event/rwlock.zig+2
...@@ -10,6 +10,8 @@ const Loop = std.event.Loop;...@@ -10,6 +10,8 @@ const Loop = std.event.Loop;
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
11/// are resumed when the lock is released, in order.11/// are resumed when the lock is released, in order.
12/// Many readers can hold the lock at the same time; however locking for writing is exclusive.12/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
13/// When a read lock is held, it will not be released until the reader queue is empty.
14/// When a write lock is held, it will not be released until the writer queue is empty.
13pub const RwLock = struct {15pub const RwLock = struct {
14 loop: *Loop,16 loop: *Loop,
15 shared_state: u8, // TODO make this an enum17 shared_state: u8, // TODO make this an enum
std/hash_map.zig+244-54
...@@ -9,6 +9,10 @@ const builtin = @import("builtin");...@@ -9,6 +9,10 @@ const builtin = @import("builtin");
9const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;9const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
10const debug_u32 = if (want_modification_safety) u32 else void;10const debug_u32 = if (want_modification_safety) u32 else void;
1111
12pub fn AutoHashMap(comptime K: type, comptime V: type) type {
13 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K));
14}
15
12pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {16pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
13 return struct {17 return struct {
14 entries: []Entry,18 entries: []Entry,
...@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
2024
21 const Self = this;25 const Self = this;
2226
23 pub const Entry = struct {27 pub const KV = struct {
24 used: bool,
25 distance_from_start_index: usize,
26 key: K,28 key: K,
27 value: V,29 value: V,
28 };30 };
2931
32 const Entry = struct {
33 used: bool,
34 distance_from_start_index: usize,
35 kv: KV,
36 };
37
38 pub const GetOrPutResult = struct {
39 kv: *KV,
40 found_existing: bool,
41 };
42
30 pub const Iterator = struct {43 pub const Iterator = struct {
31 hm: *const Self,44 hm: *const Self,
32 // how many items have we returned45 // how many items have we returned
...@@ -36,7 +49,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -36,7 +49,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
36 // used to detect concurrent modification49 // used to detect concurrent modification
37 initial_modification_count: debug_u32,50 initial_modification_count: debug_u32,
3851
39 pub fn next(it: *Iterator) ?*Entry {52 pub fn next(it: *Iterator) ?*KV {
40 if (want_modification_safety) {53 if (want_modification_safety) {
41 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification54 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
42 }55 }
...@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
46 if (entry.used) {59 if (entry.used) {
47 it.index += 1;60 it.index += 1;
48 it.count += 1;61 it.count += 1;
49 return entry;62 return &entry.kv;
50 }63 }
51 }64 }
52 unreachable; // no next item65 unreachable; // no next item
...@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
71 };84 };
72 }85 }
7386
74 pub fn deinit(hm: *const Self) void {87 pub fn deinit(hm: Self) void {
75 hm.allocator.free(hm.entries);88 hm.allocator.free(hm.entries);
76 }89 }
7790
...@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
84 hm.incrementModificationCount();97 hm.incrementModificationCount();
85 }98 }
8699
87 pub fn count(hm: *const Self) usize {100 pub fn count(self: Self) usize {
88 return hm.size;101 return self.size;
89 }102 }
90103
91 /// Returns the value that was already there.104 /// If key exists this function cannot fail.
92 pub fn put(hm: *Self, key: K, value: *const V) !?V {105 /// If there is an existing item with `key`, then the result
93 if (hm.entries.len == 0) {106 /// kv pointer points to it, and found_existing is true.
94 try hm.initCapacity(16);107 /// Otherwise, puts a new item with undefined value, and
108 /// the kv pointer points to it. Caller should then initialize
109 /// the data.
110 pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {
111 // TODO this implementation can be improved - we should only
112 // have to hash once and find the entry once.
113 if (self.get(key)) |kv| {
114 return GetOrPutResult{
115 .kv = kv,
116 .found_existing = true,
117 };
118 }
119 self.incrementModificationCount();
120 try self.ensureCapacity();
121 const put_result = self.internalPut(key);
122 assert(put_result.old_kv == null);
123 return GetOrPutResult{
124 .kv = &put_result.new_entry.kv,
125 .found_existing = false,
126 };
127 }
128
129 fn ensureCapacity(self: *Self) !void {
130 if (self.entries.len == 0) {
131 return self.initCapacity(16);
95 }132 }
96 hm.incrementModificationCount();
97133
98 // if we get too full (60%), double the capacity134 // if we get too full (60%), double the capacity
99 if (hm.size * 5 >= hm.entries.len * 3) {135 if (self.size * 5 >= self.entries.len * 3) {
100 const old_entries = hm.entries;136 const old_entries = self.entries;
101 try hm.initCapacity(hm.entries.len * 2);137 try self.initCapacity(self.entries.len * 2);
102 // dump all of the old elements into the new table138 // dump all of the old elements into the new table
103 for (old_entries) |*old_entry| {139 for (old_entries) |*old_entry| {
104 if (old_entry.used) {140 if (old_entry.used) {
105 _ = hm.internalPut(old_entry.key, old_entry.value);141 self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value;
106 }142 }
107 }143 }
108 hm.allocator.free(old_entries);144 self.allocator.free(old_entries);
109 }145 }
146 }
110147
111 return hm.internalPut(key, value);148 /// Returns the kv pair that was already there.
149 pub fn put(self: *Self, key: K, value: V) !?KV {
150 self.incrementModificationCount();
151 try self.ensureCapacity();
152
153 const put_result = self.internalPut(key);
154 put_result.new_entry.kv.value = value;
155 return put_result.old_kv;
112 }156 }
113157
114 pub fn get(hm: *const Self, key: K) ?*Entry {158 pub fn get(hm: *const Self, key: K) ?*KV {
115 if (hm.entries.len == 0) {159 if (hm.entries.len == 0) {
116 return null;160 return null;
117 }161 }
...@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
122 return hm.get(key) != null;166 return hm.get(key) != null;
123 }167 }
124168
125 pub fn remove(hm: *Self, key: K) ?*Entry {169 pub fn remove(hm: *Self, key: K) ?*KV {
126 if (hm.entries.len == 0) return null;170 if (hm.entries.len == 0) return null;
127 hm.incrementModificationCount();171 hm.incrementModificationCount();
128 const start_index = hm.keyToIndex(key);172 const start_index = hm.keyToIndex(key);
...@@ -134,7 +178,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -134,7 +178,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
134178
135 if (!entry.used) return null;179 if (!entry.used) return null;
136180
137 if (!eql(entry.key, key)) continue;181 if (!eql(entry.kv.key, key)) continue;
138182
139 while (roll_over < hm.entries.len) : (roll_over += 1) {183 while (roll_over < hm.entries.len) : (roll_over += 1) {
140 const next_index = (start_index + roll_over + 1) % hm.entries.len;184 const next_index = (start_index + roll_over + 1) % hm.entries.len;
...@@ -142,7 +186,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -142,7 +186,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
142 if (!next_entry.used or next_entry.distance_from_start_index == 0) {186 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
143 entry.used = false;187 entry.used = false;
144 hm.size -= 1;188 hm.size -= 1;
145 return entry;189 return &entry.kv;
146 }190 }
147 entry.* = next_entry.*;191 entry.* = next_entry.*;
148 entry.distance_from_start_index -= 1;192 entry.distance_from_start_index -= 1;
...@@ -168,7 +212,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -168,7 +212,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
168 try other.initCapacity(self.entries.len);212 try other.initCapacity(self.entries.len);
169 var it = self.iterator();213 var it = self.iterator();
170 while (it.next()) |entry| {214 while (it.next()) |entry| {
171 try other.put(entry.key, entry.value);215 assert((try other.put(entry.key, entry.value)) == null);
172 }216 }
173 return other;217 return other;
174 }218 }
...@@ -188,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -188,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
188 }232 }
189 }233 }
190234
191 /// Returns the value that was already there.235 const InternalPutResult = struct {
192 fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V {236 new_entry: *Entry,
237 old_kv: ?KV,
238 };
239
240 /// Returns a pointer to the new entry.
241 /// Asserts that there is enough space for the new item.
242 fn internalPut(self: *Self, orig_key: K) InternalPutResult {
193 var key = orig_key;243 var key = orig_key;
194 var value = orig_value.*;244 var value: V = undefined;
195 const start_index = hm.keyToIndex(key);245 const start_index = self.keyToIndex(key);
196 var roll_over: usize = 0;246 var roll_over: usize = 0;
197 var distance_from_start_index: usize = 0;247 var distance_from_start_index: usize = 0;
198 while (roll_over < hm.entries.len) : ({248 var got_result_entry = false;
249 var result = InternalPutResult{
250 .new_entry = undefined,
251 .old_kv = null,
252 };
253 while (roll_over < self.entries.len) : ({
199 roll_over += 1;254 roll_over += 1;
200 distance_from_start_index += 1;255 distance_from_start_index += 1;
201 }) {256 }) {
202 const index = (start_index + roll_over) % hm.entries.len;257 const index = (start_index + roll_over) % self.entries.len;
203 const entry = &hm.entries[index];258 const entry = &self.entries[index];
204259
205 if (entry.used and !eql(entry.key, key)) {260 if (entry.used and !eql(entry.kv.key, key)) {
206 if (entry.distance_from_start_index < distance_from_start_index) {261 if (entry.distance_from_start_index < distance_from_start_index) {
207 // robin hood to the rescue262 // robin hood to the rescue
208 const tmp = entry.*;263 const tmp = entry.*;
209 hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index, distance_from_start_index);264 self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index);
265 if (!got_result_entry) {
266 got_result_entry = true;
267 result.new_entry = entry;
268 }
210 entry.* = Entry{269 entry.* = Entry{
211 .used = true,270 .used = true,
212 .distance_from_start_index = distance_from_start_index,271 .distance_from_start_index = distance_from_start_index,
213 .key = key,272 .kv = KV{
214 .value = value,273 .key = key,
274 .value = value,
275 },
215 };276 };
216 key = tmp.key;277 key = tmp.kv.key;
217 value = tmp.value;278 value = tmp.kv.value;
218 distance_from_start_index = tmp.distance_from_start_index;279 distance_from_start_index = tmp.distance_from_start_index;
219 }280 }
220 continue;281 continue;
221 }282 }
222283
223 var result: ?V = null;
224 if (entry.used) {284 if (entry.used) {
225 result = entry.value;285 result.old_kv = entry.kv;
226 } else {286 } else {
227 // adding an entry. otherwise overwriting old value with287 // adding an entry. otherwise overwriting old value with
228 // same key288 // same key
229 hm.size += 1;289 self.size += 1;
230 }290 }
231291
232 hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);292 self.max_distance_from_start_index = math.max(distance_from_start_index, self.max_distance_from_start_index);
293 if (!got_result_entry) {
294 result.new_entry = entry;
295 }
233 entry.* = Entry{296 entry.* = Entry{
234 .used = true,297 .used = true,
235 .distance_from_start_index = distance_from_start_index,298 .distance_from_start_index = distance_from_start_index,
236 .key = key,299 .kv = KV{
237 .value = value,300 .key = key,
301 .value = value,
302 },
238 };303 };
239 return result;304 return result;
240 }305 }
241 unreachable; // put into a full map306 unreachable; // put into a full map
242 }307 }
243308
244 fn internalGet(hm: *const Self, key: K) ?*Entry {309 fn internalGet(hm: Self, key: K) ?*KV {
245 const start_index = hm.keyToIndex(key);310 const start_index = hm.keyToIndex(key);
246 {311 {
247 var roll_over: usize = 0;312 var roll_over: usize = 0;
...@@ -250,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -250,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
250 const entry = &hm.entries[index];315 const entry = &hm.entries[index];
251316
252 if (!entry.used) return null;317 if (!entry.used) return null;
253 if (eql(entry.key, key)) return entry;318 if (eql(entry.kv.key, key)) return &entry.kv;
254 }319 }
255 }320 }
256 return null;321 return null;
257 }322 }
258323
259 fn keyToIndex(hm: *const Self, key: K) usize {324 fn keyToIndex(hm: Self, key: K) usize {
260 return usize(hash(key)) % hm.entries.len;325 return usize(hash(key)) % hm.entries.len;
261 }326 }
262 };327 };
...@@ -266,7 +331,7 @@ test "basic hash map usage" {...@@ -266,7 +331,7 @@ test "basic hash map usage" {
266 var direct_allocator = std.heap.DirectAllocator.init();331 var direct_allocator = std.heap.DirectAllocator.init();
267 defer direct_allocator.deinit();332 defer direct_allocator.deinit();
268333
269 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);334 var map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
270 defer map.deinit();335 defer map.deinit();
271336
272 assert((try map.put(1, 11)) == null);337 assert((try map.put(1, 11)) == null);
...@@ -275,8 +340,19 @@ test "basic hash map usage" {...@@ -275,8 +340,19 @@ test "basic hash map usage" {
275 assert((try map.put(4, 44)) == null);340 assert((try map.put(4, 44)) == null);
276 assert((try map.put(5, 55)) == null);341 assert((try map.put(5, 55)) == null);
277342
278 assert((try map.put(5, 66)).? == 55);343 assert((try map.put(5, 66)).?.value == 55);
279 assert((try map.put(5, 55)).? == 66);344 assert((try map.put(5, 55)).?.value == 66);
345
346 const gop1 = try map.getOrPut(5);
347 assert(gop1.found_existing == true);
348 assert(gop1.kv.value == 55);
349 gop1.kv.value = 77;
350 assert(map.get(5).?.value == 77);
351
352 const gop2 = try map.getOrPut(99);
353 assert(gop2.found_existing == false);
354 gop2.kv.value = 42;
355 assert(map.get(99).?.value == 42);
280356
281 assert(map.contains(2));357 assert(map.contains(2));
282 assert(map.get(2).?.value == 22);358 assert(map.get(2).?.value == 22);
...@@ -289,7 +365,7 @@ test "iterator hash map" {...@@ -289,7 +365,7 @@ test "iterator hash map" {
289 var direct_allocator = std.heap.DirectAllocator.init();365 var direct_allocator = std.heap.DirectAllocator.init();
290 defer direct_allocator.deinit();366 defer direct_allocator.deinit();
291367
292 var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);368 var reset_map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
293 defer reset_map.deinit();369 defer reset_map.deinit();
294370
295 assert((try reset_map.put(1, 11)) == null);371 assert((try reset_map.put(1, 11)) == null);
...@@ -332,10 +408,124 @@ test "iterator hash map" {...@@ -332,10 +408,124 @@ test "iterator hash map" {
332 assert(entry.value == values[0]);408 assert(entry.value == values[0]);
333}409}
334410
335fn hash_i32(x: i32) u32 {411pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
336 return @bitCast(u32, x);412 return struct {
413 fn hash(key: K) u32 {
414 comptime var rng = comptime std.rand.DefaultPrng.init(0);
415 return autoHash(key, &rng.random, u32);
416 }
417 }.hash;
418}
419
420pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
421 return struct {
422 fn eql(a: K, b: K) bool {
423 return autoEql(a, b);
424 }
425 }.eql;
426}
427
428// TODO improve these hash functions
429pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type) HashInt {
430 switch (@typeInfo(@typeOf(key))) {
431 builtin.TypeId.NoReturn,
432 builtin.TypeId.Opaque,
433 builtin.TypeId.Undefined,
434 builtin.TypeId.ArgTuple,
435 => @compileError("cannot hash this type"),
436
437 builtin.TypeId.Void,
438 builtin.TypeId.Null,
439 => return 0,
440
441 builtin.TypeId.Int => |info| {
442 const unsigned_x = @bitCast(@IntType(false, info.bits), key);
443 if (info.bits <= HashInt.bit_count) {
444 return HashInt(unsigned_x) *% comptime rng.scalar(HashInt);
445 } else {
446 return @truncate(HashInt, unsigned_x *% comptime rng.scalar(@typeOf(unsigned_x)));
447 }
448 },
449
450 builtin.TypeId.Float => |info| {
451 return autoHash(@bitCast(@IntType(false, info.bits), key), rng);
452 },
453 builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng),
454 builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng),
455 builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng),
456 builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng),
457
458 builtin.TypeId.Namespace,
459 builtin.TypeId.Block,
460 builtin.TypeId.BoundFn,
461 builtin.TypeId.ComptimeFloat,
462 builtin.TypeId.ComptimeInt,
463 builtin.TypeId.Type,
464 => return 0,
465
466 builtin.TypeId.Pointer => |info| switch (info.size) {
467 builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"),
468 builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"),
469 builtin.TypeInfo.Pointer.Size.Slice => {
470 const interval = std.math.max(1, key.len / 256);
471 var i: usize = 0;
472 var h = comptime rng.scalar(HashInt);
473 while (i < key.len) : (i += interval) {
474 h ^= autoHash(key[i], rng, HashInt);
475 }
476 return h;
477 },
478 },
479
480 builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
481 builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
482 builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
483 builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
484 builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
485 }
337}486}
338487
339fn eql_i32(a: i32, b: i32) bool {488pub fn autoEql(a: var, b: @typeOf(a)) bool {
340 return a == b;489 switch (@typeInfo(@typeOf(a))) {
490 builtin.TypeId.NoReturn,
491 builtin.TypeId.Opaque,
492 builtin.TypeId.Undefined,
493 builtin.TypeId.ArgTuple,
494 => @compileError("cannot test equality of this type"),
495 builtin.TypeId.Void,
496 builtin.TypeId.Null,
497 => return true,
498 builtin.TypeId.Bool,
499 builtin.TypeId.Int,
500 builtin.TypeId.Float,
501 builtin.TypeId.ComptimeFloat,
502 builtin.TypeId.ComptimeInt,
503 builtin.TypeId.Namespace,
504 builtin.TypeId.Block,
505 builtin.TypeId.Promise,
506 builtin.TypeId.Enum,
507 builtin.TypeId.BoundFn,
508 builtin.TypeId.Fn,
509 builtin.TypeId.ErrorSet,
510 builtin.TypeId.Type,
511 => return a == b,
512
513 builtin.TypeId.Pointer => |info| switch (info.size) {
514 builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"),
515 builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"),
516 builtin.TypeInfo.Pointer.Size.Slice => {
517 if (a.len != b.len) return false;
518 for (a) |a_item, i| {
519 if (!autoEql(a_item, b[i])) return false;
520 }
521 return true;
522 },
523 },
524
525 builtin.TypeId.Optional => @compileError("TODO auto eql for optionals"),
526 builtin.TypeId.Array => @compileError("TODO auto eql for arrays"),
527 builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
528 builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
529 builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
530 }
341}531}
std/index.zig+1
...@@ -5,6 +5,7 @@ pub const BufSet = @import("buf_set.zig").BufSet;...@@ -5,6 +5,7 @@ pub const BufSet = @import("buf_set.zig").BufSet;
5pub const Buffer = @import("buffer.zig").Buffer;5pub const Buffer = @import("buffer.zig").Buffer;
6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
7pub const HashMap = @import("hash_map.zig").HashMap;7pub const HashMap = @import("hash_map.zig").HashMap;
8pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
8pub const LinkedList = @import("linked_list.zig").LinkedList;9pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const SegmentedList = @import("segmented_list.zig").SegmentedList;10pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
10pub const DynLib = @import("dynamic_library.zig").DynLib;11pub const DynLib = @import("dynamic_library.zig").DynLib;
std/json.zig+1-1
...@@ -1318,7 +1318,7 @@ pub const Parser = struct {...@@ -1318,7 +1318,7 @@ pub const Parser = struct {
1318 _ = p.stack.pop();1318 _ = p.stack.pop();
13191319
1320 var object = &p.stack.items[p.stack.len - 1].Object;1320 var object = &p.stack.items[p.stack.len - 1].Object;
1321 _ = try object.put(key, value);1321 _ = try object.put(key, value.*);
1322 p.state = State.ObjectKey;1322 p.state = State.ObjectKey;
1323 },1323 },
1324 // Array Parent -> [ ..., <array>, value ]1324 // Array Parent -> [ ..., <array>, value ]
std/special/build_runner.zig+2-2
...@@ -72,10 +72,10 @@ pub fn main() !void {...@@ -72,10 +72,10 @@ pub fn main() !void {
72 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {72 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
73 const option_name = option_contents[0..name_end];73 const option_name = option_contents[0..name_end];
74 const option_value = option_contents[name_end + 1 ..];74 const option_value = option_contents[name_end + 1 ..];
75 if (builder.addUserInputOption(option_name, option_value))75 if (try builder.addUserInputOption(option_name, option_value))
76 return usageAndErr(&builder, false, try stderr_stream);76 return usageAndErr(&builder, false, try stderr_stream);
77 } else {77 } else {
78 if (builder.addUserInputFlag(option_contents))78 if (try builder.addUserInputFlag(option_contents))
79 return usageAndErr(&builder, false, try stderr_stream);79 return usageAndErr(&builder, false, try stderr_stream);
80 }80 }
81 } else if (mem.startsWith(u8, arg, "-")) {81 } else if (mem.startsWith(u8, arg, "-")) {