Commit 6f0126e957

Vexu <git@vexu.eu>
2020-09-03 13:58:47
stage2: split Scope.Container from Scope.File
1 parent 1174cb1
Changed files (3)
src-self-hosted
src-self-hosted/link/Elf.zig
@@ -1656,8 +1656,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
         try dbg_line_buffer.ensureCapacity(26);
 
         const line_off: u28 = blk: {
-            if (decl.scope.cast(Module.Scope.File)) |scope_file| {
-                const tree = scope_file.contents.tree;
+            if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
+                const tree = container_scope.file_scope.contents.tree;
                 const file_ast_decls = tree.root_node.decls();
                 // TODO Look into improving the performance here by adding a token-index-to-line
                 // lookup table. Currently this involves scanning over the source code for newlines.
@@ -2157,8 +2157,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
     const tracy = trace(@src());
     defer tracy.end();
 
-    const scope_file = decl.scope.cast(Module.Scope.File).?;
-    const tree = scope_file.contents.tree;
+    const container_scope = decl.scope.cast(Module.Scope.Container).?;
+    const tree = container_scope.file_scope.contents.tree;
     const file_ast_decls = tree.root_node.decls();
     // TODO Look into improving the performance here by adding a token-index-to-line
     // lookup table. Currently this involves scanning over the source code for newlines.
src-self-hosted/codegen.zig
@@ -436,8 +436,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             try branch_stack.append(.{});
 
             const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
-                if (module_fn.owner_decl.scope.cast(Module.Scope.File)) |scope_file| {
-                    const tree = scope_file.contents.tree;
+                if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| {
+                    const tree = container_scope.file_scope.contents.tree;
                     const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
                     const block = fn_proto.getBodyNode().?.castTag(.Block).?;
                     const lbrace_src = tree.token_locs[block.lbrace].start;
src-self-hosted/Module.zig
@@ -125,7 +125,7 @@ pub const Decl = struct {
     /// mapping them to an address in the output file.
     /// Memory owned by this decl, using Module's allocator.
     name: [*:0]const u8,
-    /// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`.
+    /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
     /// Reference to externally owned memory.
     scope: *Scope,
     /// The AST Node decl index or ZIR Inst index that contains this declaration.
@@ -217,9 +217,10 @@ pub const Decl = struct {
 
     pub fn src(self: Decl) usize {
         switch (self.scope.tag) {
-            .file => {
-                const file = @fieldParentPtr(Scope.File, "base", self.scope);
-                const tree = file.contents.tree;
+            .container => {
+                const container = @fieldParentPtr(Scope.Container, "base", self.scope);
+                const tree = container.file_scope.contents.tree;
+                // TODO Container should have it's own decls()
                 const decl_node = tree.root_node.decls()[self.src_index];
                 return tree.token_locs[decl_node.firstToken()].start;
             },
@@ -229,6 +230,7 @@ pub const Decl = struct {
                 const src_decl = module.decls[self.src_index];
                 return src_decl.inst.src;
             },
+            .file,
             .block => unreachable,
             .gen_zir => unreachable,
             .local_val => unreachable,
@@ -359,6 +361,7 @@ pub const Scope = struct {
             .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
             .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
             .file => unreachable,
+            .container => unreachable,
         }
     }
 
@@ -368,15 +371,16 @@ pub const Scope = struct {
         return switch (self.tag) {
             .block => self.cast(Block).?.decl,
             .gen_zir => self.cast(GenZIR).?.decl,
-            .local_val => return self.cast(LocalVal).?.gen_zir.decl,
-            .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl,
+            .local_val => self.cast(LocalVal).?.gen_zir.decl,
+            .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
             .decl => self.cast(DeclAnalysis).?.decl,
             .zir_module => null,
             .file => null,
+            .container => null,
         };
     }
 
-    /// Asserts the scope has a parent which is a ZIRModule or File and
+    /// Asserts the scope has a parent which is a ZIRModule or Container and
     /// returns it.
     pub fn namespace(self: *Scope) *Scope {
         switch (self.tag) {
@@ -385,7 +389,8 @@ pub const Scope = struct {
             .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
             .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
             .decl => return self.cast(DeclAnalysis).?.decl.scope,
-            .zir_module, .file => return self,
+            .file => return &self.cast(File).?.root_container.base,
+            .zir_module, .container => return self,
         }
     }
 
@@ -399,8 +404,9 @@ pub const Scope = struct {
             .local_val => unreachable,
             .local_ptr => unreachable,
             .decl => unreachable,
+            .file => unreachable,
             .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
-            .file => return self.cast(File).?.fullyQualifiedNameHash(name),
+            .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
         }
     }
 
@@ -409,11 +415,12 @@ pub const Scope = struct {
         switch (self.tag) {
             .file => return self.cast(File).?.contents.tree,
             .zir_module => unreachable,
-            .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree,
-            .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree,
-            .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree,
-            .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(File).?.contents.tree,
-            .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(File).?.contents.tree,
+            .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+            .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+            .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+            .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
+            .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
+            .container => return self.cast(Container).?.file_scope.contents.tree,
         }
     }
 
@@ -427,13 +434,15 @@ pub const Scope = struct {
             .decl => unreachable,
             .zir_module => unreachable,
             .file => unreachable,
+            .container => unreachable,
         };
     }
 
-    /// Asserts the scope has a parent which is a ZIRModule or File and
+    /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
     /// returns the sub_file_path field.
     pub fn subFilePath(base: *Scope) []const u8 {
         switch (base.tag) {
+            .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
             .file => return @fieldParentPtr(File, "base", base).sub_file_path,
             .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
             .block => unreachable,
@@ -453,11 +462,13 @@ pub const Scope = struct {
             .local_val => unreachable,
             .local_ptr => unreachable,
             .decl => unreachable,
+            .container => unreachable,
         }
     }
 
     pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
         switch (base.tag) {
+            .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
             .file => return @fieldParentPtr(File, "base", base).getSource(module),
             .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
             .gen_zir => unreachable,
@@ -471,8 +482,9 @@ pub const Scope = struct {
     /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
     pub fn removeDecl(base: *Scope, child: *Decl) void {
         switch (base.tag) {
-            .file => return @fieldParentPtr(File, "base", base).removeDecl(child),
+            .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
             .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
+            .file => unreachable,
             .block => unreachable,
             .gen_zir => unreachable,
             .local_val => unreachable,
@@ -499,6 +511,7 @@ pub const Scope = struct {
             .local_val => unreachable,
             .local_ptr => unreachable,
             .decl => unreachable,
+            .container => unreachable,
         }
     }
 
@@ -515,6 +528,8 @@ pub const Scope = struct {
         zir_module,
         /// .zig source code.
         file,
+        /// struct, enum or union, every .file contains one of these.
+        container,
         block,
         decl,
         gen_zir,
@@ -522,6 +537,38 @@ pub const Scope = struct {
         local_ptr,
     };
 
+    pub const Container = struct {
+        pub const base_tag: Tag = .container;
+        base: Scope = Scope{ .tag = base_tag },
+
+        file_scope: *Scope.File,
+
+        /// Direct children of the file.
+        decls: ArrayListUnmanaged(*Decl),
+
+        // TODO implement container types and put this in a status union
+        // ty: Type
+
+        pub fn deinit(self: *Container, gpa: *Allocator) void {
+            self.decls.deinit(gpa);
+            self.* = undefined;
+        }
+
+        pub fn removeDecl(self: *Container, child: *Decl) void {
+            for (self.decls.items) |item, i| {
+                if (item == child) {
+                    _ = self.decls.swapRemove(i);
+                    return;
+                }
+            }
+        }
+
+        pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
+            // TODO container scope qualified names.
+            return std.zig.hashSrc(name);
+        }
+    };
+
     pub const File = struct {
         pub const base_tag: Tag = .file;
         base: Scope = Scope{ .tag = base_tag },
@@ -544,8 +591,7 @@ pub const Scope = struct {
             loaded_success,
         },
 
-        /// Direct children of the file.
-        decls: ArrayListUnmanaged(*Decl),
+        root_container: Container,
 
         pub fn unload(self: *File, gpa: *Allocator) void {
             switch (self.status) {
@@ -569,20 +615,11 @@ pub const Scope = struct {
         }
 
         pub fn deinit(self: *File, gpa: *Allocator) void {
-            self.decls.deinit(gpa);
+            self.root_container.deinit(gpa);
             self.unload(gpa);
             self.* = undefined;
         }
 
-        pub fn removeDecl(self: *File, child: *Decl) void {
-            for (self.decls.items) |item, i| {
-                if (item == child) {
-                    _ = self.decls.swapRemove(i);
-                    return;
-                }
-            }
-        }
-
         pub fn dumpSrc(self: *File, src: usize) void {
             const loc = std.zig.findLineColumn(self.source.bytes, src);
             std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
@@ -604,11 +641,6 @@ pub const Scope = struct {
                 .bytes => |bytes| return bytes,
             }
         }
-
-        pub fn fullyQualifiedNameHash(self: *File, name: []const u8) NameHash {
-            // We don't have struct scopes yet so this is currently just a simple name hash.
-            return std.zig.hashSrc(name);
-        }
     };
 
     pub const ZIRModule = struct {
@@ -861,7 +893,10 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
                 .source = .{ .unloaded = {} },
                 .contents = .{ .not_available = {} },
                 .status = .never_loaded,
-                .decls = .{},
+                .root_container = .{
+                    .file_scope = root_scope,
+                    .decls = .{},
+                },
             };
             break :blk &root_scope.base;
         } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
@@ -969,7 +1004,7 @@ pub fn update(self: *Module) !void {
     // to force a refresh we unload now.
     if (self.root_scope.cast(Scope.File)) |zig_file| {
         zig_file.unload(self.gpa);
-        self.analyzeRootSrcFile(zig_file) catch |err| switch (err) {
+        self.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
             error.AnalysisFail => {
                 assert(self.totalErrorCount() != 0);
             },
@@ -1237,8 +1272,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const file_scope = decl.scope.cast(Scope.File).?;
-    const tree = try self.getAstTree(file_scope);
+    const container_scope = decl.scope.cast(Scope.Container).?;
+    const tree = try self.getAstTree(container_scope);
     const ast_node = tree.root_node.decls()[decl.src_index];
     switch (ast_node.tag) {
         .FnProto => {
@@ -1698,10 +1733,12 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
     }
 }
 
-fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
+fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const root_scope = container_scope.file_scope;
+
     switch (root_scope.status) {
         .never_loaded, .unloaded_success => {
             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 {
     }
 }
 
-fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
+fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
     // We may be analyzing it for the first time, or this may be
     // an incremental update. This code handles both cases.
-    const tree = try self.getAstTree(root_scope);
+    const tree = try self.getAstTree(container_scope);
     const decls = tree.root_node.decls();
 
     try self.work_queue.ensureUnusedCapacity(decls.len);
-    try root_scope.decls.ensureCapacity(self.gpa, decls.len);
+    try container_scope.decls.ensureCapacity(self.gpa, decls.len);
 
     // Keep track of the decls that we expect to see in this file so that
     // we know which ones have been deleted.
     var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
     defer deleted_decls.deinit();
-    try deleted_decls.ensureCapacity(root_scope.decls.items.len);
-    for (root_scope.decls.items) |file_decl| {
+    try deleted_decls.ensureCapacity(container_scope.decls.items.len);
+    for (container_scope.decls.items) |file_decl| {
         deleted_decls.putAssumeCapacityNoClobber(file_decl, {});
     }
 
@@ -1773,7 +1810,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
 
             const name_loc = tree.token_locs[name_tok];
             const name = tree.tokenSliceLoc(name_loc);
-            const name_hash = root_scope.fullyQualifiedNameHash(name);
+            const name_hash = container_scope.fullyQualifiedNameHash(name);
             const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
             if (self.decl_table.get(name_hash)) |decl| {
                 // 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 {
                     }
                 }
             } else {
-                const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
-                root_scope.decls.appendAssumeCapacity(new_decl);
+                const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+                container_scope.decls.appendAssumeCapacity(new_decl);
                 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
                     if (tree.token_ids[maybe_export_token] == .Keyword_export) {
                         self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
@@ -1812,7 +1849,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
         } else if (src_decl.castTag(.VarDecl)) |var_decl| {
             const name_loc = tree.token_locs[var_decl.name_token];
             const name = tree.tokenSliceLoc(name_loc);
-            const name_hash = root_scope.fullyQualifiedNameHash(name);
+            const name_hash = container_scope.fullyQualifiedNameHash(name);
             const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
             if (self.decl_table.get(name_hash)) |decl| {
                 // 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 {
                     decl.contents_hash = contents_hash;
                 }
             } else {
-                const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
-                root_scope.decls.appendAssumeCapacity(new_decl);
+                const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+                container_scope.decls.appendAssumeCapacity(new_decl);
                 if (var_decl.getExternExportToken()) |maybe_export_token| {
                     if (tree.token_ids[maybe_export_token] == .Keyword_export) {
                         self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
@@ -1841,11 +1878,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
             const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
             defer self.gpa.free(name);
 
-            const name_hash = root_scope.fullyQualifiedNameHash(name);
+            const name_hash = container_scope.fullyQualifiedNameHash(name);
             const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
 
-            const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
-            root_scope.decls.appendAssumeCapacity(new_decl);
+            const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+            container_scope.decls.appendAssumeCapacity(new_decl);
             self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
         } else if (src_decl.castTag(.ContainerField)) |container_field| {
             log.err("TODO: analyze container field", .{});
@@ -3124,6 +3161,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
             self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
         },
         .file => unreachable,
+        .container => unreachable,
     }
     return error.AnalysisFail;
 }