| ... | ... | @@ -125,7 +125,7 @@ pub const Decl = struct { |
| 125 | 125 | /// mapping them to an address in the output file. |
| 126 | 126 | /// Memory owned by this decl, using Module's allocator. |
| 127 | 127 | name: [*:0]const u8, |
| 128 | | /// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`. |
| 128 | /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`. |
| 129 | 129 | /// Reference to externally owned memory. |
| 130 | 130 | scope: *Scope, |
| 131 | 131 | /// The AST Node decl index or ZIR Inst index that contains this declaration. |
| ... | ... | @@ -217,9 +217,10 @@ pub const Decl = struct { |
| 217 | 217 | |
| 218 | 218 | pub fn src(self: Decl) usize { |
| 219 | 219 | switch (self.scope.tag) { |
| 220 | | .file => { |
| 221 | | const file = @fieldParentPtr(Scope.File, "base", self.scope); |
| 222 | | const tree = file.contents.tree; |
| 220 | .container => { |
| 221 | const container = @fieldParentPtr(Scope.Container, "base", self.scope); |
| 222 | const tree = container.file_scope.contents.tree; |
| 223 | // TODO Container should have it's own decls() |
| 223 | 224 | const decl_node = tree.root_node.decls()[self.src_index]; |
| 224 | 225 | return tree.token_locs[decl_node.firstToken()].start; |
| 225 | 226 | }, |
| ... | ... | @@ -229,6 +230,7 @@ pub const Decl = struct { |
| 229 | 230 | const src_decl = module.decls[self.src_index]; |
| 230 | 231 | return src_decl.inst.src; |
| 231 | 232 | }, |
| 233 | .file, |
| 232 | 234 | .block => unreachable, |
| 233 | 235 | .gen_zir => unreachable, |
| 234 | 236 | .local_val => unreachable, |
| ... | ... | @@ -359,6 +361,7 @@ pub const Scope = struct { |
| 359 | 361 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, |
| 360 | 362 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, |
| 361 | 363 | .file => unreachable, |
| 364 | .container => unreachable, |
| 362 | 365 | } |
| 363 | 366 | } |
| 364 | 367 | |
| ... | ... | @@ -368,15 +371,16 @@ pub const Scope = struct { |
| 368 | 371 | return switch (self.tag) { |
| 369 | 372 | .block => self.cast(Block).?.decl, |
| 370 | 373 | .gen_zir => self.cast(GenZIR).?.decl, |
| 371 | | .local_val => return self.cast(LocalVal).?.gen_zir.decl, |
| 372 | | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl, |
| 374 | .local_val => self.cast(LocalVal).?.gen_zir.decl, |
| 375 | .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, |
| 373 | 376 | .decl => self.cast(DeclAnalysis).?.decl, |
| 374 | 377 | .zir_module => null, |
| 375 | 378 | .file => null, |
| 379 | .container => null, |
| 376 | 380 | }; |
| 377 | 381 | } |
| 378 | 382 | |
| 379 | | /// Asserts the scope has a parent which is a ZIRModule or File and |
| 383 | /// Asserts the scope has a parent which is a ZIRModule or Container and |
| 380 | 384 | /// returns it. |
| 381 | 385 | pub fn namespace(self: *Scope) *Scope { |
| 382 | 386 | switch (self.tag) { |
| ... | ... | @@ -385,7 +389,8 @@ pub const Scope = struct { |
| 385 | 389 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope, |
| 386 | 390 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope, |
| 387 | 391 | .decl => return self.cast(DeclAnalysis).?.decl.scope, |
| 388 | | .zir_module, .file => return self, |
| 392 | .file => return &self.cast(File).?.root_container.base, |
| 393 | .zir_module, .container => return self, |
| 389 | 394 | } |
| 390 | 395 | } |
| 391 | 396 | |
| ... | ... | @@ -399,8 +404,9 @@ pub const Scope = struct { |
| 399 | 404 | .local_val => unreachable, |
| 400 | 405 | .local_ptr => unreachable, |
| 401 | 406 | .decl => unreachable, |
| 407 | .file => unreachable, |
| 402 | 408 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), |
| 403 | | .file => return self.cast(File).?.fullyQualifiedNameHash(name), |
| 409 | .container => return self.cast(Container).?.fullyQualifiedNameHash(name), |
| 404 | 410 | } |
| 405 | 411 | } |
| 406 | 412 | |
| ... | ... | @@ -409,11 +415,12 @@ pub const Scope = struct { |
| 409 | 415 | switch (self.tag) { |
| 410 | 416 | .file => return self.cast(File).?.contents.tree, |
| 411 | 417 | .zir_module => unreachable, |
| 412 | | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree, |
| 413 | | .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree, |
| 414 | | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree, |
| 415 | | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(File).?.contents.tree, |
| 416 | | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(File).?.contents.tree, |
| 418 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 419 | .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 420 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 421 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 422 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 423 | .container => return self.cast(Container).?.file_scope.contents.tree, |
| 417 | 424 | } |
| 418 | 425 | } |
| 419 | 426 | |
| ... | ... | @@ -427,13 +434,15 @@ pub const Scope = struct { |
| 427 | 434 | .decl => unreachable, |
| 428 | 435 | .zir_module => unreachable, |
| 429 | 436 | .file => unreachable, |
| 437 | .container => unreachable, |
| 430 | 438 | }; |
| 431 | 439 | } |
| 432 | 440 | |
| 433 | | /// Asserts the scope has a parent which is a ZIRModule or File and |
| 441 | /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and |
| 434 | 442 | /// returns the sub_file_path field. |
| 435 | 443 | pub fn subFilePath(base: *Scope) []const u8 { |
| 436 | 444 | switch (base.tag) { |
| 445 | .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path, |
| 437 | 446 | .file => return @fieldParentPtr(File, "base", base).sub_file_path, |
| 438 | 447 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, |
| 439 | 448 | .block => unreachable, |
| ... | ... | @@ -453,11 +462,13 @@ pub const Scope = struct { |
| 453 | 462 | .local_val => unreachable, |
| 454 | 463 | .local_ptr => unreachable, |
| 455 | 464 | .decl => unreachable, |
| 465 | .container => unreachable, |
| 456 | 466 | } |
| 457 | 467 | } |
| 458 | 468 | |
| 459 | 469 | pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 { |
| 460 | 470 | switch (base.tag) { |
| 471 | .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module), |
| 461 | 472 | .file => return @fieldParentPtr(File, "base", base).getSource(module), |
| 462 | 473 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), |
| 463 | 474 | .gen_zir => unreachable, |
| ... | ... | @@ -471,8 +482,9 @@ pub const Scope = struct { |
| 471 | 482 | /// Asserts the scope is a namespace Scope and removes the Decl from the namespace. |
| 472 | 483 | pub fn removeDecl(base: *Scope, child: *Decl) void { |
| 473 | 484 | switch (base.tag) { |
| 474 | | .file => return @fieldParentPtr(File, "base", base).removeDecl(child), |
| 485 | .container => return @fieldParentPtr(Container, "base", base).removeDecl(child), |
| 475 | 486 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), |
| 487 | .file => unreachable, |
| 476 | 488 | .block => unreachable, |
| 477 | 489 | .gen_zir => unreachable, |
| 478 | 490 | .local_val => unreachable, |
| ... | ... | @@ -499,6 +511,7 @@ pub const Scope = struct { |
| 499 | 511 | .local_val => unreachable, |
| 500 | 512 | .local_ptr => unreachable, |
| 501 | 513 | .decl => unreachable, |
| 514 | .container => unreachable, |
| 502 | 515 | } |
| 503 | 516 | } |
| 504 | 517 | |
| ... | ... | @@ -515,6 +528,8 @@ pub const Scope = struct { |
| 515 | 528 | zir_module, |
| 516 | 529 | /// .zig source code. |
| 517 | 530 | file, |
| 531 | /// struct, enum or union, every .file contains one of these. |
| 532 | container, |
| 518 | 533 | block, |
| 519 | 534 | decl, |
| 520 | 535 | gen_zir, |
| ... | ... | @@ -522,6 +537,38 @@ pub const Scope = struct { |
| 522 | 537 | local_ptr, |
| 523 | 538 | }; |
| 524 | 539 | |
| 540 | pub const Container = struct { |
| 541 | pub const base_tag: Tag = .container; |
| 542 | base: Scope = Scope{ .tag = base_tag }, |
| 543 | |
| 544 | file_scope: *Scope.File, |
| 545 | |
| 546 | /// Direct children of the file. |
| 547 | decls: ArrayListUnmanaged(*Decl), |
| 548 | |
| 549 | // TODO implement container types and put this in a status union |
| 550 | // ty: Type |
| 551 | |
| 552 | pub fn deinit(self: *Container, gpa: *Allocator) void { |
| 553 | self.decls.deinit(gpa); |
| 554 | self.* = undefined; |
| 555 | } |
| 556 | |
| 557 | pub fn removeDecl(self: *Container, child: *Decl) void { |
| 558 | for (self.decls.items) |item, i| { |
| 559 | if (item == child) { |
| 560 | _ = self.decls.swapRemove(i); |
| 561 | return; |
| 562 | } |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash { |
| 567 | // TODO container scope qualified names. |
| 568 | return std.zig.hashSrc(name); |
| 569 | } |
| 570 | }; |
| 571 | |
| 525 | 572 | pub const File = struct { |
| 526 | 573 | pub const base_tag: Tag = .file; |
| 527 | 574 | base: Scope = Scope{ .tag = base_tag }, |
| ... | ... | @@ -544,8 +591,7 @@ pub const Scope = struct { |
| 544 | 591 | loaded_success, |
| 545 | 592 | }, |
| 546 | 593 | |
| 547 | | /// Direct children of the file. |
| 548 | | decls: ArrayListUnmanaged(*Decl), |
| 594 | root_container: Container, |
| 549 | 595 | |
| 550 | 596 | pub fn unload(self: *File, gpa: *Allocator) void { |
| 551 | 597 | switch (self.status) { |
| ... | ... | @@ -569,20 +615,11 @@ pub const Scope = struct { |
| 569 | 615 | } |
| 570 | 616 | |
| 571 | 617 | pub fn deinit(self: *File, gpa: *Allocator) void { |
| 572 | | self.decls.deinit(gpa); |
| 618 | self.root_container.deinit(gpa); |
| 573 | 619 | self.unload(gpa); |
| 574 | 620 | self.* = undefined; |
| 575 | 621 | } |
| 576 | 622 | |
| 577 | | pub fn removeDecl(self: *File, child: *Decl) void { |
| 578 | | for (self.decls.items) |item, i| { |
| 579 | | if (item == child) { |
| 580 | | _ = self.decls.swapRemove(i); |
| 581 | | return; |
| 582 | | } |
| 583 | | } |
| 584 | | } |
| 585 | | |
| 586 | 623 | pub fn dumpSrc(self: *File, src: usize) void { |
| 587 | 624 | const loc = std.zig.findLineColumn(self.source.bytes, src); |
| 588 | 625 | std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); |
| ... | ... | @@ -604,11 +641,6 @@ pub const Scope = struct { |
| 604 | 641 | .bytes => |bytes| return bytes, |
| 605 | 642 | } |
| 606 | 643 | } |
| 607 | | |
| 608 | | pub fn fullyQualifiedNameHash(self: *File, name: []const u8) NameHash { |
| 609 | | // We don't have struct scopes yet so this is currently just a simple name hash. |
| 610 | | return std.zig.hashSrc(name); |
| 611 | | } |
| 612 | 644 | }; |
| 613 | 645 | |
| 614 | 646 | pub const ZIRModule = struct { |
| ... | ... | @@ -861,7 +893,10 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 861 | 893 | .source = .{ .unloaded = {} }, |
| 862 | 894 | .contents = .{ .not_available = {} }, |
| 863 | 895 | .status = .never_loaded, |
| 864 | | .decls = .{}, |
| 896 | .root_container = .{ |
| 897 | .file_scope = root_scope, |
| 898 | .decls = .{}, |
| 899 | }, |
| 865 | 900 | }; |
| 866 | 901 | break :blk &root_scope.base; |
| 867 | 902 | } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) { |
| ... | ... | @@ -969,7 +1004,7 @@ pub fn update(self: *Module) !void { |
| 969 | 1004 | // to force a refresh we unload now. |
| 970 | 1005 | if (self.root_scope.cast(Scope.File)) |zig_file| { |
| 971 | 1006 | zig_file.unload(self.gpa); |
| 972 | | self.analyzeRootSrcFile(zig_file) catch |err| switch (err) { |
| 1007 | self.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { |
| 973 | 1008 | error.AnalysisFail => { |
| 974 | 1009 | assert(self.totalErrorCount() != 0); |
| 975 | 1010 | }, |
| ... | ... | @@ -1237,8 +1272,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1237 | 1272 | const tracy = trace(@src()); |
| 1238 | 1273 | defer tracy.end(); |
| 1239 | 1274 | |
| 1240 | | const file_scope = decl.scope.cast(Scope.File).?; |
| 1241 | | const tree = try self.getAstTree(file_scope); |
| 1275 | const container_scope = decl.scope.cast(Scope.Container).?; |
| 1276 | const tree = try self.getAstTree(container_scope); |
| 1242 | 1277 | const ast_node = tree.root_node.decls()[decl.src_index]; |
| 1243 | 1278 | switch (ast_node.tag) { |
| 1244 | 1279 | .FnProto => { |
| ... | ... | @@ -1698,10 +1733,12 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 1698 | 1733 | } |
| 1699 | 1734 | } |
| 1700 | 1735 | |
| 1701 | | fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1736 | fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { |
| 1702 | 1737 | const tracy = trace(@src()); |
| 1703 | 1738 | defer tracy.end(); |
| 1704 | 1739 | |
| 1740 | const root_scope = container_scope.file_scope; |
| 1741 | |
| 1705 | 1742 | switch (root_scope.status) { |
| 1706 | 1743 | .never_loaded, .unloaded_success => { |
| 1707 | 1744 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); |
| ... | ... | @@ -1743,24 +1780,24 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1743 | 1780 | } |
| 1744 | 1781 | } |
| 1745 | 1782 | |
| 1746 | | fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1783 | fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { |
| 1747 | 1784 | const tracy = trace(@src()); |
| 1748 | 1785 | defer tracy.end(); |
| 1749 | 1786 | |
| 1750 | 1787 | // We may be analyzing it for the first time, or this may be |
| 1751 | 1788 | // an incremental update. This code handles both cases. |
| 1752 | | const tree = try self.getAstTree(root_scope); |
| 1789 | const tree = try self.getAstTree(container_scope); |
| 1753 | 1790 | const decls = tree.root_node.decls(); |
| 1754 | 1791 | |
| 1755 | 1792 | try self.work_queue.ensureUnusedCapacity(decls.len); |
| 1756 | | try root_scope.decls.ensureCapacity(self.gpa, decls.len); |
| 1793 | try container_scope.decls.ensureCapacity(self.gpa, decls.len); |
| 1757 | 1794 | |
| 1758 | 1795 | // Keep track of the decls that we expect to see in this file so that |
| 1759 | 1796 | // we know which ones have been deleted. |
| 1760 | 1797 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); |
| 1761 | 1798 | defer deleted_decls.deinit(); |
| 1762 | | try deleted_decls.ensureCapacity(root_scope.decls.items.len); |
| 1763 | | for (root_scope.decls.items) |file_decl| { |
| 1799 | try deleted_decls.ensureCapacity(container_scope.decls.items.len); |
| 1800 | for (container_scope.decls.items) |file_decl| { |
| 1764 | 1801 | deleted_decls.putAssumeCapacityNoClobber(file_decl, {}); |
| 1765 | 1802 | } |
| 1766 | 1803 | |
| ... | ... | @@ -1773,7 +1810,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1773 | 1810 | |
| 1774 | 1811 | const name_loc = tree.token_locs[name_tok]; |
| 1775 | 1812 | const name = tree.tokenSliceLoc(name_loc); |
| 1776 | | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| 1813 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 1777 | 1814 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| 1778 | 1815 | if (self.decl_table.get(name_hash)) |decl| { |
| 1779 | 1816 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| ... | ... | @@ -1801,8 +1838,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1801 | 1838 | } |
| 1802 | 1839 | } |
| 1803 | 1840 | } else { |
| 1804 | | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); |
| 1805 | | root_scope.decls.appendAssumeCapacity(new_decl); |
| 1841 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 1842 | container_scope.decls.appendAssumeCapacity(new_decl); |
| 1806 | 1843 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { |
| 1807 | 1844 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| 1808 | 1845 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| ... | ... | @@ -1812,7 +1849,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1812 | 1849 | } else if (src_decl.castTag(.VarDecl)) |var_decl| { |
| 1813 | 1850 | const name_loc = tree.token_locs[var_decl.name_token]; |
| 1814 | 1851 | const name = tree.tokenSliceLoc(name_loc); |
| 1815 | | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| 1852 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 1816 | 1853 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| 1817 | 1854 | if (self.decl_table.get(name_hash)) |decl| { |
| 1818 | 1855 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| ... | ... | @@ -1828,8 +1865,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1828 | 1865 | decl.contents_hash = contents_hash; |
| 1829 | 1866 | } |
| 1830 | 1867 | } else { |
| 1831 | | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); |
| 1832 | | root_scope.decls.appendAssumeCapacity(new_decl); |
| 1868 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 1869 | container_scope.decls.appendAssumeCapacity(new_decl); |
| 1833 | 1870 | if (var_decl.getExternExportToken()) |maybe_export_token| { |
| 1834 | 1871 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| 1835 | 1872 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| ... | ... | @@ -1841,11 +1878,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1841 | 1878 | const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index}); |
| 1842 | 1879 | defer self.gpa.free(name); |
| 1843 | 1880 | |
| 1844 | | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| 1881 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 1845 | 1882 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| 1846 | 1883 | |
| 1847 | | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); |
| 1848 | | root_scope.decls.appendAssumeCapacity(new_decl); |
| 1884 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 1885 | container_scope.decls.appendAssumeCapacity(new_decl); |
| 1849 | 1886 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 1850 | 1887 | } else if (src_decl.castTag(.ContainerField)) |container_field| { |
| 1851 | 1888 | log.err("TODO: analyze container field", .{}); |
| ... | ... | @@ -3124,6 +3161,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err |
| 3124 | 3161 | self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); |
| 3125 | 3162 | }, |
| 3126 | 3163 | .file => unreachable, |
| 3164 | .container => unreachable, |
| 3127 | 3165 | } |
| 3128 | 3166 | return error.AnalysisFail; |
| 3129 | 3167 | } |