| author | |
| committer | |
| log | 5dfcd09e496160054d4f333500d084e35f2fbdd2 |
| tree | 08f4177b091848c1b78ec8e7c1d7973ee230391a |
| parent | 7f6e97cb26ffabbc192e7ccdf44aebbbc3be751d |
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 { |
| 230 | 230 | ||
| 231 | c_int_types: [CInt.list.len]*Type.Int, | 231 | c_int_types: [CInt.list.len]*Type.Int, |
| 232 | 232 | ||
| 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 | }; |
| 289 | 292 | ||
| 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()); | ||
| 437 | 441 | ||
| 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 | } |
| 487 | 491 | ||
| 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(); |
| 489 | 496 | ||
| 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 |_| {}; |
| 688 | 695 | ||
| 696 | self.fs_watch.destroy(); | ||
| 689 | self.events.destroy(); | 697 | self.events.destroy(); |
| 690 | 698 | ||
| 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); |
| 721 | 729 | ||
| 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 mode | 734 | // 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 | } |
| 747 | 757 | ||
| 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); |
| 767 | 803 | ||
| ... | @@ -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(); |
| 795 | 831 | ||
| 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 { |
| 824 | 860 | ||
| 825 | // TODO connect existing comptime decls to updated source files | 861 | // TODO connect existing comptime decls to updated source files |
| 826 | 862 | ||
| 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); |
| 832 | 868 | ||
| 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); |
| 864 | 902 | ||
| 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.real | 922 | // 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); |
| 890 | 928 | ||
| ... | @@ -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); |
| 894 | 932 | ||
| 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 | } |
| 898 | 937 | ||
| ... | @@ -917,6 +956,7 @@ pub const Compilation = struct { | ... | @@ -917,6 +956,7 @@ pub const Compilation = struct { |
| 917 | /// caller takes ownership of resulting Code | 956 | /// 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 { |
| 950 | 991 | ||
| 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 { |
| 958 | 1000 | ||
| 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); | ||
| 980 | 1022 | ||
| 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 | } |
| 985 | 1027 | ||
| 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 here | 1031 | // TODO note: other definition here |
| 1032 | } else { | ||
| 1033 | gop.kv.value = decl; | ||
| 989 | } | 1034 | } |
| 990 | } | 1035 | } |
| 991 | 1036 | ||
| 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); |
| 995 | 1050 | ||
| 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(); |
| 998 | 1053 | ||
| 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 { |
| 1017 | 1072 | ||
| 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 | } |
| 1142 | 1197 | ||
| 1143 | /// Returns a value which has been ref()'d once | 1198 | /// 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()); |
| 1147 | 1208 | ||
| 1148 | return analyzed_code.getCompTimeResult(comp); | 1209 | return analyzed_code.getCompTimeResult(comp); |
| 1149 | } | 1210 | } |
| 1150 | 1211 | ||
| 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); |
| 1154 | 1215 | ||
| 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); |
| 1157 | 1218 | ||
| 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 | }; |
| 1170 | 1231 | ||
| 1171 | fn 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 | |||
| 1178 | fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib { | 1232 | fn 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 | } |
| 1199 | 1253 | ||
| 1200 | async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { | 1254 | async 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); |
| 1202 | 1258 | ||
| 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); |
| 1205 | 1261 | ||
| 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); |
| 1208 | 1264 | ||
| 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; |
| 1217 | 1273 | ||
| 1218 | // Define local parameter variables | 1274 | // 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); |
| 1231 | 1286 | ||
| 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 | } |
| 1250 | 1305 | ||
| 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 | } |
| 1281 | 1337 | ||
| 1282 | async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn { | 1338 | async 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); |
| 1289 | 1350 | ||
| 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 | } |
| 1338 | 1399 | ||
| 1339 | async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { | 1400 | async 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); |
| 1342 | 1408 | ||
| 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, |
| 20 | 22 | ||
| 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 | }; |
| 34 | 34 | ||
| 35 | pub const Msg = struct { | 35 | pub const Msg = struct { |
| 36 | span: Span, | ||
| 37 | text: []u8, | 36 | text: []u8, |
| 37 | realpath: []u8, | ||
| 38 | data: Data, | 38 | data: Data, |
| 39 | 39 | ||
| 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 | }; |
| 44 | 45 | ||
| 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 | }; |
| 50 | 51 | ||
| 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 | }; |
| 55 | 57 | ||
| 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 { |
| 69 | 82 | ||
| 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 | } |
| 80 | 94 | ||
| 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 | } |
| 102 | 106 | ||
| 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 text | 115 | /// Takes ownership of text |
| 104 | /// References tree_scope, and derefs when the msg is freed | 116 | /// 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 | } |
| 119 | 135 | ||
| 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(); |
| 128 | 160 | ||
| 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); |
| 131 | 166 | ||
| 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(); |
| 163 | 199 | ||
| 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); |
| 166 | 205 | ||
| 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 | } |
| 186 | 225 | ||
| 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(); |
| 191 | 237 | ||
| 192 | const cwd = try os.getCwd(allocator); | 238 | const cwd = try os.getCwd(allocator); |
| 193 | defer allocator.free(cwd); | 239 | defer allocator.free(cwd); |
| 194 | 240 | ||
| 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); |
| 197 | 243 | ||
| 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(); | ||
| 199 | 246 | ||
| 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, | ||
| 964 | 965 | ||
| 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, |
| 1020 | 1020 | ||
| 1021 | pub const Error = Analyze.Error; | 1021 | pub const Error = Analyze.Error; |
| 1022 | 1022 | ||
| 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()); |
| 1031 | 1032 | ||
| 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 | } |
| 1303 | 1304 | ||
| 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); |
| 1306 | 1307 | ||
| 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 | } |
| 1342 | 1343 | ||
| 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); |
| 1346 | 1347 | ||
| 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 { |
| 1427 | 1428 | ||
| 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 scope | 1430 | // 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 { |
| 1599 | 1600 | ||
| 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); |
| 1603 | 1604 | ||
| 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 scope | 1657 | // 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 type | 1658 | // so that future references to this same name will find a variable with an invalid type |
| 1658 | 1659 | ||
| 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 | } |
| 1662 | 1663 | ||
| ... | @@ -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, |
| 1690 | 1691 | ||
| 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, |
| 1741 | 1743 | ||
| 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 | }; |
| 1970 | 1973 | ||
| 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(); |
| 1974 | 1977 | ||
| 1975 | return Analyze{ | 1978 | return Analyze{ |
| ... | @@ -2047,7 +2050,7 @@ const Analyze = struct { | ... | @@ -2047,7 +2050,7 @@ const Analyze = struct { |
| 2047 | } | 2050 | } |
| 2048 | 2051 | ||
| 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 | } |
| 2052 | 2055 | ||
| 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 { |
| 2535 | pub async fn gen( | 2538 | pub 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(); |
| 2542 | 2546 | ||
| 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( |
| 2555 | 2559 | ||
| 2556 | pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { | 2560 | pub 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(); | ||
| 2559 | 2562 | ||
| 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(); |
| 2562 | 2565 | ||
| 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; |
| 24 | var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; | 24 | var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; |
| 25 | var stdout: *io.OutStream(io.FileOutStream.Error) = undefined; | 25 | var stdout: *io.OutStream(io.FileOutStream.Error) = undefined; |
| 26 | 26 | ||
| 27 | const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB | ||
| 28 | |||
| 27 | const usage = | 29 | const 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 | } |
| 72 | 74 | ||
| 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 | } |
| 473 | 475 | ||
| 474 | async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { | 476 | async 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 elision | 479 | // 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; | ||
| 478 | 482 | ||
| 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); |
| 616 | 623 | ||
| 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); |
| 619 | 626 | ||
| 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 | } |
| 719 | 723 | ||
| 720 | async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { | 724 | 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,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(); |
| 739 | 744 | ||
| ... | @@ -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 | } |
| 776 | 781 | ||
| 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(); |
| 779 | 785 |
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 | } |
| 133 | 137 | ||
| 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 { |
| 144 | 149 | ||
| 145 | /// Creates a scope with 1 reference | 150 | /// 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); |
| 154 | 159 | ||
| 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; |
| 16117 | 16120 | ||
| 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 | } |
| 426 | 426 | ||
| 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 exists | 433 | .used = false, |
| 434 | switch (prev_value.value) { | 434 | }; |
| 435 | UserValue.Scalar => |s| { | 435 | return false; |
| 436 | // turn it into a list | 436 | } |
| 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 list | 447 | .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 | } |
| 463 | 467 | ||
| 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 | } |
| 369 | 369 | ||
| 370 | pub const Watch = struct { | 370 | pub fn Watch(comptime V: type) type { |
| 371 | channel: *event.Channel(Event), | 371 | return struct { |
| 372 | putter: promise, | 372 | channel: *event.Channel(Event), |
| 373 | 373 | 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 | |
| 378 | 378 | 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 | |
| 383 | 383 | const Dir = struct { | |
| 384 | pub fn destroy(self: *Watch) void { | 384 | dirname: []const u8, |
| 385 | // TODO https://github.com/ziglang/zig/issues/1261 | 385 | file_table: FileTable, |
| 386 | cancel self.putter; | 386 | }; |
| 387 | } | ||
| 388 | }; | ||
| 389 | |||
| 390 | pub 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); | ||
| 393 | 387 | ||
| 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, | ||
| 396 | 391 | ||
| 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 | }; | ||
| 399 | 397 | ||
| 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); | ||
| 402 | 401 | ||
| 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 | |||
| 408 | async 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 | } | ||
| 413 | 404 | ||
| 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; | ||
| 419 | 409 | ||
| 420 | const loop = channel.loop; | 410 | pub fn destroy(self: *Self) void { |
| 421 | loop.beginOneEvent(); | 411 | cancel self.putter; |
| 412 | } | ||
| 422 | 413 | ||
| 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 | } | ||
| 429 | 456 | ||
| 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 | } | ||
| 431 | 460 | ||
| 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 field | 466 | |
| 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 | } |
| 474 | 558 | ||
| 475 | const test_tmp_dir = "std_event_fs_test"; | 559 | const 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)); |
| 518 | 602 | ||
| 519 | // now watch the file | 603 | // 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(); |
| 522 | 606 | ||
| 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 { |
| 534 | 620 | ||
| 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 | } |
| 540 | 626 | ||
| 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, and | 10 | /// 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. | ||
| 13 | pub const RwLock = struct { | 15 | pub const RwLock = struct { |
| 14 | loop: *Loop, | 16 | loop: *Loop, |
| 15 | shared_state: u8, // TODO make this an enum | 17 | 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"); |
| 9 | const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast; | 9 | const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast; |
| 10 | const debug_u32 = if (want_modification_safety) u32 else void; | 10 | const debug_u32 = if (want_modification_safety) u32 else void; |
| 11 | 11 | ||
| 12 | pub fn AutoHashMap(comptime K: type, comptime V: type) type { | ||
| 13 | return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K)); | ||
| 14 | } | ||
| 15 | |||
| 12 | pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type { | 16 | pub 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 |
| 20 | 24 | ||
| 21 | const Self = this; | 25 | const Self = this; |
| 22 | 26 | ||
| 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 | }; |
| 29 | 31 | ||
| 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 returned | 45 | // 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 modification | 49 | // used to detect concurrent modification |
| 37 | initial_modification_count: debug_u32, | 50 | initial_modification_count: debug_u32, |
| 38 | 51 | ||
| 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 modification | 54 | 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 item | 65 | 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 | } |
| 73 | 86 | ||
| 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 | } |
| 77 | 90 | ||
| ... | @@ -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 | } |
| 86 | 99 | ||
| 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 | } |
| 90 | 103 | ||
| 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(); | ||
| 97 | 133 | ||
| 98 | // if we get too full (60%), double the capacity | 134 | // 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 table | 138 | // 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 | } | ||
| 110 | 147 | ||
| 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 | } |
| 113 | 157 | ||
| 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 | } |
| 124 | 168 | ||
| 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 |
| 134 | 178 | ||
| 135 | if (!entry.used) return null; | 179 | if (!entry.used) return null; |
| 136 | 180 | ||
| 137 | if (!eql(entry.key, key)) continue; | 181 | if (!eql(entry.kv.key, key)) continue; |
| 138 | 182 | ||
| 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 | } |
| 190 | 234 | ||
| 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]; |
| 204 | 259 | ||
| 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 rescue | 262 | // 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 | } |
| 222 | 283 | ||
| 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 with | 287 | // adding an entry. otherwise overwriting old value with |
| 228 | // same key | 288 | // same key |
| 229 | hm.size += 1; | 289 | self.size += 1; |
| 230 | } | 290 | } |
| 231 | 291 | ||
| 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 map | 306 | unreachable; // put into a full map |
| 242 | } | 307 | } |
| 243 | 308 | ||
| 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]; |
| 251 | 316 | ||
| 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 | } |
| 258 | 323 | ||
| 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(); |
| 268 | 333 | ||
| 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(); |
| 271 | 336 | ||
| 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); |
| 277 | 342 | ||
| 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); | ||
| 280 | 356 | ||
| 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(); |
| 291 | 367 | ||
| 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(); |
| 294 | 370 | ||
| 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 | } |
| 334 | 410 | ||
| 335 | fn hash_i32(x: i32) u32 { | 411 | pub 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 | |||
| 420 | pub 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 | ||
| 429 | pub 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 | } |
| 338 | 487 | ||
| 339 | fn eql_i32(a: i32, b: i32) bool { | 488 | pub 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; |
| 5 | pub const Buffer = @import("buffer.zig").Buffer; | 5 | pub const Buffer = @import("buffer.zig").Buffer; |
| 6 | pub const BufferOutStream = @import("buffer.zig").BufferOutStream; | 6 | pub const BufferOutStream = @import("buffer.zig").BufferOutStream; |
| 7 | pub const HashMap = @import("hash_map.zig").HashMap; | 7 | pub const HashMap = @import("hash_map.zig").HashMap; |
| 8 | pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; | ||
| 8 | pub const LinkedList = @import("linked_list.zig").LinkedList; | 9 | pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 9 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | 10 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 10 | pub const DynLib = @import("dynamic_library.zig").DynLib; | 11 | pub 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(); |
| 1319 | 1319 | ||
| 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, "-")) { |