Sema: fix typeInfo decls with usingnamespace

Closes #12403
This commit is contained in:
Veikka Tuominen 2023-01-10 21:54:58 +02:00
parent 1658e4893d
commit d8128c272a
3 changed files with 98 additions and 34 deletions

View File

@ -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;

View File

@ -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 {

View File

@ -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");
}