| ... | @@ -74,9 +74,9 @@ const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope | ... | @@ -74,9 +74,9 @@ const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope |
| 74 | const WorkItem = union(enum) { | 74 | const WorkItem = union(enum) { |
| 75 | /// Write the machine code for a Decl to the output file. | 75 | /// Write the machine code for a Decl to the output file. |
| 76 | codegen_decl: *Decl, | 76 | codegen_decl: *Decl, |
| 77 | /// Decl has been determined to be outdated; perform semantic analysis again. | | |
| 78 | re_analyze_decl: *Decl, | | |
| 79 | /// The Decl needs to be analyzed and possibly export itself. | 77 | /// The Decl needs to be analyzed and possibly export itself. |
| | 78 | /// It may have already be analyzed, or it may have been determined |
| | 79 | /// to be outdated; in this case perform semantic analysis again. |
| 80 | analyze_decl: *Decl, | 80 | analyze_decl: *Decl, |
| 81 | }; | 81 | }; |
| 82 | | 82 | |
| ... | @@ -403,6 +403,17 @@ pub const Scope = struct { | ... | @@ -403,6 +403,17 @@ pub const Scope = struct { |
| 403 | } | 403 | } |
| 404 | } | 404 | } |
| 405 | | 405 | |
| | 406 | /// Asserts the scope is a namespace Scope and removes the Decl from the namespace. |
| | 407 | pub fn removeDecl(base: *Scope, child: *Decl) void { |
| | 408 | switch (base.tag) { |
| | 409 | .file => return @fieldParentPtr(File, "base", base).removeDecl(child), |
| | 410 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), |
| | 411 | .block => unreachable, |
| | 412 | .gen_zir => unreachable, |
| | 413 | .decl => unreachable, |
| | 414 | } |
| | 415 | } |
| | 416 | |
| 406 | /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it. | 417 | /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it. |
| 407 | pub fn destroy(base: *Scope, allocator: *Allocator) void { | 418 | pub fn destroy(base: *Scope, allocator: *Allocator) void { |
| 408 | switch (base.tag) { | 419 | switch (base.tag) { |
| ... | @@ -462,6 +473,9 @@ pub const Scope = struct { | ... | @@ -462,6 +473,9 @@ pub const Scope = struct { |
| 462 | loaded_success, | 473 | loaded_success, |
| 463 | }, | 474 | }, |
| 464 | | 475 | |
| | 476 | /// Direct children of the file. |
| | 477 | decls: ArrayListUnmanaged(*Decl), |
| | 478 | |
| 465 | pub fn unload(self: *File, allocator: *Allocator) void { | 479 | pub fn unload(self: *File, allocator: *Allocator) void { |
| 466 | switch (self.status) { | 480 | switch (self.status) { |
| 467 | .never_loaded, | 481 | .never_loaded, |
| ... | @@ -484,10 +498,20 @@ pub const Scope = struct { | ... | @@ -484,10 +498,20 @@ pub const Scope = struct { |
| 484 | } | 498 | } |
| 485 | | 499 | |
| 486 | pub fn deinit(self: *File, allocator: *Allocator) void { | 500 | pub fn deinit(self: *File, allocator: *Allocator) void { |
| | 501 | self.decls.deinit(allocator); |
| 487 | self.unload(allocator); | 502 | self.unload(allocator); |
| 488 | self.* = undefined; | 503 | self.* = undefined; |
| 489 | } | 504 | } |
| 490 | | 505 | |
| | 506 | pub fn removeDecl(self: *File, child: *Decl) void { |
| | 507 | for (self.decls.items) |item, i| { |
| | 508 | if (item == child) { |
| | 509 | _ = self.decls.swapRemove(i); |
| | 510 | return; |
| | 511 | } |
| | 512 | } |
| | 513 | } |
| | 514 | |
| 491 | pub fn dumpSrc(self: *File, src: usize) void { | 515 | pub fn dumpSrc(self: *File, src: usize) void { |
| 492 | const loc = std.zig.findLineColumn(self.source.bytes, src); | 516 | const loc = std.zig.findLineColumn(self.source.bytes, src); |
| 493 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | 517 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); |
| ... | @@ -540,6 +564,11 @@ pub const Scope = struct { | ... | @@ -540,6 +564,11 @@ pub const Scope = struct { |
| 540 | loaded_success, | 564 | loaded_success, |
| 541 | }, | 565 | }, |
| 542 | | 566 | |
| | 567 | /// Even though .zir files only have 1 module, this set is still needed |
| | 568 | /// because of anonymous Decls, which can exist in the global set, but |
| | 569 | /// not this one. |
| | 570 | decls: ArrayListUnmanaged(*Decl), |
| | 571 | |
| 543 | pub fn unload(self: *ZIRModule, allocator: *Allocator) void { | 572 | pub fn unload(self: *ZIRModule, allocator: *Allocator) void { |
| 544 | switch (self.status) { | 573 | switch (self.status) { |
| 545 | .never_loaded, | 574 | .never_loaded, |
| ... | @@ -569,10 +598,20 @@ pub const Scope = struct { | ... | @@ -569,10 +598,20 @@ pub const Scope = struct { |
| 569 | } | 598 | } |
| 570 | | 599 | |
| 571 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { | 600 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { |
| | 601 | self.decls.deinit(allocator); |
| 572 | self.unload(allocator); | 602 | self.unload(allocator); |
| 573 | self.* = undefined; | 603 | self.* = undefined; |
| 574 | } | 604 | } |
| 575 | | 605 | |
| | 606 | pub fn removeDecl(self: *ZIRModule, child: *Decl) void { |
| | 607 | for (self.decls.items) |item, i| { |
| | 608 | if (item == child) { |
| | 609 | _ = self.decls.swapRemove(i); |
| | 610 | return; |
| | 611 | } |
| | 612 | } |
| | 613 | } |
| | 614 | |
| 576 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { | 615 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { |
| 577 | const loc = std.zig.findLineColumn(self.source.bytes, src); | 616 | const loc = std.zig.findLineColumn(self.source.bytes, src); |
| 578 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | 617 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); |
| ... | @@ -700,6 +739,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { | ... | @@ -700,6 +739,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 700 | .source = .{ .unloaded = {} }, | 739 | .source = .{ .unloaded = {} }, |
| 701 | .contents = .{ .not_available = {} }, | 740 | .contents = .{ .not_available = {} }, |
| 702 | .status = .never_loaded, | 741 | .status = .never_loaded, |
| | 742 | .decls = .{}, |
| 703 | }; | 743 | }; |
| 704 | break :blk &root_scope.base; | 744 | break :blk &root_scope.base; |
| 705 | } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) { | 745 | } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) { |
| ... | @@ -709,6 +749,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { | ... | @@ -709,6 +749,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 709 | .source = .{ .unloaded = {} }, | 749 | .source = .{ .unloaded = {} }, |
| 710 | .contents = .{ .not_available = {} }, | 750 | .contents = .{ .not_available = {} }, |
| 711 | .status = .never_loaded, | 751 | .status = .never_loaded, |
| | 752 | .decls = .{}, |
| 712 | }; | 753 | }; |
| 713 | break :blk &root_scope.base; | 754 | break :blk &root_scope.base; |
| 714 | } else { | 755 | } else { |
| ... | @@ -828,13 +869,14 @@ pub fn update(self: *Module) !void { | ... | @@ -828,13 +869,14 @@ pub fn update(self: *Module) !void { |
| 828 | try self.performAllTheWork(); | 869 | try self.performAllTheWork(); |
| 829 | | 870 | |
| 830 | // Process the deletion set. | 871 | // Process the deletion set. |
| 831 | while (self.deletion_set.popOrNull()) |decl| { | 872 | for (self.deletion_set.items) |decl| { |
| 832 | if (decl.dependants.items.len != 0) { | 873 | if (decl.dependants.items.len != 0) { |
| 833 | decl.deletion_flag = false; | 874 | decl.deletion_flag = false; |
| 834 | continue; | 875 | continue; |
| 835 | } | 876 | } |
| 836 | try self.deleteDecl(decl); | 877 | try self.deleteDecl(decl); |
| 837 | } | 878 | } |
| | 879 | self.deletion_set.shrink(self.allocator, 0); |
| 838 | | 880 | |
| 839 | self.link_error_flags = self.bin_file.error_flags; | 881 | self.link_error_flags = self.bin_file.error_flags; |
| 840 | | 882 | |
| ... | @@ -969,49 +1011,6 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | ... | @@ -969,49 +1011,6 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 969 | }; | 1011 | }; |
| 970 | }, | 1012 | }, |
| 971 | }, | 1013 | }, |
| 972 | .re_analyze_decl => |decl| switch (decl.analysis) { | | |
| 973 | .unreferenced => unreachable, | | |
| 974 | .in_progress => unreachable, | | |
| 975 | | | |
| 976 | .sema_failure, | | |
| 977 | .codegen_failure, | | |
| 978 | .dependency_failure, | | |
| 979 | .complete, | | |
| 980 | .codegen_failure_retryable, | | |
| 981 | .sema_failure_retryable, | | |
| 982 | => continue, | | |
| 983 | | | |
| 984 | .outdated => { | | |
| 985 | if (decl.scope.cast(Scope.File)) |file_scope| { | | |
| 986 | @panic("TODO re_analyze_decl for .zig files"); | | |
| 987 | } else if (decl.scope.cast(Scope.ZIRModule)) |zir_scope| { | | |
| 988 | const zir_module = self.getSrcModule(zir_scope) catch |err| switch (err) { | | |
| 989 | error.OutOfMemory => return error.OutOfMemory, | | |
| 990 | else => { | | |
| 991 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | | |
| 992 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | | |
| 993 | self.allocator, | | |
| 994 | decl.src(), | | |
| 995 | "unable to load source file '{}': {}", | | |
| 996 | .{ zir_scope.sub_file_path, @errorName(err) }, | | |
| 997 | )); | | |
| 998 | decl.analysis = .codegen_failure_retryable; | | |
| 999 | continue; | | |
| 1000 | }, | | |
| 1001 | }; | | |
| 1002 | const decl_name = mem.spanZ(decl.name); | | |
| 1003 | // We already detected deletions, so we know this will be found. | | |
| 1004 | const src_decl_and_index = zir_module.findDecl(decl_name).?; | | |
| 1005 | decl.src_index = src_decl_and_index.index; | | |
| 1006 | self.reAnalyzeDecl(decl, src_decl_and_index.decl.inst) catch |err| switch (err) { | | |
| 1007 | error.OutOfMemory => return error.OutOfMemory, | | |
| 1008 | error.AnalysisFail => continue, | | |
| 1009 | }; | | |
| 1010 | } else { | | |
| 1011 | unreachable; | | |
| 1012 | } | | |
| 1013 | }, | | |
| 1014 | }, | | |
| 1015 | .analyze_decl => |decl| { | 1014 | .analyze_decl => |decl| { |
| 1016 | self.ensureDeclAnalyzed(decl) catch |err| switch (err) { | 1015 | self.ensureDeclAnalyzed(decl) catch |err| switch (err) { |
| 1017 | error.OutOfMemory => return error.OutOfMemory, | 1016 | error.OutOfMemory => return error.OutOfMemory, |
| ... | @@ -1022,9 +1021,12 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | ... | @@ -1022,9 +1021,12 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1022 | } | 1021 | } |
| 1023 | | 1022 | |
| 1024 | fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | 1023 | fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 1025 | switch (decl.analysis) { | 1024 | const tracy = trace(@src()); |
| | 1025 | defer tracy.end(); |
| | 1026 | |
| | 1027 | const subsequent_analysis = switch (decl.analysis) { |
| | 1028 | .complete => return, |
| 1026 | .in_progress => unreachable, | 1029 | .in_progress => unreachable, |
| 1027 | .outdated => unreachable, | | |
| 1028 | | 1030 | |
| 1029 | .sema_failure, | 1031 | .sema_failure, |
| 1030 | .sema_failure_retryable, | 1032 | .sema_failure_retryable, |
| ... | @@ -1033,29 +1035,73 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | ... | @@ -1033,29 +1035,73 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 1033 | .codegen_failure_retryable, | 1035 | .codegen_failure_retryable, |
| 1034 | => return error.AnalysisFail, | 1036 | => return error.AnalysisFail, |
| 1035 | | 1037 | |
| 1036 | .complete => return, | 1038 | .outdated => blk: { |
| | 1039 | //std.debug.warn("re-analyzing {}\n", .{decl.name}); |
| | 1040 | |
| | 1041 | // The exports this Decl performs will be re-discovered, so we remove them here |
| | 1042 | // prior to re-analysis. |
| | 1043 | self.deleteDeclExports(decl); |
| | 1044 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. |
| | 1045 | for (decl.dependencies.items) |dep| { |
| | 1046 | dep.removeDependant(decl); |
| | 1047 | if (dep.dependants.items.len == 0) { |
| | 1048 | // We don't perform a deletion here, because this Decl or another one |
| | 1049 | // may end up referencing it before the update is complete. |
| | 1050 | assert(!dep.deletion_flag); |
| | 1051 | dep.deletion_flag = true; |
| | 1052 | try self.deletion_set.append(self.allocator, dep); |
| | 1053 | } |
| | 1054 | } |
| | 1055 | decl.dependencies.shrink(self.allocator, 0); |
| 1037 | | 1056 | |
| 1038 | .unreferenced => { | 1057 | break :blk true; |
| 1039 | self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { | 1058 | }, |
| 1040 | error.OutOfMemory => return error.OutOfMemory, | 1059 | |
| 1041 | error.AnalysisFail => return error.AnalysisFail, | 1060 | .unreferenced => false, |
| 1042 | else => { | 1061 | }; |
| 1043 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | 1062 | |
| 1044 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 1063 | const type_changed = self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { |
| 1045 | self.allocator, | 1064 | error.OutOfMemory => return error.OutOfMemory, |
| 1046 | decl.src(), | 1065 | error.AnalysisFail => return error.AnalysisFail, |
| 1047 | "unable to analyze: {}", | 1066 | else => { |
| 1048 | .{@errorName(err)}, | 1067 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| 1049 | )); | 1068 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1050 | decl.analysis = .sema_failure_retryable; | 1069 | self.allocator, |
| 1051 | return error.AnalysisFail; | 1070 | decl.src(), |
| 1052 | }, | 1071 | "unable to analyze: {}", |
| 1053 | }; | 1072 | .{@errorName(err)}, |
| | 1073 | )); |
| | 1074 | decl.analysis = .sema_failure_retryable; |
| | 1075 | return error.AnalysisFail; |
| 1054 | }, | 1076 | }, |
| | 1077 | }; |
| | 1078 | |
| | 1079 | if (subsequent_analysis) { |
| | 1080 | // We may need to chase the dependants and re-analyze them. |
| | 1081 | // However, if the decl is a function, and the type is the same, we do not need to. |
| | 1082 | if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) { |
| | 1083 | for (decl.dependants.items) |dep| { |
| | 1084 | switch (dep.analysis) { |
| | 1085 | .unreferenced => unreachable, |
| | 1086 | .in_progress => unreachable, |
| | 1087 | .outdated => continue, // already queued for update |
| | 1088 | |
| | 1089 | .dependency_failure, |
| | 1090 | .sema_failure, |
| | 1091 | .sema_failure_retryable, |
| | 1092 | .codegen_failure, |
| | 1093 | .codegen_failure_retryable, |
| | 1094 | .complete, |
| | 1095 | => if (dep.generation != self.generation) { |
| | 1096 | try self.markOutdatedDecl(dep); |
| | 1097 | }, |
| | 1098 | } |
| | 1099 | } |
| | 1100 | } |
| 1055 | } | 1101 | } |
| 1056 | } | 1102 | } |
| 1057 | | 1103 | |
| 1058 | fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { | 1104 | fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1059 | const tracy = trace(@src()); | 1105 | const tracy = trace(@src()); |
| 1060 | defer tracy.end(); | 1106 | defer tracy.end(); |
| 1061 | | 1107 | |
| ... | @@ -1170,6 +1216,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { | ... | @@ -1170,6 +1216,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { |
| 1170 | }; | 1216 | }; |
| 1171 | fn_payload.* = .{ .func = new_func }; | 1217 | fn_payload.* = .{ .func = new_func }; |
| 1172 | | 1218 | |
| | 1219 | var prev_type_has_bits = false; |
| | 1220 | var type_changed = true; |
| | 1221 | |
| | 1222 | if (decl.typedValueManaged()) |tvm| { |
| | 1223 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); |
| | 1224 | type_changed = !tvm.typed_value.ty.eql(fn_type); |
| | 1225 | |
| | 1226 | tvm.deinit(self.allocator); |
| | 1227 | } |
| | 1228 | |
| 1173 | decl_arena_state.* = decl_arena.state; | 1229 | decl_arena_state.* = decl_arena.state; |
| 1174 | decl.typed_value = .{ | 1230 | decl.typed_value = .{ |
| 1175 | .most_recent = .{ | 1231 | .most_recent = .{ |
| ... | @@ -1183,11 +1239,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { | ... | @@ -1183,11 +1239,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { |
| 1183 | decl.analysis = .complete; | 1239 | decl.analysis = .complete; |
| 1184 | decl.generation = self.generation; | 1240 | decl.generation = self.generation; |
| 1185 | | 1241 | |
| 1186 | // We don't fully codegen the decl until later, but we do need to reserve a global | 1242 | if (fn_type.hasCodeGenBits()) { |
| 1187 | // offset table index for it. This allows us to codegen decls out of dependency order, | 1243 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 1188 | // increasing how many computations can be done in parallel. | 1244 | // offset table index for it. This allows us to codegen decls out of dependency order, |
| 1189 | try self.bin_file.allocateDeclIndexes(decl); | 1245 | // increasing how many computations can be done in parallel. |
| 1190 | try self.work_queue.writeItem(.{ .codegen_decl = decl }); | 1246 | try self.bin_file.allocateDeclIndexes(decl); |
| | 1247 | try self.work_queue.writeItem(.{ .codegen_decl = decl }); |
| | 1248 | } else if (prev_type_has_bits) { |
| | 1249 | self.bin_file.freeDecl(decl); |
| | 1250 | } |
| 1191 | | 1251 | |
| 1192 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { | 1252 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { |
| 1193 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | 1253 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| ... | @@ -1198,6 +1258,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { | ... | @@ -1198,6 +1258,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { |
| 1198 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | 1258 | try self.analyzeExport(&block_scope.base, export_src, name, decl); |
| 1199 | } | 1259 | } |
| 1200 | } | 1260 | } |
| | 1261 | return type_changed; |
| 1201 | }, | 1262 | }, |
| 1202 | .VarDecl => @panic("TODO var decl"), | 1263 | .VarDecl => @panic("TODO var decl"), |
| 1203 | .Comptime => @panic("TODO comptime decl"), | 1264 | .Comptime => @panic("TODO comptime decl"), |
| ... | @@ -1602,40 +1663,63 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1602,40 +1663,63 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1602 | } | 1663 | } |
| 1603 | | 1664 | |
| 1604 | fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { | 1665 | fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1605 | switch (root_scope.status) { | 1666 | // We may be analyzing it for the first time, or this may be |
| 1606 | .never_loaded => { | 1667 | // an incremental update. This code handles both cases. |
| 1607 | const tree = try self.getAstTree(root_scope); | 1668 | const tree = try self.getAstTree(root_scope); |
| 1608 | const decls = tree.root_node.decls(); | 1669 | const decls = tree.root_node.decls(); |
| 1609 | | 1670 | |
| 1610 | try self.work_queue.ensureUnusedCapacity(decls.len); | 1671 | try self.work_queue.ensureUnusedCapacity(decls.len); |
| 1611 | | 1672 | try root_scope.decls.ensureCapacity(self.allocator, decls.len); |
| 1612 | for (decls) |src_decl, decl_i| { | 1673 | |
| 1613 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { | 1674 | // Keep track of the decls that we expect to see in this file so that |
| 1614 | // We will create a Decl for it regardless of analysis status. | 1675 | // we know which ones have been deleted. |
| 1615 | const name_tok = fn_proto.name_token orelse | 1676 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); |
| 1616 | @panic("TODO handle missing function name in the parser"); | 1677 | defer deleted_decls.deinit(); |
| 1617 | const name_loc = tree.token_locs[name_tok]; | 1678 | try deleted_decls.ensureCapacity(root_scope.decls.items.len); |
| 1618 | const name = tree.tokenSliceLoc(name_loc); | 1679 | for (root_scope.decls.items) |file_decl| { |
| 1619 | const name_hash = root_scope.fullyQualifiedNameHash(name); | 1680 | deleted_decls.putAssumeCapacityNoClobber(file_decl, {}); |
| 1620 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | 1681 | } |
| 1621 | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); | 1682 | |
| 1622 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { | 1683 | for (decls) |src_decl, decl_i| { |
| 1623 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | 1684 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { |
| 1624 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | 1685 | // We will create a Decl for it regardless of analysis status. |
| 1625 | } | 1686 | const name_tok = fn_proto.name_token orelse |
| | 1687 | @panic("TODO handle missing function name in the parser"); |
| | 1688 | const name_loc = tree.token_locs[name_tok]; |
| | 1689 | const name = tree.tokenSliceLoc(name_loc); |
| | 1690 | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| | 1691 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| | 1692 | if (self.decl_table.get(name_hash)) |kv| { |
| | 1693 | const decl = kv.value; |
| | 1694 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| | 1695 | // have been re-ordered. |
| | 1696 | decl.src_index = decl_i; |
| | 1697 | deleted_decls.removeAssertDiscard(decl); |
| | 1698 | if (!srcHashEql(decl.contents_hash, contents_hash)) { |
| | 1699 | try self.markOutdatedDecl(decl); |
| | 1700 | decl.contents_hash = contents_hash; |
| | 1701 | } |
| | 1702 | } else { |
| | 1703 | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); |
| | 1704 | root_scope.decls.appendAssumeCapacity(new_decl); |
| | 1705 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { |
| | 1706 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| | 1707 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 1626 | } | 1708 | } |
| 1627 | } | 1709 | } |
| 1628 | // TODO also look for global variable declarations | | |
| 1629 | // TODO also look for comptime blocks and exported globals | | |
| 1630 | } | 1710 | } |
| 1631 | }, | 1711 | } |
| 1632 | | 1712 | // TODO also look for global variable declarations |
| 1633 | .unloaded_parse_failure, | 1713 | // TODO also look for comptime blocks and exported globals |
| 1634 | .unloaded_success, | 1714 | } |
| 1635 | .loaded_success, | 1715 | { |
| 1636 | => { | 1716 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 1637 | @panic("TODO process update"); | 1717 | // with when we delete decls because they are no longer referenced. |
| 1638 | }, | 1718 | var it = deleted_decls.iterator(); |
| | 1719 | while (it.next()) |kv| { |
| | 1720 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); |
| | 1721 | try self.deleteDecl(kv.key); |
| | 1722 | } |
| 1639 | } | 1723 | } |
| 1640 | } | 1724 | } |
| 1641 | | 1725 | |
| ... | @@ -1684,7 +1768,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { | ... | @@ -1684,7 +1768,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1684 | const decl = kv.value; | 1768 | const decl = kv.value; |
| 1685 | deleted_decls.removeAssertDiscard(decl); | 1769 | deleted_decls.removeAssertDiscard(decl); |
| 1686 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); | 1770 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); |
| 1687 | if (!mem.eql(u8, &src_decl.contents_hash, &decl.contents_hash)) { | 1771 | if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { |
| 1688 | try self.markOutdatedDecl(decl); | 1772 | try self.markOutdatedDecl(decl); |
| 1689 | decl.contents_hash = src_decl.contents_hash; | 1773 | decl.contents_hash = src_decl.contents_hash; |
| 1690 | } | 1774 | } |
| ... | @@ -1711,6 +1795,10 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { | ... | @@ -1711,6 +1795,10 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1711 | fn deleteDecl(self: *Module, decl: *Decl) !void { | 1795 | fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 1712 | try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len); | 1796 | try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len); |
| 1713 | | 1797 | |
| | 1798 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will |
| | 1799 | // not be present in the set, and this does nothing. |
| | 1800 | decl.scope.removeDecl(decl); |
| | 1801 | |
| 1714 | //std.debug.warn("deleting decl '{}'\n", .{decl.name}); | 1802 | //std.debug.warn("deleting decl '{}'\n", .{decl.name}); |
| 1715 | const name_hash = decl.fullyQualifiedNameHash(); | 1803 | const name_hash = decl.fullyQualifiedNameHash(); |
| 1716 | self.decl_table.removeAssertDiscard(name_hash); | 1804 | self.decl_table.removeAssertDiscard(name_hash); |
| ... | @@ -1799,110 +1887,9 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | ... | @@ -1799,110 +1887,9 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 1799 | //std.debug.warn("set {} to success\n", .{decl.name}); | 1887 | //std.debug.warn("set {} to success\n", .{decl.name}); |
| 1800 | } | 1888 | } |
| 1801 | | 1889 | |
| 1802 | fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void { | | |
| 1803 | switch (decl.analysis) { | | |
| 1804 | .unreferenced => unreachable, | | |
| 1805 | .in_progress => unreachable, | | |
| 1806 | .dependency_failure, | | |
| 1807 | .sema_failure, | | |
| 1808 | .sema_failure_retryable, | | |
| 1809 | .codegen_failure, | | |
| 1810 | .codegen_failure_retryable, | | |
| 1811 | .complete, | | |
| 1812 | => return, | | |
| 1813 | | | |
| 1814 | .outdated => {}, // Decl re-analysis | | |
| 1815 | } | | |
| 1816 | //std.debug.warn("re-analyzing {}\n", .{decl.name}); | | |
| 1817 | | | |
| 1818 | // The exports this Decl performs will be re-discovered, so we remove them here | | |
| 1819 | // prior to re-analysis. | | |
| 1820 | self.deleteDeclExports(decl); | | |
| 1821 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. | | |
| 1822 | for (decl.dependencies.items) |dep| { | | |
| 1823 | dep.removeDependant(decl); | | |
| 1824 | if (dep.dependants.items.len == 0) { | | |
| 1825 | // We don't perform a deletion here, because this Decl or another one | | |
| 1826 | // may end up referencing it before the update is complete. | | |
| 1827 | assert(!dep.deletion_flag); | | |
| 1828 | dep.deletion_flag = true; | | |
| 1829 | try self.deletion_set.append(self.allocator, dep); | | |
| 1830 | } | | |
| 1831 | } | | |
| 1832 | decl.dependencies.shrink(self.allocator, 0); | | |
| 1833 | var decl_scope: Scope.DeclAnalysis = .{ | | |
| 1834 | .decl = decl, | | |
| 1835 | .arena = std.heap.ArenaAllocator.init(self.allocator), | | |
| 1836 | }; | | |
| 1837 | errdefer decl_scope.arena.deinit(); | | |
| 1838 | | | |
| 1839 | const typed_value = self.analyzeConstInst(&decl_scope.base, old_inst) catch |err| switch (err) { | | |
| 1840 | error.OutOfMemory => return error.OutOfMemory, | | |
| 1841 | error.AnalysisFail => { | | |
| 1842 | switch (decl.analysis) { | | |
| 1843 | .in_progress => decl.analysis = .dependency_failure, | | |
| 1844 | else => {}, | | |
| 1845 | } | | |
| 1846 | decl.generation = self.generation; | | |
| 1847 | return error.AnalysisFail; | | |
| 1848 | }, | | |
| 1849 | }; | | |
| 1850 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); | | |
| 1851 | arena_state.* = decl_scope.arena.state; | | |
| 1852 | | | |
| 1853 | var prev_type_has_bits = false; | | |
| 1854 | var type_changed = true; | | |
| 1855 | | | |
| 1856 | if (decl.typedValueManaged()) |tvm| { | | |
| 1857 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | | |
| 1858 | type_changed = !tvm.typed_value.ty.eql(typed_value.ty); | | |
| 1859 | | | |
| 1860 | tvm.deinit(self.allocator); | | |
| 1861 | } | | |
| 1862 | decl.typed_value = .{ | | |
| 1863 | .most_recent = .{ | | |
| 1864 | .typed_value = typed_value, | | |
| 1865 | .arena = arena_state, | | |
| 1866 | }, | | |
| 1867 | }; | | |
| 1868 | decl.analysis = .complete; | | |
| 1869 | decl.generation = self.generation; | | |
| 1870 | if (typed_value.ty.hasCodeGenBits()) { | | |
| 1871 | // We don't fully codegen the decl until later, but we do need to reserve a global | | |
| 1872 | // offset table index for it. This allows us to codegen decls out of dependency order, | | |
| 1873 | // increasing how many computations can be done in parallel. | | |
| 1874 | try self.bin_file.allocateDeclIndexes(decl); | | |
| 1875 | try self.work_queue.writeItem(.{ .codegen_decl = decl }); | | |
| 1876 | } else if (prev_type_has_bits) { | | |
| 1877 | self.bin_file.freeDecl(decl); | | |
| 1878 | } | | |
| 1879 | | | |
| 1880 | // If the decl is a function, and the type is the same, we do not need | | |
| 1881 | // to chase the dependants. | | |
| 1882 | if (type_changed or typed_value.val.tag() != .function) { | | |
| 1883 | for (decl.dependants.items) |dep| { | | |
| 1884 | switch (dep.analysis) { | | |
| 1885 | .unreferenced => unreachable, | | |
| 1886 | .in_progress => unreachable, | | |
| 1887 | .outdated => continue, // already queued for update | | |
| 1888 | | | |
| 1889 | .dependency_failure, | | |
| 1890 | .sema_failure, | | |
| 1891 | .sema_failure_retryable, | | |
| 1892 | .codegen_failure, | | |
| 1893 | .codegen_failure_retryable, | | |
| 1894 | .complete, | | |
| 1895 | => if (dep.generation != self.generation) { | | |
| 1896 | try self.markOutdatedDecl(dep); | | |
| 1897 | }, | | |
| 1898 | } | | |
| 1899 | } | | |
| 1900 | } | | |
| 1901 | } | | |
| 1902 | | | |
| 1903 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | 1890 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { |
| 1904 | //std.debug.warn("mark {} outdated\n", .{decl.name}); | 1891 | //std.debug.warn("mark {} outdated\n", .{decl.name}); |
| 1905 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); | 1892 | try self.work_queue.writeItem(.{ .analyze_decl = decl }); |
| 1906 | if (self.failed_decls.remove(decl)) |entry| { | 1893 | if (self.failed_decls.remove(decl)) |entry| { |
| 1907 | entry.value.destroy(self.allocator); | 1894 | entry.value.destroy(self.allocator); |
| 1908 | } | 1895 | } |
| ... | @@ -2381,11 +2368,10 @@ fn createAnonymousDecl( | ... | @@ -2381,11 +2368,10 @@ fn createAnonymousDecl( |
| 2381 | decl_arena: *std.heap.ArenaAllocator, | 2368 | decl_arena: *std.heap.ArenaAllocator, |
| 2382 | typed_value: TypedValue, | 2369 | typed_value: TypedValue, |
| 2383 | ) !*Decl { | 2370 | ) !*Decl { |
| 2384 | var name_buf: [32]u8 = undefined; | | |
| 2385 | const name_index = self.getNextAnonNameIndex(); | 2371 | const name_index = self.getNextAnonNameIndex(); |
| 2386 | const name = std.fmt.bufPrint(&name_buf, "unnamed_{}", .{name_index}) catch unreachable; | | |
| 2387 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); | | |
| 2388 | const scope_decl = scope.decl().?; | 2372 | const scope_decl = scope.decl().?; |
| | 2373 | const name = try std.fmt.allocPrint(self.allocator, "{}${}", .{ scope_decl.name, name_index }); |
| | 2374 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); |
| 2389 | const src_hash: std.zig.SrcHash = undefined; | 2375 | const src_hash: std.zig.SrcHash = undefined; |
| 2390 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | 2376 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); |
| 2391 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | 2377 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| ... | @@ -3360,3 +3346,7 @@ pub const ErrorMsg = struct { | ... | @@ -3360,3 +3346,7 @@ pub const ErrorMsg = struct { |
| 3360 | self.* = undefined; | 3346 | self.* = undefined; |
| 3361 | } | 3347 | } |
| 3362 | }; | 3348 | }; |
| | 3349 | |
| | 3350 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { |
| | 3351 | return @bitCast(u128, a) == @bitCast(u128, b); |
| | 3352 | } |