| ... | ... | @@ -74,9 +74,9 @@ const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope |
| 74 | 74 | const WorkItem = union(enum) { |
| 75 | 75 | /// Write the machine code for a Decl to the output file. |
| 76 | 76 | codegen_decl: *Decl, |
| 77 | | /// Decl has been determined to be outdated; perform semantic analysis again. |
| 78 | | re_analyze_decl: *Decl, |
| 79 | 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 | 80 | analyze_decl: *Decl, |
| 81 | 81 | }; |
| 82 | 82 | |
| ... | ... | @@ -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 | 417 | /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it. |
| 407 | 418 | pub fn destroy(base: *Scope, allocator: *Allocator) void { |
| 408 | 419 | switch (base.tag) { |
| ... | ... | @@ -462,6 +473,9 @@ pub const Scope = struct { |
| 462 | 473 | loaded_success, |
| 463 | 474 | }, |
| 464 | 475 | |
| 476 | /// Direct children of the file. |
| 477 | decls: ArrayListUnmanaged(*Decl), |
| 478 | |
| 465 | 479 | pub fn unload(self: *File, allocator: *Allocator) void { |
| 466 | 480 | switch (self.status) { |
| 467 | 481 | .never_loaded, |
| ... | ... | @@ -484,10 +498,20 @@ pub const Scope = struct { |
| 484 | 498 | } |
| 485 | 499 | |
| 486 | 500 | pub fn deinit(self: *File, allocator: *Allocator) void { |
| 501 | self.decls.deinit(allocator); |
| 487 | 502 | self.unload(allocator); |
| 488 | 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 | 515 | pub fn dumpSrc(self: *File, src: usize) void { |
| 492 | 516 | const loc = std.zig.findLineColumn(self.source.bytes, src); |
| 493 | 517 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); |
| ... | ... | @@ -540,6 +564,11 @@ pub const Scope = struct { |
| 540 | 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 | 572 | pub fn unload(self: *ZIRModule, allocator: *Allocator) void { |
| 544 | 573 | switch (self.status) { |
| 545 | 574 | .never_loaded, |
| ... | ... | @@ -569,10 +598,20 @@ pub const Scope = struct { |
| 569 | 598 | } |
| 570 | 599 | |
| 571 | 600 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { |
| 601 | self.decls.deinit(allocator); |
| 572 | 602 | self.unload(allocator); |
| 573 | 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 | 615 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { |
| 577 | 616 | const loc = std.zig.findLineColumn(self.source.bytes, src); |
| 578 | 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 | 739 | .source = .{ .unloaded = {} }, |
| 701 | 740 | .contents = .{ .not_available = {} }, |
| 702 | 741 | .status = .never_loaded, |
| 742 | .decls = .{}, |
| 703 | 743 | }; |
| 704 | 744 | break :blk &root_scope.base; |
| 705 | 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 | 749 | .source = .{ .unloaded = {} }, |
| 710 | 750 | .contents = .{ .not_available = {} }, |
| 711 | 751 | .status = .never_loaded, |
| 752 | .decls = .{}, |
| 712 | 753 | }; |
| 713 | 754 | break :blk &root_scope.base; |
| 714 | 755 | } else { |
| ... | ... | @@ -828,13 +869,14 @@ pub fn update(self: *Module) !void { |
| 828 | 869 | try self.performAllTheWork(); |
| 829 | 870 | |
| 830 | 871 | // Process the deletion set. |
| 831 | | while (self.deletion_set.popOrNull()) |decl| { |
| 872 | for (self.deletion_set.items) |decl| { |
| 832 | 873 | if (decl.dependants.items.len != 0) { |
| 833 | 874 | decl.deletion_flag = false; |
| 834 | 875 | continue; |
| 835 | 876 | } |
| 836 | 877 | try self.deleteDecl(decl); |
| 837 | 878 | } |
| 879 | self.deletion_set.shrink(self.allocator, 0); |
| 838 | 880 | |
| 839 | 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 | 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 | 1014 | .analyze_decl => |decl| { |
| 1016 | 1015 | self.ensureDeclAnalyzed(decl) catch |err| switch (err) { |
| 1017 | 1016 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -1022,9 +1021,12 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1022 | 1021 | } |
| 1023 | 1022 | |
| 1024 | 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 | 1029 | .in_progress => unreachable, |
| 1027 | | .outdated => unreachable, |
| 1028 | 1030 | |
| 1029 | 1031 | .sema_failure, |
| 1030 | 1032 | .sema_failure_retryable, |
| ... | ... | @@ -1033,29 +1035,73 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 1033 | 1035 | .codegen_failure_retryable, |
| 1034 | 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 => { |
| 1039 | | self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { |
| 1040 | | error.OutOfMemory => return error.OutOfMemory, |
| 1041 | | error.AnalysisFail => return error.AnalysisFail, |
| 1042 | | else => { |
| 1043 | | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| 1044 | | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1045 | | self.allocator, |
| 1046 | | decl.src(), |
| 1047 | | "unable to analyze: {}", |
| 1048 | | .{@errorName(err)}, |
| 1049 | | )); |
| 1050 | | decl.analysis = .sema_failure_retryable; |
| 1051 | | return error.AnalysisFail; |
| 1052 | | }, |
| 1053 | | }; |
| 1057 | break :blk true; |
| 1058 | }, |
| 1059 | |
| 1060 | .unreferenced => false, |
| 1061 | }; |
| 1062 | |
| 1063 | const type_changed = self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { |
| 1064 | error.OutOfMemory => return error.OutOfMemory, |
| 1065 | error.AnalysisFail => return error.AnalysisFail, |
| 1066 | else => { |
| 1067 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| 1068 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1069 | self.allocator, |
| 1070 | decl.src(), |
| 1071 | "unable to analyze: {}", |
| 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 | 1105 | const tracy = trace(@src()); |
| 1060 | 1106 | defer tracy.end(); |
| 1061 | 1107 | |
| ... | ... | @@ -1170,6 +1216,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { |
| 1170 | 1216 | }; |
| 1171 | 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 | 1229 | decl_arena_state.* = decl_arena.state; |
| 1174 | 1230 | decl.typed_value = .{ |
| 1175 | 1231 | .most_recent = .{ |
| ... | ... | @@ -1183,11 +1239,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { |
| 1183 | 1239 | decl.analysis = .complete; |
| 1184 | 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 |
| 1187 | | // offset table index for it. This allows us to codegen decls out of dependency order, |
| 1188 | | // increasing how many computations can be done in parallel. |
| 1189 | | try self.bin_file.allocateDeclIndexes(decl); |
| 1190 | | try self.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 1242 | if (fn_type.hasCodeGenBits()) { |
| 1243 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 1244 | // offset table index for it. This allows us to codegen decls out of dependency order, |
| 1245 | // increasing how many computations can be done in parallel. |
| 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 | 1252 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { |
| 1193 | 1253 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| ... | ... | @@ -1198,6 +1258,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { |
| 1198 | 1258 | try self.analyzeExport(&block_scope.base, export_src, name, decl); |
| 1199 | 1259 | } |
| 1200 | 1260 | } |
| 1261 | return type_changed; |
| 1201 | 1262 | }, |
| 1202 | 1263 | .VarDecl => @panic("TODO var decl"), |
| 1203 | 1264 | .Comptime => @panic("TODO comptime decl"), |
| ... | ... | @@ -1602,40 +1663,63 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1602 | 1663 | } |
| 1603 | 1664 | |
| 1604 | 1665 | fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1605 | | switch (root_scope.status) { |
| 1606 | | .never_loaded => { |
| 1607 | | const tree = try self.getAstTree(root_scope); |
| 1608 | | const decls = tree.root_node.decls(); |
| 1609 | | |
| 1610 | | try self.work_queue.ensureUnusedCapacity(decls.len); |
| 1611 | | |
| 1612 | | for (decls) |src_decl, decl_i| { |
| 1613 | | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { |
| 1614 | | // We will create a Decl for it regardless of analysis status. |
| 1615 | | const name_tok = fn_proto.name_token orelse |
| 1616 | | @panic("TODO handle missing function name in the parser"); |
| 1617 | | const name_loc = tree.token_locs[name_tok]; |
| 1618 | | const name = tree.tokenSliceLoc(name_loc); |
| 1619 | | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| 1620 | | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| 1621 | | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); |
| 1622 | | if (fn_proto.extern_export_inline_token) |maybe_export_token| { |
| 1623 | | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| 1624 | | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 1625 | | } |
| 1666 | // We may be analyzing it for the first time, or this may be |
| 1667 | // an incremental update. This code handles both cases. |
| 1668 | const tree = try self.getAstTree(root_scope); |
| 1669 | const decls = tree.root_node.decls(); |
| 1670 | |
| 1671 | try self.work_queue.ensureUnusedCapacity(decls.len); |
| 1672 | try root_scope.decls.ensureCapacity(self.allocator, decls.len); |
| 1673 | |
| 1674 | // Keep track of the decls that we expect to see in this file so that |
| 1675 | // we know which ones have been deleted. |
| 1676 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); |
| 1677 | defer deleted_decls.deinit(); |
| 1678 | try deleted_decls.ensureCapacity(root_scope.decls.items.len); |
| 1679 | for (root_scope.decls.items) |file_decl| { |
| 1680 | deleted_decls.putAssumeCapacityNoClobber(file_decl, {}); |
| 1681 | } |
| 1682 | |
| 1683 | for (decls) |src_decl, decl_i| { |
| 1684 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { |
| 1685 | // We will create a Decl for it regardless of analysis status. |
| 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 | | }, |
| 1632 | | |
| 1633 | | .unloaded_parse_failure, |
| 1634 | | .unloaded_success, |
| 1635 | | .loaded_success, |
| 1636 | | => { |
| 1637 | | @panic("TODO process update"); |
| 1638 | | }, |
| 1711 | } |
| 1712 | // TODO also look for global variable declarations |
| 1713 | // TODO also look for comptime blocks and exported globals |
| 1714 | } |
| 1715 | { |
| 1716 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 1717 | // with when we delete decls because they are no longer referenced. |
| 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 | 1768 | const decl = kv.value; |
| 1685 | 1769 | deleted_decls.removeAssertDiscard(decl); |
| 1686 | 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 | 1772 | try self.markOutdatedDecl(decl); |
| 1689 | 1773 | decl.contents_hash = src_decl.contents_hash; |
| 1690 | 1774 | } |
| ... | ... | @@ -1711,6 +1795,10 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1711 | 1795 | fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 1712 | 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 | 1802 | //std.debug.warn("deleting decl '{}'\n", .{decl.name}); |
| 1715 | 1803 | const name_hash = decl.fullyQualifiedNameHash(); |
| 1716 | 1804 | self.decl_table.removeAssertDiscard(name_hash); |
| ... | ... | @@ -1799,110 +1887,9 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 1799 | 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 | 1890 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { |
| 1904 | 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 | 1893 | if (self.failed_decls.remove(decl)) |entry| { |
| 1907 | 1894 | entry.value.destroy(self.allocator); |
| 1908 | 1895 | } |
| ... | ... | @@ -2381,11 +2368,10 @@ fn createAnonymousDecl( |
| 2381 | 2368 | decl_arena: *std.heap.ArenaAllocator, |
| 2382 | 2369 | typed_value: TypedValue, |
| 2383 | 2370 | ) !*Decl { |
| 2384 | | var name_buf: [32]u8 = undefined; |
| 2385 | 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 | 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 | 2375 | const src_hash: std.zig.SrcHash = undefined; |
| 2390 | 2376 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); |
| 2391 | 2377 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| ... | ... | @@ -3360,3 +3346,7 @@ pub const ErrorMsg = struct { |
| 3360 | 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 | } |