diff options
Diffstat (limited to 'src/Module.zig')
| -rw-r--r-- | src/Module.zig | 140 |
1 files changed, 130 insertions, 10 deletions
diff --git a/src/Module.zig b/src/Module.zig index 20cc6b3c0d..807b30c4d7 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -657,6 +657,7 @@ pub const Scope = struct { /// Direct children of the namespace. Used during an update to detect /// which decls have been added/removed from source. decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + usingnamespace_set: std.AutoHashMapUnmanaged(*Namespace, bool) = .{}, pub fn deinit(ns: *Namespace, gpa: *Allocator) void { ns.decls.deinit(gpa); @@ -2540,6 +2541,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { .code = code, .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -2560,7 +2562,73 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { decl.generation = mod.generation; return true; }, - .@"usingnamespace" => @panic("TODO usingnamespace decl"), + .@"usingnamespace" => { + decl.analysis = .in_progress; + + const type_expr = node_datas[decl_node].lhs; + const is_pub = blk: { + const main_tokens = tree.nodes.items(.main_token); + const token_tags = tree.tokens.items(.tag); + const main_token = main_tokens[decl_node]; + break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub); + }; + + // A usingnamespace decl does not store any value so we can + // deinit this arena after analysis is done. + var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa); + defer analysis_arena.deinit(); + + var code: zir.Code = blk: { + var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator); + defer astgen.deinit(); + + var gen_scope: Scope.GenZir = .{ + .force_comptime = true, + .parent = &decl.namespace.base, + .astgen = &astgen, + }; + defer gen_scope.instructions.deinit(mod.gpa); + + const ns_type = try AstGen.typeExpr(&gen_scope, &gen_scope.base, type_expr); + _ = try gen_scope.addBreak(.break_inline, 0, ns_type); + + const code = try gen_scope.finish(); + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + code.dump(mod.gpa, "usingnamespace_type", &gen_scope.base, 0) catch {}; + } + break :blk code; + }; + defer code.deinit(mod.gpa); + + var sema: Sema = .{ + .mod = mod, + .gpa = mod.gpa, + .arena = &analysis_arena.allocator, + .code = code, + .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len), + .owner_decl = decl, + .namespace = decl.namespace, + .func = null, + .owner_func = null, + .param_inst_list = &.{}, + }; + var block_scope: Scope.Block = .{ + .parent = null, + .sema = &sema, + .src_decl = decl, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer block_scope.instructions.deinit(mod.gpa); + + const ty = try sema.rootAsType(&block_scope); + try decl.namespace.usingnamespace_set.put(mod.gpa, ty.getNamespace().?, is_pub); + + decl.analysis = .complete; + decl.generation = mod.generation; + return true; + }, else => unreachable, } } @@ -2765,6 +2833,7 @@ fn astgenAndSemaFn( .code = fn_type_code, .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3064,6 +3133,7 @@ fn astgenAndSemaVarDecl( .code = code, .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3125,6 +3195,7 @@ fn astgenAndSemaVarDecl( .code = code, .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3387,6 +3458,7 @@ pub fn importFile(mod: *Module, cur_pkg: *Package, import_string: []const u8) !* .code = code, .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), .owner_decl = top_decl, + .namespace = top_decl.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3411,7 +3483,7 @@ pub fn importFile(mod: *Module, cur_pkg: *Package, import_string: []const u8) !* struct_decl.contents_hash = top_decl.contents_hash; new_file.namespace = struct_ty.getNamespace().?; new_file.namespace.parent = null; - new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash; + //new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash; // Transfer the dependencies to `owner_decl`. assert(top_decl.dependants.count() == 0); @@ -3422,24 +3494,31 @@ pub fn importFile(mod: *Module, cur_pkg: *Package, import_string: []const u8) !* _ = 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); + // We call `getAstTree` here so that `analyzeFile` has the error set that includes + // file system operations, but `analyzeNamespace` does not. + const tree = try mod.getAstTree(file.namespace.file_scope); + const decls = tree.rootDecls(); + return mod.analyzeNamespace(file.namespace, decls); } -pub fn analyzeNamespace(mod: *Module, namespace: *Scope.Namespace) !void { +pub fn analyzeNamespace( + mod: *Module, + namespace: *Scope.Namespace, + decls: []const ast.Node.Index, +) InnerError!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(namespace.file_scope); + assert(namespace.file_scope.status == .loaded_success); // Caller must ensure tree loaded. + const tree: *const ast.Tree = &namespace.file_scope.tree; 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 namespace.decls.ensureCapacity(mod.gpa, decls.len); @@ -3612,7 +3691,20 @@ pub fn analyzeNamespace(mod: *Module, namespace: *Scope.Namespace) !void { } }, .@"usingnamespace" => { - log.err("TODO: analyze usingnamespace decl", .{}); + const name_index = mod.getNextAnonNameIndex(); + const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index}); + defer mod.gpa.free(name); + + const name_hash = namespace.fullyQualifiedNameHash(name); + const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); + + const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash); + namespace.decls.putAssumeCapacity(new_decl, {}); + + mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => continue, + }; }, else => unreachable, }; @@ -3900,6 +3992,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { .code = func.zir, .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len), .owner_decl = decl, + .namespace = decl.namespace, .func = func, .owner_func = func, .param_inst_list = param_inst_list, @@ -4001,6 +4094,10 @@ fn createNewDecl( 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); + log.debug("insert Decl {s} with hash {}", .{ + new_decl.name, + std.fmt.fmtSliceHexLower(&name_hash), + }); mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); return new_decl; } @@ -4245,7 +4342,7 @@ fn getNextAnonNameIndex(mod: *Module) usize { 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| { + if (mod.lookupInNamespace(namespace, ident_name, false)) |decl| { return decl; } namespace = namespace.parent orelse break; @@ -4259,9 +4356,32 @@ pub fn lookupInNamespace( mod: *Module, namespace: *Scope.Namespace, ident_name: []const u8, + only_pub_usingnamespaces: bool, ) ?*Decl { const name_hash = namespace.fullyQualifiedNameHash(ident_name); - return mod.decl_table.get(name_hash); + log.debug("lookup Decl {s} with hash {}", .{ + ident_name, + std.fmt.fmtSliceHexLower(&name_hash), + }); + // TODO handle decl collision with usingnamespace + // TODO the decl doing the looking up needs to create a decl dependency + // on each usingnamespace decl here. + if (mod.decl_table.get(name_hash)) |decl| { + return decl; + } + { + var it = namespace.usingnamespace_set.iterator(); + while (it.next()) |entry| { + const other_ns = entry.key; + const other_is_pub = entry.value; + if (only_pub_usingnamespaces and !other_is_pub) continue; + // TODO handle cycles + if (mod.lookupInNamespace(other_ns, ident_name, true)) |decl| { + return decl; + } + } + } + return null; } pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { |
