diff options
Diffstat (limited to 'src/Module.zig')
| -rw-r--r-- | src/Module.zig | 115 |
1 files changed, 78 insertions, 37 deletions
diff --git a/src/Module.zig b/src/Module.zig index 4ed39c9954..07b86c0d51 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -11,7 +11,7 @@ const log = std.log.scoped(.module); const BigIntConst = std.math.big.int.Const; const BigIntMutable = std.math.big.int.Mutable; const Target = std.Target; -const ast = std.zig.ast; +const Ast = std.zig.Ast; const Module = @This(); const Compilation = @import("Compilation.zig"); @@ -291,7 +291,7 @@ pub const Decl = struct { generation: u32, /// The AST node index of this declaration. /// Must be recomputed when the corresponding source file is modified. - src_node: ast.Node.Index, + src_node: Ast.Node.Index, /// Line number corresponding to `src_node`. Stored separately so that source files /// do not need to be loaded into memory in order to compute debug line numbers. src_line: u32, @@ -365,6 +365,8 @@ pub const Decl = struct { /// Decl is marked alive, then it sends the Decl to the linker. Otherwise it /// deletes the Decl on the spot. alive: bool, + /// Whether the Decl is a `usingnamespace` declaration. + is_usingnamespace: bool, /// Represents the position of the code in the output file. /// This is populated regardless of semantic analysis and code generation. @@ -497,19 +499,19 @@ pub const Decl = struct { return decl.src_line + offset; } - pub fn relativeToNodeIndex(decl: Decl, offset: i32) ast.Node.Index { - return @bitCast(ast.Node.Index, offset + @bitCast(i32, decl.src_node)); + pub fn relativeToNodeIndex(decl: Decl, offset: i32) Ast.Node.Index { + return @bitCast(Ast.Node.Index, offset + @bitCast(i32, decl.src_node)); } - pub fn nodeIndexToRelative(decl: Decl, node_index: ast.Node.Index) i32 { + pub fn nodeIndexToRelative(decl: Decl, node_index: Ast.Node.Index) i32 { return @bitCast(i32, node_index) - @bitCast(i32, decl.src_node); } - pub fn tokSrcLoc(decl: Decl, token_index: ast.TokenIndex) LazySrcLoc { + pub fn tokSrcLoc(decl: Decl, token_index: Ast.TokenIndex) LazySrcLoc { return .{ .token_offset = token_index - decl.srcToken() }; } - pub fn nodeSrcLoc(decl: Decl, node_index: ast.Node.Index) LazySrcLoc { + pub fn nodeSrcLoc(decl: Decl, node_index: Ast.Node.Index) LazySrcLoc { return .{ .node_offset = decl.nodeIndexToRelative(node_index) }; } @@ -525,7 +527,7 @@ pub const Decl = struct { }; } - pub fn srcToken(decl: Decl) ast.TokenIndex { + pub fn srcToken(decl: Decl) Ast.TokenIndex { const tree = &decl.namespace.file_scope.tree; return tree.firstToken(decl.src_node); } @@ -1008,6 +1010,11 @@ pub const Scope = struct { anon_decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + /// Key is usingnamespace Decl itself. To find the namespace being included, + /// the Decl Value has to be resolved as a Type which has a Namespace. + /// Value is whether the usingnamespace decl is marked `pub`. + usingnamespace_set: std.AutoHashMapUnmanaged(*Decl, bool) = .{}, + pub fn deinit(ns: *Namespace, mod: *Module) void { ns.destroyDecls(mod); ns.* = undefined; @@ -1114,7 +1121,7 @@ pub const Scope = struct { /// Whether this is populated depends on `status`. stat_mtime: i128, /// Whether this is populated or not depends on `tree_loaded`. - tree: ast.Tree, + tree: Ast, /// Whether this is populated or not depends on `zir_loaded`. zir: Zir, /// Package that this file is a part of, managed externally. @@ -1213,7 +1220,7 @@ pub const Scope = struct { return source; } - pub fn getTree(file: *File, gpa: *Allocator) !*const ast.Tree { + pub fn getTree(file: *File, gpa: *Allocator) !*const Ast { if (file.tree_loaded) return &file.tree; const source = try file.getSource(gpa); @@ -1558,17 +1565,17 @@ pub const ErrorMsg = struct { pub const SrcLoc = struct { file_scope: *Scope.File, /// Might be 0 depending on tag of `lazy`. - parent_decl_node: ast.Node.Index, + parent_decl_node: Ast.Node.Index, /// Relative to `parent_decl_node`. lazy: LazySrcLoc, - pub fn declSrcToken(src_loc: SrcLoc) ast.TokenIndex { + pub fn declSrcToken(src_loc: SrcLoc) Ast.TokenIndex { const tree = src_loc.file_scope.tree; return tree.firstToken(src_loc.parent_decl_node); } - pub fn declRelativeToNodeIndex(src_loc: SrcLoc, offset: i32) ast.TokenIndex { - return @bitCast(ast.Node.Index, offset + @bitCast(i32, src_loc.parent_decl_node)); + pub fn declRelativeToNodeIndex(src_loc: SrcLoc, offset: i32) Ast.TokenIndex { + return @bitCast(Ast.Node.Index, offset + @bitCast(i32, src_loc.parent_decl_node)); } pub fn byteOffset(src_loc: SrcLoc, gpa: *Allocator) !u32 { @@ -1694,7 +1701,7 @@ pub const SrcLoc = struct { const tree = try src_loc.file_scope.getTree(gpa); const node_tags = tree.nodes.items(.tag); const node = src_loc.declRelativeToNodeIndex(node_off); - var params: [1]ast.Node.Index = undefined; + var params: [1]Ast.Node.Index = undefined; const full = switch (node_tags[node]) { .call_one, .call_one_comma, @@ -1824,7 +1831,7 @@ pub const SrcLoc = struct { const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); - const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); + const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); const case_nodes = tree.extra_data[extra.start..extra.end]; for (case_nodes) |case_node| { const case = switch (node_tags[case_node]) { @@ -1850,7 +1857,7 @@ pub const SrcLoc = struct { const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); - const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); + const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); const case_nodes = tree.extra_data[extra.start..extra.end]; for (case_nodes) |case_node| { const case = switch (node_tags[case_node]) { @@ -1879,7 +1886,7 @@ pub const SrcLoc = struct { const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = src_loc.declRelativeToNodeIndex(node_off); - var params: [1]ast.Node.Index = undefined; + var params: [1]Ast.Node.Index = undefined; const full = switch (node_tags[node]) { .fn_proto_simple => tree.fnProtoSimple(¶ms, node), .fn_proto_multi => tree.fnProtoMulti(node), @@ -1904,7 +1911,7 @@ pub const SrcLoc = struct { const tree = try src_loc.file_scope.getTree(gpa); const node_tags = tree.nodes.items(.tag); const node = src_loc.declRelativeToNodeIndex(node_off); - var params: [1]ast.Node.Index = undefined; + var params: [1]Ast.Node.Index = undefined; const full = switch (node_tags[node]) { .fn_proto_simple => tree.fnProtoSimple(¶ms, node), .fn_proto_multi => tree.fnProtoMulti(node), @@ -1934,7 +1941,7 @@ pub const SrcLoc = struct { const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const parent_node = src_loc.declRelativeToNodeIndex(node_off); - var params: [1]ast.Node.Index = undefined; + var params: [1]Ast.Node.Index = undefined; const full = switch (node_tags[parent_node]) { .fn_proto_simple => tree.fnProtoSimple(¶ms, parent_node), .fn_proto_multi => tree.fnProtoMulti(parent_node), @@ -2648,7 +2655,10 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void { undefined; defer if (data_has_safety_tag) gpa.free(safety_buffer); const data_ptr = if (data_has_safety_tag) - @ptrCast([*]const u8, safety_buffer.ptr) + if (file.zir.instructions.len == 0) + @as([*]const u8, undefined) + else + @ptrCast([*]const u8, safety_buffer.ptr) else @ptrCast([*]const u8, file.zir.instructions.items(.data).ptr); if (data_has_safety_tag) { @@ -3171,6 +3181,31 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); + if (decl.is_usingnamespace) { + const ty_ty = Type.initTag(.type); + if (!decl_tv.ty.eql(ty_ty)) { + return mod.fail(&block_scope.base, src, "expected type, found {}", .{decl_tv.ty}); + } + var buffer: Value.ToTypeBuffer = undefined; + const ty = decl_tv.val.toType(&buffer); + if (ty.getNamespace() == null) { + return mod.fail(&block_scope.base, src, "type {} has no namespace", .{ty}); + } + + decl.ty = ty_ty; + decl.val = try Value.Tag.ty.create(&decl_arena.allocator, ty); + decl.align_val = Value.initTag(.null_value); + decl.linksection_val = Value.initTag(.null_value); + decl.has_tv = true; + decl.owns_tv = false; + decl_arena_state.* = decl_arena.state; + decl.value_arena = decl_arena_state; + decl.analysis = .complete; + decl.generation = mod.generation; + + return true; + } + if (decl_tv.val.castTag(.function)) |fn_payload| { const func = fn_payload.data; const owns_tv = func.owner_decl == decl; @@ -3266,16 +3301,13 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (type_changed and mod.emit_h != null) { try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); } - } else if (decl_tv.ty.zigTypeTag() == .Type) { - // In case this Decl is a struct or union, we need to resolve the fields - // while we still have the `Sema` in scope, so that the field type expressions - // can use the resolved AIR instructions that they possibly reference. - // We do this after the decl is populated and set to `complete` so that a `Decl` - // may reference itself. - var buffer: Value.ToTypeBuffer = undefined; - const ty = decl.val.toType(&buffer); - try sema.resolveDeclFields(&block_scope, src, ty); } + // In case this Decl is a struct or union, we need to resolve the fields + // while we still have the `Sema` in scope, so that the field type expressions + // can use the resolved AIR instructions that they possibly reference. + // We do this after the decl is populated and set to `complete` so that a `Decl` + // may reference itself. + try sema.resolvePendingTypes(&block_scope); if (decl.is_exported) { const export_src = src; // TODO point to the export token @@ -3491,7 +3523,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // zig fmt: off const is_pub = (flags & 0b0001) != 0; - const is_exported = (flags & 0b0010) != 0; + const export_bit = (flags & 0b0010) != 0; const has_align = (flags & 0b0100) != 0; const has_linksection = (flags & 0b1000) != 0; // zig fmt: on @@ -3506,7 +3538,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi var is_named_test = false; const decl_name: [:0]const u8 = switch (decl_name_index) { 0 => name: { - if (is_exported) { + if (export_bit) { const i = iter.usingnamespace_index; iter.usingnamespace_index += 1; break :name try std.fmt.allocPrintZ(gpa, "usingnamespace_{d}", .{i}); @@ -3532,11 +3564,17 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi } }, }; + const is_exported = export_bit and decl_name_index != 0; + const is_usingnamespace = export_bit and decl_name_index == 0; + if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); // We create a Decl for it regardless of analysis status. const gop = try namespace.decls.getOrPut(gpa, decl_name); if (!gop.found_existing) { const new_decl = try mod.allocateNewDecl(namespace, decl_node); + if (is_usingnamespace) { + namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub); + } log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); new_decl.src_line = line; new_decl.name = decl_name; @@ -3545,7 +3583,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // test decls if in test mode, get analyzed. const decl_pkg = namespace.file_scope.pkg; const want_analysis = is_exported or switch (decl_name_index) { - 0 => true, // comptime decl + 0 => true, // comptime or usingnamespace decl 1 => blk: { // test decl with no name. Skip the part where we check against // the test name filter. @@ -3568,6 +3606,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi } new_decl.is_pub = is_pub; new_decl.is_exported = is_exported; + new_decl.is_usingnamespace = is_usingnamespace; new_decl.has_align = has_align; new_decl.has_linksection = has_linksection; new_decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -3584,6 +3623,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi decl.is_pub = is_pub; decl.is_exported = is_exported; + decl.is_usingnamespace = is_usingnamespace; decl.has_align = has_align; decl.has_linksection = has_linksection; decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -3927,7 +3967,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { decl.analysis = .outdated; } -pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node.Index) !*Decl { +pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.Node.Index) !*Decl { // If we have emit-h then we must allocate a bigger structure to store the emit-h state. const new_decl: *Decl = if (mod.emit_h != null) blk: { const parent_struct = try mod.gpa.create(DeclPlusEmitH); @@ -3976,6 +4016,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast. .has_linksection = false, .has_align = false, .alive = false, + .is_usingnamespace = false, }; return new_decl; } @@ -4196,7 +4237,7 @@ pub fn fail( pub fn failTok( mod: *Module, scope: *Scope, - token_index: ast.TokenIndex, + token_index: Ast.TokenIndex, comptime format: []const u8, args: anytype, ) CompileError { @@ -4209,7 +4250,7 @@ pub fn failTok( pub fn failNode( mod: *Module, scope: *Scope, - node_index: ast.Node.Index, + node_index: Ast.Node.Index, comptime format: []const u8, args: anytype, ) CompileError { @@ -4414,7 +4455,7 @@ pub const SwitchProngSrc = union(enum) { const main_tokens = tree.nodes.items(.main_token); const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); - const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); + const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); const case_nodes = tree.extra_data[extra.start..extra.end]; var multi_i: u32 = 0; |
