diff options
Diffstat (limited to 'src/Module.zig')
| -rw-r--r-- | src/Module.zig | 678 |
1 files changed, 409 insertions, 269 deletions
diff --git a/src/Module.zig b/src/Module.zig index 96b490e2a1..ab0cf3c10f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -26,6 +26,7 @@ const trace = @import("tracy.zig").trace; const AstGen = @import("AstGen.zig"); const Sema = @import("Sema.zig"); const target_util = @import("target.zig"); +const Cache = @import("Cache.zig"); /// General-purpose allocator. Used for both temporary and long-term storage. gpa: *Allocator, @@ -35,8 +36,6 @@ comp: *Compilation, zig_cache_artifact_directory: Compilation.Directory, /// Pointer to externally managed resource. `null` if there is no zig file being compiled. root_pkg: *Package, -/// Module owns this resource. -root_scope: *Scope.File, /// It's rare for a decl to be exported, so we save memory by having a sparse map of /// Decl pointers to details about them being exported. /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. @@ -52,6 +51,12 @@ symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{}, export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, /// Maps fully qualified namespaced names to the Decl struct for them. decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, +/// The set of all the files in the Module. We keep track of this in order to iterate +/// over it and check which source files have been modified on the file system when +/// an update is requested, as well as to cache `@import` results. +/// Keys are fully resolved file paths. This table owns the keys and values. +import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, + /// We optimize memory usage for a compilation with no compile errors by storing the /// error messages and mapping outside of `Decl`. /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. @@ -85,9 +90,6 @@ global_error_set: std.StringHashMapUnmanaged(ErrorInt) = .{}, /// Corresponds with `global_error_set`. error_name_list: ArrayListUnmanaged([]const u8) = .{}, -/// Keys are fully qualified paths -import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, - /// Incrementing integer used to compare against the corresponding Decl /// field to determine whether a Decl's status applies to an ongoing update, or a /// previous analysis. @@ -147,15 +149,16 @@ pub const Decl = struct { /// This is necessary for 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. + /// The direct parent namespace of the Decl. /// Reference to externally owned memory. - container: *Scope.Container, + /// This is `null` for the Decl that represents a `File`. + namespace: *Scope.Namespace, /// An integer that can be checked against the corresponding incrementing /// generation field of Module. This is used to determine whether `complete` status /// represents pre- or post- re-analysis. generation: u32, - /// The AST Node index or ZIR Inst index that contains this declaration. + /// The AST node index of this declaration. /// Must be recomputed when the corresponding source file is modified. src_node: ast.Node.Index, @@ -273,22 +276,22 @@ pub const Decl = struct { } pub fn srcToken(decl: Decl) u32 { - const tree = &decl.container.file_scope.tree; + const tree = &decl.namespace.file_scope.tree; return tree.firstToken(decl.src_node); } pub fn srcByteOffset(decl: Decl) u32 { - const tree = &decl.container.file_scope.tree; + const tree = &decl.namespace.file_scope.tree; return tree.tokens.items(.start)[decl.srcToken()]; } pub fn fullyQualifiedNameHash(decl: Decl) Scope.NameHash { - return decl.container.fullyQualifiedNameHash(mem.spanZ(decl.name)); + return decl.namespace.fullyQualifiedNameHash(mem.spanZ(decl.name)); } pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void { const unqualified_name = mem.spanZ(decl.name); - return decl.container.renderFullyQualifiedName(unqualified_name, writer); + return decl.namespace.renderFullyQualifiedName(unqualified_name, writer); } pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 { @@ -330,7 +333,7 @@ pub const Decl = struct { } pub fn getFileScope(decl: Decl) *Scope.File { - return decl.container.file_scope; + return decl.namespace.file_scope; } pub fn getEmitH(decl: *Decl, module: *Module) *EmitH { @@ -377,7 +380,7 @@ pub const Struct = struct { /// Set of field names in declaration order. fields: std.StringArrayHashMapUnmanaged(Field), /// Represents the declarations inside this struct. - container: Scope.Container, + namespace: Scope.Namespace, /// Offset from `owner_decl`, points to the struct AST node. node_offset: i32, @@ -434,7 +437,7 @@ pub const EnumFull = struct { /// If this hash map is empty, it means the enum tags are auto-numbered. values: ValueMap, /// Represents the declarations inside this struct. - container: Scope.Container, + namespace: Scope.Namespace, /// Offset from `owner_decl`, points to the enum decl AST node. node_offset: i32, @@ -521,7 +524,7 @@ pub const Scope = struct { .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena, .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena, .file => unreachable, - .container => unreachable, + .namespace => unreachable, .decl_ref => unreachable, } } @@ -533,7 +536,7 @@ pub const Scope = struct { .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, .file => null, - .container => null, + .namespace => null, .decl_ref => scope.cast(DeclRef).?.decl, }; } @@ -545,36 +548,21 @@ pub const Scope = struct { .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, .file => null, - .container => null, + .namespace => null, .decl_ref => scope.cast(DeclRef).?.decl, }; } - /// Asserts the scope has a parent which is a Container and returns it. - pub fn namespace(scope: *Scope) *Container { + /// Asserts the scope has a parent which is a Namespace and returns it. + pub fn namespace(scope: *Scope) *Namespace { switch (scope.tag) { - .block => return scope.cast(Block).?.sema.owner_decl.container, - .gen_zir => return scope.cast(GenZir).?.astgen.decl.container, - .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.container, - .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.container, - .file => return &scope.cast(File).?.root_container, - .container => return scope.cast(Container).?, - .decl_ref => return scope.cast(DeclRef).?.decl.container, - } - } - - /// Must generate unique bytes with no collisions with other decls. - /// The point of hashing here is only to limit the number of bytes of - /// the unique identifier to a fixed size (16 bytes). - pub fn fullyQualifiedNameHash(scope: *Scope, name: []const u8) NameHash { - switch (scope.tag) { - .block => unreachable, - .gen_zir => unreachable, - .local_val => unreachable, - .local_ptr => unreachable, - .file => unreachable, - .container => return scope.cast(Container).?.fullyQualifiedNameHash(name), - .decl_ref => unreachable, + .block => return scope.cast(Block).?.sema.owner_decl.namespace, + .gen_zir => return scope.cast(GenZir).?.astgen.decl.namespace, + .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.namespace, + .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.namespace, + .file => return scope.cast(File).?.namespace, + .namespace => return scope.cast(Namespace).?, + .decl_ref => return scope.cast(DeclRef).?.decl.namespace, } } @@ -582,12 +570,12 @@ pub const Scope = struct { pub fn tree(scope: *Scope) *const ast.Tree { switch (scope.tag) { .file => return &scope.cast(File).?.tree, - .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree, + .block => return &scope.cast(Block).?.src_decl.namespace.file_scope.tree, .gen_zir => return scope.cast(GenZir).?.tree(), - .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.container.file_scope.tree, - .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.container.file_scope.tree, - .container => return &scope.cast(Container).?.file_scope.tree, - .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree, + .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.namespace.file_scope.tree, + .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.namespace.file_scope.tree, + .namespace => return &scope.cast(Namespace).?.file_scope.tree, + .decl_ref => return &scope.cast(DeclRef).?.decl.namespace.file_scope.tree, } } @@ -599,16 +587,16 @@ pub const Scope = struct { .local_val => return scope.cast(LocalVal).?.gen_zir, .local_ptr => return scope.cast(LocalPtr).?.gen_zir, .file => unreachable, - .container => unreachable, + .namespace => unreachable, .decl_ref => unreachable, }; } - /// Asserts the scope has a parent which is a Container or File and + /// Asserts the scope has a parent which is a Namespace 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, + .namespace => return @fieldParentPtr(Namespace, "base", base).file_scope.sub_file_path, .file => return @fieldParentPtr(File, "base", base).sub_file_path, .block => unreachable, .gen_zir => unreachable, @@ -618,30 +606,18 @@ pub const Scope = struct { } } - 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), - .gen_zir => unreachable, - .local_val => unreachable, - .local_ptr => unreachable, - .block => unreachable, - .decl_ref => unreachable, - } - } - /// When called from inside a Block Scope, chases the src_decl, not the owner_decl. pub fn getFileScope(base: *Scope) *Scope.File { var cur = base; while (true) { cur = switch (cur.tag) { - .container => return @fieldParentPtr(Container, "base", cur).file_scope, + .namespace => return @fieldParentPtr(Namespace, "base", cur).file_scope, .file => return @fieldParentPtr(File, "base", cur), .gen_zir => @fieldParentPtr(GenZir, "base", cur).parent, .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, - .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope, - .decl_ref => return @fieldParentPtr(DeclRef, "base", cur).decl.container.file_scope, + .block => return @fieldParentPtr(Block, "base", cur).src_decl.namespace.file_scope, + .decl_ref => return @fieldParentPtr(DeclRef, "base", cur).decl.namespace.file_scope, }; } } @@ -657,8 +633,8 @@ pub const Scope = struct { pub const Tag = enum { /// .zig source code. file, - /// struct, enum or union, every .file contains one of these. - container, + /// Namespace owned by structs, enums, unions, and opaques for decls. + namespace, block, gen_zir, local_val, @@ -669,37 +645,44 @@ pub const Scope = struct { decl_ref, }; - pub const Container = struct { - pub const base_tag: Tag = .container; + /// The container that structs, enums, unions, and opaques have. + pub const Namespace = struct { + pub const base_tag: Tag = .namespace; base: Scope = Scope{ .tag = base_tag }, + parent: ?*Namespace, file_scope: *Scope.File, parent_name_hash: NameHash, - - /// Direct children of the file. - decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + /// Will be a struct, enum, union, or opaque. ty: Type, + /// Direct children of the namespace. Used during an update to detect + /// which decls have been added/removed from source. + decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, - pub fn deinit(cont: *Container, gpa: *Allocator) void { - cont.decls.deinit(gpa); - // TODO either Container of File should have an arena for sub_file_path and ty - gpa.destroy(cont.ty.castTag(.empty_struct).?); - gpa.free(cont.file_scope.sub_file_path); - cont.* = undefined; + pub fn deinit(ns: *Namespace, gpa: *Allocator) void { + ns.decls.deinit(gpa); + ns.* = undefined; } - pub fn removeDecl(cont: *Container, child: *Decl) void { - _ = cont.decls.swapRemove(child); + pub fn removeDecl(ns: *Namespace, child: *Decl) void { + _ = ns.decls.swapRemove(child); } - pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash { - return std.zig.hashName(cont.parent_name_hash, ".", name); + /// Must generate unique bytes with no collisions with other decls. + /// The point of hashing here is only to limit the number of bytes of + /// the unique identifier to a fixed size (16 bytes). + pub fn fullyQualifiedNameHash(ns: Namespace, name: []const u8) NameHash { + return std.zig.hashName(ns.parent_name_hash, ".", name); } - pub fn renderFullyQualifiedName(cont: Container, name: []const u8, writer: anytype) !void { + pub fn renderFullyQualifiedName(ns: Namespace, name: []const u8, writer: anytype) !void { // TODO this should render e.g. "std.fs.Dir.OpenOptions" return writer.writeAll(name); } + + pub fn getDecl(ns: Namespace) *Decl { + return ns.ty.getOwnerDecl(); + } }; pub const File = struct { @@ -711,46 +694,54 @@ pub const Scope = struct { unloaded_parse_failure, loaded_success, }, - + source_loaded: bool, /// Relative to the owning package's root_src_dir. - /// Reference to external memory, not owned by File. + /// Memory is stored in gpa, owned by File. sub_file_path: []const u8, - source: union(enum) { - unloaded: void, - bytes: [:0]const u8, - }, + /// Whether this is populated depends on `source_loaded`. + source: [:0]const u8, + /// Whether this is populated depends on `status`. + stat_size: u64, + /// Whether this is populated depends on `status`. + stat_inode: std.fs.File.INode, + /// Whether this is populated depends on `status`. + stat_mtime: i128, + /// Whether this is populated depends on `status`. + source_hash: Cache.BinDigest, /// Whether this is populated or not depends on `status`. tree: ast.Tree, /// Package that this file is a part of, managed externally. pkg: *Package, - - root_container: Container, + /// The namespace of the struct that represents this file. + namespace: *Namespace, pub fn unload(file: *File, gpa: *Allocator) void { - switch (file.status) { - .unloaded_parse_failure, - .never_loaded, - .unloaded_success, - => { - file.status = .unloaded_success; - }, + file.unloadTree(gpa); + file.unloadSource(gpa); + } - .loaded_success => { - file.tree.deinit(gpa); - file.status = .unloaded_success; - }, + pub fn unloadTree(file: *File, gpa: *Allocator) void { + if (file.status == .loaded_success) { + file.tree.deinit(gpa); } - switch (file.source) { - .bytes => |bytes| { - gpa.free(bytes); - file.source = .{ .unloaded = {} }; - }, - .unloaded => {}, + file.status = .unloaded_success; + } + + pub fn unloadSource(file: *File, gpa: *Allocator) void { + if (file.source_loaded) { + file.source_loaded = false; + gpa.free(file.source); + } + } + + pub fn updateTreeToNewSource(file: *File) void { + assert(file.source_loaded); + if (file.status == .loaded_success) { + file.tree.source = file.source; } } pub fn deinit(file: *File, gpa: *Allocator) void { - file.root_container.deinit(gpa); file.unload(gpa); file.* = undefined; } @@ -765,22 +756,44 @@ pub const Scope = struct { std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 }); } - pub fn getSource(file: *File, module: *Module) ![:0]const u8 { - switch (file.source) { - .unloaded => { - const source = try file.pkg.root_src_directory.handle.readFileAllocOptions( - module.gpa, - file.sub_file_path, - std.math.maxInt(u32), - null, - 1, - 0, - ); - file.source = .{ .bytes = source }; - return source; - }, - .bytes => |bytes| return bytes, - } + pub fn getSource(file: *File, gpa: *Allocator) ![:0]const u8 { + if (file.source_loaded) return file.source; + + // Keep track of inode, file size, mtime, hash so we can detect which files + // have been modified when an incremental update is requested. + var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{}); + defer f.close(); + + const stat = try f.stat(); + + try file.finishGettingSource(gpa, f, stat); + assert(file.source_loaded); + return file.source; + } + + pub fn finishGettingSource( + file: *File, + gpa: *Allocator, + f: std.fs.File, + stat: std.fs.File.Stat, + ) !void { + if (stat.size > std.math.maxInt(u32)) + return error.FileTooBig; + + const source = try gpa.allocSentinel(u8, stat.size, 0); + const amt = try f.readAll(source); + if (amt != stat.size) + return error.UnexpectedEndOfFile; + + var hasher = Cache.hasher_init; + hasher.update(source); + hasher.final(&file.source_hash); + + file.stat_size = stat.size; + file.stat_inode = stat.inode; + file.stat_mtime = stat.mtime; + file.source = source; + file.source_loaded = true; } }; @@ -859,7 +872,7 @@ pub const Scope = struct { } pub fn getFileScope(block: *Block) *Scope.File { - return block.src_decl.container.file_scope; + return block.src_decl.namespace.file_scope; } pub fn addNoOp( @@ -1110,7 +1123,7 @@ pub const Scope = struct { } pub fn tree(gz: *const GenZir) *const ast.Tree { - return &gz.astgen.decl.container.file_scope.tree; + return &gz.astgen.decl.namespace.file_scope.tree; } pub fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void { @@ -1678,7 +1691,7 @@ pub const SrcLoc = struct { .node_offset_switch_range, .node_offset_fn_type_cc, .node_offset_fn_type_ret_ty, - => src_loc.container.decl.container.file_scope, + => src_loc.container.decl.namespace.file_scope, }; } @@ -1706,14 +1719,14 @@ pub const SrcLoc = struct { .token_offset => |tok_off| { const decl = src_loc.container.decl; const tok_index = decl.srcToken() + tok_off; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const token_starts = tree.tokens.items(.start); return token_starts[tok_index]; }, .node_offset, .node_offset_bin_op => |node_off| { const decl = src_loc.container.decl; const node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const main_tokens = tree.nodes.items(.main_token); const tok_index = main_tokens[node]; const token_starts = tree.tokens.items(.start); @@ -1722,7 +1735,7 @@ pub const SrcLoc = struct { .node_offset_back2tok => |node_off| { const decl = src_loc.container.decl; const node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const tok_index = tree.firstToken(node) - 2; const token_starts = tree.tokens.items(.start); return token_starts[tok_index]; @@ -1730,7 +1743,7 @@ pub const SrcLoc = struct { .node_offset_var_decl_ty => |node_off| { const decl = src_loc.container.decl; const node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_tags = tree.nodes.items(.tag); const full = switch (node_tags[node]) { .global_var_decl => tree.globalVarDecl(node), @@ -1750,7 +1763,7 @@ pub const SrcLoc = struct { }, .node_offset_builtin_call_arg0 => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1766,7 +1779,7 @@ pub const SrcLoc = struct { }, .node_offset_builtin_call_arg1 => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1782,7 +1795,7 @@ pub const SrcLoc = struct { }, .node_offset_array_access_index => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1793,7 +1806,7 @@ pub const SrcLoc = struct { }, .node_offset_slice_sentinel => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1810,7 +1823,7 @@ pub const SrcLoc = struct { }, .node_offset_call_func => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1837,7 +1850,7 @@ pub const SrcLoc = struct { }, .node_offset_field_name => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1850,7 +1863,7 @@ pub const SrcLoc = struct { }, .node_offset_deref_ptr => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1860,7 +1873,7 @@ pub const SrcLoc = struct { }, .node_offset_asm_source => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1876,7 +1889,7 @@ pub const SrcLoc = struct { }, .node_offset_asm_ret_ty => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -1894,7 +1907,7 @@ pub const SrcLoc = struct { .node_offset_for_cond, .node_offset_if_cond => |node_off| { const decl = src_loc.container.decl; const node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_tags = tree.nodes.items(.tag); const src_node = switch (node_tags[node]) { .if_simple => tree.ifSimple(node).ast.cond_expr, @@ -1914,7 +1927,7 @@ pub const SrcLoc = struct { .node_offset_bin_lhs => |node_off| { const decl = src_loc.container.decl; const node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const src_node = node_datas[node].lhs; const main_tokens = tree.nodes.items(.main_token); @@ -1925,7 +1938,7 @@ pub const SrcLoc = struct { .node_offset_bin_rhs => |node_off| { const decl = src_loc.container.decl; const node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const src_node = node_datas[node].rhs; const main_tokens = tree.nodes.items(.main_token); @@ -1937,7 +1950,7 @@ pub const SrcLoc = struct { .node_offset_switch_operand => |node_off| { const decl = src_loc.container.decl; const node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const src_node = node_datas[node].lhs; const main_tokens = tree.nodes.items(.main_token); @@ -1949,7 +1962,7 @@ pub const SrcLoc = struct { .node_offset_switch_special_prong => |node_off| { const decl = src_loc.container.decl; const switch_node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); @@ -1976,7 +1989,7 @@ pub const SrcLoc = struct { .node_offset_switch_range => |node_off| { const decl = src_loc.container.decl; const switch_node = decl.relativeToNodeIndex(node_off); - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); @@ -2006,7 +2019,7 @@ pub const SrcLoc = struct { .node_offset_fn_type_cc => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -2026,7 +2039,7 @@ pub const SrcLoc = struct { .node_offset_fn_type_ret_ty => |node_off| { const decl = src_loc.container.decl; - const tree = decl.container.file_scope.base.tree(); + const tree = decl.namespace.file_scope.base.tree(); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(node_off); @@ -2351,7 +2364,6 @@ pub fn deinit(mod: *Module) void { mod.export_owners.deinit(gpa); mod.symbol_exports.deinit(gpa); - mod.root_scope.destroy(gpa); var it = mod.global_error_set.iterator(); while (it.next()) |entry| { @@ -2362,6 +2374,7 @@ pub fn deinit(mod: *Module) void { mod.error_name_list.deinit(gpa); for (mod.import_table.items()) |entry| { + gpa.free(entry.key); entry.value.destroy(gpa); } mod.import_table.deinit(gpa); @@ -2465,7 +2478,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { const tracy = trace(@src()); defer tracy.end(); - const tree = try mod.getAstTree(decl.container.file_scope); + const tree = try mod.getAstTree(decl.namespace.file_scope); const node_tags = tree.nodes.items(.tag); const node_datas = tree.nodes.items(.data); const decl_node = decl.src_node; @@ -2516,7 +2529,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { var gen_scope: Scope.GenZir = .{ .force_comptime = true, - .parent = &decl.container.base, + .parent = &decl.namespace.base, .astgen = &astgen, }; defer gen_scope.instructions.deinit(mod.gpa); @@ -2590,7 +2603,7 @@ fn astgenAndSemaFn( var fn_type_scope: Scope.GenZir = .{ .force_comptime = true, - .parent = &decl.container.base, + .parent = &decl.namespace.base, .astgen = &fn_type_astgen, }; defer fn_type_scope.instructions.deinit(mod.gpa); @@ -2829,7 +2842,7 @@ fn astgenAndSemaFn( var gen_scope: Scope.GenZir = .{ .force_comptime = false, - .parent = &decl.container.base, + .parent = &decl.namespace.base, .astgen = &astgen, }; defer gen_scope.instructions.deinit(mod.gpa); @@ -3035,7 +3048,7 @@ fn astgenAndSemaVarDecl( var gen_scope: Scope.GenZir = .{ .force_comptime = true, - .parent = &decl.container.base, + .parent = &decl.namespace.base, .astgen = &astgen, }; defer gen_scope.instructions.deinit(mod.gpa); @@ -3104,7 +3117,7 @@ fn astgenAndSemaVarDecl( var type_scope: Scope.GenZir = .{ .force_comptime = true, - .parent = &decl.container.base, + .parent = &decl.namespace.base, .astgen = &astgen, }; defer type_scope.instructions.deinit(mod.gpa); @@ -3220,46 +3233,48 @@ pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !u3 return @intCast(u32, gop.index); } -pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree { +pub fn getAstTree(mod: *Module, file: *Scope.File) !*const ast.Tree { const tracy = trace(@src()); defer tracy.end(); - switch (root_scope.status) { + switch (file.status) { .never_loaded, .unloaded_success => { - try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.items().len + 1); + const gpa = mod.gpa; + + try mod.failed_files.ensureCapacity(gpa, mod.failed_files.items().len + 1); - const source = try root_scope.getSource(mod); + const source = try file.getSource(gpa); var keep_tree = false; - root_scope.tree = try std.zig.parse(mod.gpa, source); - defer if (!keep_tree) root_scope.tree.deinit(mod.gpa); + file.tree = try std.zig.parse(gpa, source); + defer if (!keep_tree) file.tree.deinit(gpa); - const tree = &root_scope.tree; + const tree = &file.tree; if (tree.errors.len != 0) { const parse_err = tree.errors[0]; - var msg = std.ArrayList(u8).init(mod.gpa); + var msg = std.ArrayList(u8).init(gpa); defer msg.deinit(); const token_starts = tree.tokens.items(.start); try tree.renderError(parse_err, msg.writer()); - const err_msg = try mod.gpa.create(ErrorMsg); + const err_msg = try gpa.create(ErrorMsg); err_msg.* = .{ .src_loc = .{ - .container = .{ .file_scope = root_scope }, + .container = .{ .file_scope = file }, .lazy = .{ .byte_abs = token_starts[parse_err.token] }, }, .msg = msg.toOwnedSlice(), }; - mod.failed_files.putAssumeCapacityNoClobber(root_scope, err_msg); - root_scope.status = .unloaded_parse_failure; + mod.failed_files.putAssumeCapacityNoClobber(file, err_msg); + file.status = .unloaded_parse_failure; return error.AnalysisFail; } - root_scope.status = .loaded_success; + file.status = .loaded_success; keep_tree = true; return tree; @@ -3267,30 +3282,186 @@ pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree { .unloaded_parse_failure => return error.AnalysisFail, - .loaded_success => return &root_scope.tree, + .loaded_success => return &file.tree, } } -pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { +pub fn importFile(mod: *Module, cur_pkg: *Package, import_string: []const u8) !*Scope.File { + const gpa = mod.gpa; + + const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse "."; + const found_pkg = cur_pkg.table.get(import_string); + + const resolved_path = if (found_pkg) |pkg| + try std.fs.path.resolve(gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path }) + else + try std.fs.path.resolve(gpa, &[_][]const u8{ cur_pkg_dir_path, import_string }); + var keep_resolved_path = false; + defer if (!keep_resolved_path) gpa.free(resolved_path); + + const gop = try mod.import_table.getOrPut(gpa, resolved_path); + if (gop.found_existing) return gop.entry.value; + + if (found_pkg == null) { + const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path}); + defer gpa.free(resolved_root_path); + + if (!mem.startsWith(u8, resolved_path, resolved_root_path)) { + return error.ImportOutsidePkgPath; + } + } + + const new_file = try gpa.create(Scope.File); + gop.entry.value = new_file; + new_file.* = .{ + .sub_file_path = resolved_path, + .source = undefined, + .source_hash = undefined, + .source_loaded = false, + .stat_size = undefined, + .stat_inode = undefined, + .stat_mtime = undefined, + .tree = undefined, + .status = .never_loaded, + .pkg = found_pkg orelse cur_pkg, + .namespace = undefined, + }; + keep_resolved_path = true; + + const tree = try mod.getAstTree(new_file); + + const parent_name_hash: Scope.NameHash = if (found_pkg) |pkg| + pkg.namespace_hash + else + std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path); + + // We need a Decl to pass to AstGen and collect dependencies. But ultimately we + // want to pass them on to the Decl for the struct that represents the file. + var tmp_namespace: Scope.Namespace = .{ + .parent = null, + .file_scope = new_file, + .parent_name_hash = parent_name_hash, + .ty = Type.initTag(.type), + }; + const top_decl = try mod.createNewDecl( + &tmp_namespace, + resolved_path, + 0, + parent_name_hash, + new_file.source_hash, + ); + defer { + mod.decl_table.removeAssertDiscard(parent_name_hash); + top_decl.destroy(mod); + } + + var gen_scope_arena = std.heap.ArenaAllocator.init(gpa); + defer gen_scope_arena.deinit(); + + var astgen = try AstGen.init(mod, top_decl, &gen_scope_arena.allocator); + defer astgen.deinit(); + + var gen_scope: Scope.GenZir = .{ + .force_comptime = true, + .parent = &new_file.base, + .astgen = &astgen, + }; + defer gen_scope.instructions.deinit(gpa); + + const container_decl: ast.full.ContainerDecl = .{ + .layout_token = null, + .ast = .{ + .main_token = undefined, + .enum_token = null, + .members = tree.rootDecls(), + .arg = 0, + }, + }; + + const struct_decl_ref = try AstGen.structDeclInner( + &gen_scope, + &gen_scope.base, + 0, + container_decl, + .struct_decl, + ); + _ = try gen_scope.addBreak(.break_inline, 0, struct_decl_ref); + + var code = try gen_scope.finish(); + defer code.deinit(gpa); + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + code.dump(gpa, "import", &gen_scope.base, 0) catch {}; + } + + var sema: Sema = .{ + .mod = mod, + .gpa = gpa, + .arena = &gen_scope_arena.allocator, + .code = code, + .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), + .owner_decl = top_decl, + .func = null, + .owner_func = null, + .param_inst_list = &.{}, + }; + var block_scope: Scope.Block = .{ + .parent = null, + .sema = &sema, + .src_decl = top_decl, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer block_scope.instructions.deinit(gpa); + + const init_inst_zir_ref = try sema.rootAsRef(&block_scope); + const analyzed_struct_inst = try sema.resolveInst(init_inst_zir_ref); + assert(analyzed_struct_inst.ty.zigTypeTag() == .Type); + const val = analyzed_struct_inst.value().?; + const struct_ty = try val.toType(&gen_scope_arena.allocator); + const struct_decl = struct_ty.getOwnerDecl(); + + new_file.namespace = struct_ty.getNamespace().?; + new_file.namespace.parent = null; + new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash; + + // Transfer the dependencies to `owner_decl`. + assert(top_decl.dependants.count() == 0); + for (top_decl.dependencies.items()) |entry| { + const dep = entry.key; + dep.removeDependant(top_decl); + if (dep == struct_decl) continue; + _ = try mod.declareDeclDependency(struct_decl, dep); + } + + try mod.analyzeFile(new_file); + return new_file; +} + +pub fn analyzeFile(mod: *Module, file: *Scope.File) !void { + return mod.analyzeNamespace(file.namespace); +} + +pub fn analyzeNamespace(mod: *Module, namespace: *Scope.Namespace) !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 mod.getAstTree(container_scope.file_scope); + const tree = try mod.getAstTree(namespace.file_scope); const node_tags = tree.nodes.items(.tag); const node_datas = tree.nodes.items(.data); const decls = tree.rootDecls(); try mod.comp.work_queue.ensureUnusedCapacity(decls.len); - try container_scope.decls.ensureCapacity(mod.gpa, decls.len); + try namespace.decls.ensureCapacity(mod.gpa, decls.len); - // Keep track of the decls that we expect to see in this file so that + // Keep track of the decls that we expect to see in this namespace so that // we know which ones have been deleted. var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa); defer deleted_decls.deinit(); - try deleted_decls.ensureCapacity(container_scope.decls.items().len); - for (container_scope.decls.items()) |entry| { + try deleted_decls.ensureCapacity(namespace.decls.items().len); + for (namespace.decls.items()) |entry| { deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); } @@ -3310,7 +3481,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { .fn_proto_simple => { var params: [1]ast.Node.Index = undefined; try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3320,7 +3491,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { ); }, .fn_proto_multi => try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3331,7 +3502,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { .fn_proto_one => { var params: [1]ast.Node.Index = undefined; try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3341,7 +3512,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { ); }, .fn_proto => try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3355,7 +3526,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { .fn_proto_simple => { var params: [1]ast.Node.Index = undefined; try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3365,7 +3536,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { ); }, .fn_proto_multi => try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3376,7 +3547,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { .fn_proto_one => { var params: [1]ast.Node.Index = undefined; try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3386,7 +3557,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { ); }, .fn_proto => try mod.semaContainerFn( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3396,7 +3567,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { ), .global_var_decl => try mod.semaContainerVar( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3404,7 +3575,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { tree.globalVarDecl(decl_node), ), .local_var_decl => try mod.semaContainerVar( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3412,7 +3583,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { tree.localVarDecl(decl_node), ), .simple_var_decl => try mod.semaContainerVar( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3420,7 +3591,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { tree.simpleVarDecl(decl_node), ), .aligned_var_decl => try mod.semaContainerVar( - container_scope, + namespace, &deleted_decls, &outdated_decls, decl_node, @@ -3433,11 +3604,11 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index}); defer mod.gpa.free(name); - const name_hash = container_scope.fullyQualifiedNameHash(name); + const name_hash = namespace.fullyQualifiedNameHash(name); const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); - const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_node, name_hash, contents_hash); - container_scope.decls.putAssumeCapacity(new_decl, {}); + const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash); + namespace.decls.putAssumeCapacity(new_decl, {}); mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); }, @@ -3483,7 +3654,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { fn semaContainerFn( mod: *Module, - container_scope: *Scope.Container, + namespace: *Scope.Namespace, deleted_decls: *std.AutoArrayHashMap(*Decl, void), outdated_decls: *std.AutoArrayHashMap(*Decl, void), decl_node: ast.Node.Index, @@ -3500,25 +3671,25 @@ fn semaContainerFn( @panic("TODO missing function name"); }; const name = tree.tokenSlice(name_token); // TODO use identifierTokenString - const name_hash = container_scope.fullyQualifiedNameHash(name); + const name_hash = namespace.fullyQualifiedNameHash(name); const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); if (mod.decl_table.get(name_hash)) |decl| { - // Update the AST Node index of the decl, even if its contents are unchanged, it may + // Update the AST node of the decl; even if its contents are unchanged, it may // have been re-ordered. const prev_src_node = decl.src_node; decl.src_node = decl_node; if (deleted_decls.swapRemove(decl) == null) { decl.analysis = .sema_failure; const msg = try ErrorMsg.create(mod.gpa, .{ - .container = .{ .file_scope = container_scope.file_scope }, + .container = .{ .file_scope = namespace.file_scope }, .lazy = .{ .token_abs = name_token }, - }, "redefinition of '{s}'", .{decl.name}); + }, "redeclaration of '{s}'", .{decl.name}); errdefer msg.destroy(mod.gpa); const other_src_loc: SrcLoc = .{ - .container = .{ .file_scope = decl.container.file_scope }, + .container = .{ .file_scope = decl.namespace.file_scope }, .lazy = .{ .node_abs = prev_src_node }, }; - try mod.errNoteNonLazy(other_src_loc, msg, "previous definition here", .{}); + try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{}); try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); } else { if (!srcHashEql(decl.contents_hash, contents_hash)) { @@ -3542,8 +3713,8 @@ fn semaContainerFn( } } } else { - const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_node, name_hash, contents_hash); - container_scope.decls.putAssumeCapacity(new_decl, {}); + const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash); + namespace.decls.putAssumeCapacity(new_decl, {}); if (fn_proto.extern_export_token) |maybe_export_token| { const token_tags = tree.tokens.items(.tag); if (token_tags[maybe_export_token] == .keyword_export) { @@ -3556,7 +3727,7 @@ fn semaContainerFn( fn semaContainerVar( mod: *Module, - container_scope: *Scope.Container, + namespace: *Scope.Namespace, deleted_decls: *std.AutoArrayHashMap(*Decl, void), outdated_decls: *std.AutoArrayHashMap(*Decl, void), decl_node: ast.Node.Index, @@ -3568,7 +3739,7 @@ fn semaContainerVar( const name_token = var_decl.ast.mut_token + 1; const name = tree.tokenSlice(name_token); // TODO identifierTokenString - const name_hash = container_scope.fullyQualifiedNameHash(name); + const name_hash = namespace.fullyQualifiedNameHash(name); const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); if (mod.decl_table.get(name_hash)) |decl| { // Update the AST Node index of the decl, even if its contents are unchanged, it may @@ -3578,23 +3749,23 @@ fn semaContainerVar( if (deleted_decls.swapRemove(decl) == null) { decl.analysis = .sema_failure; const msg = try ErrorMsg.create(mod.gpa, .{ - .container = .{ .file_scope = container_scope.file_scope }, + .container = .{ .file_scope = namespace.file_scope }, .lazy = .{ .token_abs = name_token }, - }, "redefinition of '{s}'", .{decl.name}); + }, "redeclaration of '{s}'", .{decl.name}); errdefer msg.destroy(mod.gpa); const other_src_loc: SrcLoc = .{ - .container = .{ .file_scope = decl.container.file_scope }, + .container = .{ .file_scope = decl.namespace.file_scope }, .lazy = .{ .node_abs = prev_src_node }, }; - try mod.errNoteNonLazy(other_src_loc, msg, "previous definition here", .{}); + try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{}); try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); } else if (!srcHashEql(decl.contents_hash, contents_hash)) { try outdated_decls.put(decl, {}); decl.contents_hash = contents_hash; } } else { - const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_node, name_hash, contents_hash); - container_scope.decls.putAssumeCapacity(new_decl, {}); + const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash); + namespace.decls.putAssumeCapacity(new_decl, {}); if (var_decl.extern_export_token) |maybe_export_token| { const token_tags = tree.tokens.items(.tag); if (token_tags[maybe_export_token] == .keyword_export) { @@ -3624,7 +3795,7 @@ pub fn deleteDecl( // Remove from the namespace it resides in. In the case of an anonymous Decl it will // not be present in the set, and this does nothing. - decl.container.removeDecl(decl); + decl.namespace.removeDecl(decl); const name_hash = decl.fullyQualifiedNameHash(); mod.decl_table.removeAssertDiscard(name_hash); @@ -3786,7 +3957,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { fn allocateNewDecl( mod: *Module, - scope: *Scope, + namespace: *Scope.Namespace, src_node: ast.Node.Index, contents_hash: std.zig.SrcHash, ) !*Decl { @@ -3802,7 +3973,7 @@ fn allocateNewDecl( new_decl.* = .{ .name = "", - .container = scope.namespace(), + .namespace = namespace, .src_node = src_node, .typed_value = .{ .never_succeeded = {} }, .analysis = .unreferenced, @@ -3832,14 +4003,14 @@ fn allocateNewDecl( fn createNewDecl( mod: *Module, - scope: *Scope, + namespace: *Scope.Namespace, decl_name: []const u8, src_node: ast.Node.Index, name_hash: Scope.NameHash, contents_hash: std.zig.SrcHash, ) !*Decl { try mod.decl_table.ensureCapacity(mod.gpa, mod.decl_table.items().len + 1); - const new_decl = try mod.allocateNewDecl(scope, src_node, contents_hash); + const new_decl = try mod.allocateNewDecl(namespace, src_node, contents_hash); errdefer mod.gpa.destroy(new_decl); new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name); mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); @@ -3930,7 +4101,7 @@ pub fn analyzeExport( ); errdefer msg.destroy(mod.gpa); try mod.errNote( - &other_export.owner_decl.container.base, + &other_export.owner_decl.namespace.base, other_export.src, msg, "other symbol here", @@ -4050,9 +4221,10 @@ pub fn createAnonymousDecl( const scope_decl = scope.ownerDecl().?; const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); defer mod.gpa.free(name); - const name_hash = scope.namespace().fullyQualifiedNameHash(name); + const namespace = scope_decl.namespace; + const name_hash = namespace.fullyQualifiedNameHash(name); const src_hash: std.zig.SrcHash = undefined; - const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_node, name_hash, src_hash); + const new_decl = try mod.createNewDecl(namespace, name, scope_decl.src_node, name_hash, src_hash); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); decl_arena_state.* = decl_arena.state; @@ -4076,55 +4248,30 @@ pub fn createAnonymousDecl( return new_decl; } -pub fn createContainerDecl( - mod: *Module, - scope: *Scope, - base_token: std.zig.ast.TokenIndex, - decl_arena: *std.heap.ArenaAllocator, - typed_value: TypedValue, -) !*Decl { - const scope_decl = scope.ownerDecl().?; - const name = try mod.getAnonTypeName(scope, base_token); - defer mod.gpa.free(name); - const name_hash = scope.namespace().fullyQualifiedNameHash(name); - const src_hash: std.zig.SrcHash = undefined; - const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_node, name_hash, src_hash); - const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); - - decl_arena_state.* = decl_arena.state; - new_decl.typed_value = .{ - .most_recent = .{ - .typed_value = typed_value, - .arena = decl_arena_state, - }, - }; - new_decl.analysis = .complete; - new_decl.generation = mod.generation; - - return new_decl; -} - -fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { - // TODO add namespaces, generic function signatrues - const tree = scope.tree(); - const token_tags = tree.tokens.items(.tag); - const base_name = switch (token_tags[base_token]) { - .keyword_struct => "struct", - .keyword_enum => "enum", - .keyword_union => "union", - .keyword_opaque => "opaque", - else => unreachable, - }; - const loc = tree.tokenLocation(0, base_token); - return std.fmt.allocPrint(mod.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column }); -} - fn getNextAnonNameIndex(mod: *Module) usize { return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); } -pub fn lookupDeclName(mod: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { - const namespace = scope.namespace(); +/// This looks up a bare identifier in the given scope. This will walk up the tree of namespaces +/// in scope and check each one for the identifier. +pub fn lookupIdentifier(mod: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { + var namespace = scope.namespace(); + while (true) { + if (mod.lookupInNamespace(namespace, ident_name)) |decl| { + return decl; + } + namespace = namespace.parent orelse break; + } + return null; +} + +/// This looks up a member of a specific namespace. It is affected by `usingnamespace` but +/// only for ones in the specified namespace. +pub fn lookupInNamespace( + mod: *Module, + namespace: *Scope.Namespace, + ident_name: []const u8, +) ?*Decl { const name_hash = namespace.fullyQualifiedNameHash(ident_name); return mod.decl_table.get(name_hash); } @@ -4271,7 +4418,7 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); }, .file => unreachable, - .container => unreachable, + .namespace => unreachable, .decl_ref => { const decl_ref = scope.cast(Scope.DeclRef).?; decl_ref.decl.analysis = .sema_failure; @@ -4683,10 +4830,3 @@ pub fn parseStrLit( }, } } - -pub fn unloadFile(mod: *Module, file_scope: *Scope.File) void { - if (file_scope.status == .unloaded_parse_failure) { - mod.failed_files.swapRemove(file_scope).?.value.destroy(mod.gpa); - } - file_scope.unload(mod.gpa); -} |
