diff --git a/src/Module.zig b/src/Module.zig index 4fcd2e7ba6..558ed498ca 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -528,10 +528,10 @@ 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, /// If true `name` is already fully qualified. name_fully_qualified: bool = false, + /// What kind of a declaration is this. + kind: Kind, /// Represents the position of the code in the output file. /// This is populated regardless of semantic analysis and code generation. @@ -551,6 +551,14 @@ pub const Decl = struct { /// typed_value may need to be regenerated. dependencies: DepsTable = .{}, + pub const Kind = enum { + @"usingnamespace", + @"test", + @"comptime", + named, + anon, + }; + pub const Index = enum(u32) { _, @@ -4438,7 +4446,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { // not the struct itself. try sema.resolveTypeLayout(decl_tv.ty); - if (decl.is_usingnamespace) { + if (decl.kind == .@"usingnamespace") { if (!decl_tv.ty.eql(Type.type, mod)) { return sema.fail(&block_scope, ty_src, "expected type, found {}", .{ decl_tv.ty.fmt(mod), @@ -4964,26 +4972,31 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err // Every Decl needs a name. var is_named_test = false; + var kind: Decl.Kind = .named; const decl_name: [:0]const u8 = switch (decl_name_index) { 0 => name: { if (export_bit) { const i = iter.usingnamespace_index; iter.usingnamespace_index += 1; + kind = .@"usingnamespace"; break :name try std.fmt.allocPrintZ(gpa, "usingnamespace_{d}", .{i}); } else { const i = iter.comptime_index; iter.comptime_index += 1; + kind = .@"comptime"; break :name try std.fmt.allocPrintZ(gpa, "comptime_{d}", .{i}); } }, 1 => name: { const i = iter.unnamed_test_index; iter.unnamed_test_index += 1; + kind = .@"test"; break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i}); }, 2 => name: { is_named_test = true; const test_name = zir.nullTerminatedString(decl_doccomment_index); + kind = .@"test"; break :name try std.fmt.allocPrintZ(gpa, "decltest.{s}", .{test_name}); }, else => name: { @@ -4991,6 +5004,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err if (raw_name.len == 0) { is_named_test = true; const test_name = zir.nullTerminatedString(decl_name_index + 1); + kind = .@"test"; break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name}); } else { break :name try gpa.dupeZ(u8, raw_name); @@ -4998,8 +5012,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err }, }; 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); + if (kind == .@"usingnamespace") try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); // We create a Decl for it regardless of analysis status. const gop = try namespace.decls.getOrPutContextAdapted( @@ -5012,8 +5025,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err if (!gop.found_existing) { const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope); const new_decl = mod.declPtr(new_decl_index); + new_decl.kind = kind; new_decl.name = decl_name; - if (is_usingnamespace) { + if (kind == .@"usingnamespace") { namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, is_pub); } log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); @@ -5058,7 +5072,6 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err } 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_or_addrspace = has_linksection_or_addrspace; new_decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -5076,7 +5089,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err decl.is_pub = is_pub; decl.is_exported = is_exported; - decl.is_usingnamespace = is_usingnamespace; + decl.kind = kind; decl.has_align = has_align; decl.has_linksection_or_addrspace = has_linksection_or_addrspace; decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -5635,7 +5648,7 @@ pub fn allocateNewDecl( .has_linksection_or_addrspace = false, .has_align = false, .alive = false, - .is_usingnamespace = false, + .kind = .anon, }; return decl_and_index.decl_index; diff --git a/src/Sema.zig b/src/Sema.zig index 8263468fbf..3db5eb5ba1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16217,11 +16217,54 @@ fn typeInfoDecls( }; try sema.queueFullTypeResolution(try declaration_ty.copy(sema.arena)); - const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0; - const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len); - for (decls_vals) |*decls_val, i| { - const decl_index = opt_namespace.?.decls.keys()[i]; + var decl_vals = std.ArrayList(Value).init(sema.gpa); + defer decl_vals.deinit(); + + var seen_namespaces = std.AutoHashMap(*Namespace, void).init(sema.gpa); + defer seen_namespaces.deinit(); + + if (opt_namespace) |some| { + try sema.typeInfoNamespaceDecls(block, decls_anon_decl.arena(), some, &decl_vals, &seen_namespaces); + } + + const new_decl = try decls_anon_decl.finish( + try Type.Tag.array.create(decls_anon_decl.arena(), .{ + .len = decl_vals.items.len, + .elem_type = declaration_ty, + }), + try Value.Tag.aggregate.create( + decls_anon_decl.arena(), + try decls_anon_decl.arena().dupe(Value, decl_vals.items), + ), + 0, // default alignment + ); + return try Value.Tag.slice.create(sema.arena, .{ + .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), + .len = try Value.Tag.int_u64.create(sema.arena, decl_vals.items.len), + }); +} + +fn typeInfoNamespaceDecls( + sema: *Sema, + block: *Block, + decls_anon_decl: Allocator, + namespace: *Namespace, + decl_vals: *std.ArrayList(Value), + seen_namespaces: *std.AutoHashMap(*Namespace, void), +) !void { + const gop = try seen_namespaces.getOrPut(namespace); + if (gop.found_existing) return; + const decls = namespace.decls.keys(); + for (decls) |decl_index| { const decl = sema.mod.declPtr(decl_index); + if (decl.kind == .@"usingnamespace") { + try sema.mod.ensureDeclAnalyzed(decl_index); + var buf: Value.ToTypeBuffer = undefined; + const new_ns = decl.val.toType(&buf).getNamespace().?; + try sema.typeInfoNamespaceDecls(block, decls_anon_decl, new_ns, decl_vals, seen_namespaces); + continue; + } + if (decl.kind != .named) continue; const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); @@ -16231,37 +16274,21 @@ fn typeInfoDecls( try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), 0, // default alignment ); - break :v try Value.Tag.slice.create(decls_anon_decl.arena(), .{ - .ptr = try Value.Tag.decl_ref.create(decls_anon_decl.arena(), new_decl), - .len = try Value.Tag.int_u64.create(decls_anon_decl.arena(), bytes.len), + break :v try Value.Tag.slice.create(decls_anon_decl, .{ + .ptr = try Value.Tag.decl_ref.create(decls_anon_decl, new_decl), + .len = try Value.Tag.int_u64.create(decls_anon_decl, bytes.len), }); }; - const fields = try decls_anon_decl.arena().create([2]Value); + const fields = try decls_anon_decl.create([2]Value); fields.* = .{ //name: []const u8, name_val, //is_pub: bool, Value.makeBool(decl.is_pub), }; - decls_val.* = try Value.Tag.aggregate.create(decls_anon_decl.arena(), fields); + try decl_vals.append(try Value.Tag.aggregate.create(decls_anon_decl, fields)); } - - const new_decl = try decls_anon_decl.finish( - try Type.Tag.array.create(decls_anon_decl.arena(), .{ - .len = decls_vals.len, - .elem_type = declaration_ty, - }), - try Value.Tag.aggregate.create( - decls_anon_decl.arena(), - try decls_anon_decl.arena().dupe(Value, decls_vals), - ), - 0, // default alignment - ); - return try Value.Tag.slice.create(sema.arena, .{ - .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), - .len = try Value.Tag.int_u64.create(sema.arena, decls_vals.len), - }); } fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 0fb17519e6..ef8c89bd23 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -566,3 +566,27 @@ test "value from struct @typeInfo default_value can be loaded at comptime" { try expect(@ptrCast(*const u8, a).* == 1); } } + +test "@typeInfo decls and usingnamespace" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const A = struct { + const x = 5; + const y = 34; + + comptime {} + }; + const B = struct { + usingnamespace A; + const z = 56; + + test {} + }; + const decls = @typeInfo(B).Struct.decls; + try expect(decls.len == 3); + try expectEqualStrings(decls[0].name, "x"); + try expectEqualStrings(decls[1].name, "y"); + try expectEqualStrings(decls[2].name, "z"); +}