mirror of
https://github.com/ziglang/zig.git
synced 2025-12-17 19:53:06 +00:00
Sema: implement type info for structs
This commit is contained in:
parent
23567cdd98
commit
9ea253b9c4
105
src/Sema.zig
105
src/Sema.zig
@ -9958,7 +9958,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
|||||||
// layout: ContainerLayout,
|
// layout: ContainerLayout,
|
||||||
try Value.Tag.enum_field_index.create(
|
try Value.Tag.enum_field_index.create(
|
||||||
sema.arena,
|
sema.arena,
|
||||||
@enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto),
|
@enumToInt(union_ty.containerLayout()),
|
||||||
),
|
),
|
||||||
|
|
||||||
// tag_type: ?type,
|
// tag_type: ?type,
|
||||||
@ -9977,13 +9977,112 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
.Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}),
|
.Struct => {
|
||||||
.Opaque => {
|
|
||||||
// TODO: look into memoizing this result.
|
// TODO: look into memoizing this result.
|
||||||
|
|
||||||
var fields_anon_decl = try block.startAnonDecl(src);
|
var fields_anon_decl = try block.startAnonDecl(src);
|
||||||
defer fields_anon_decl.deinit();
|
defer fields_anon_decl.deinit();
|
||||||
|
|
||||||
|
const struct_field_ty = t: {
|
||||||
|
const struct_field_ty_decl = (try sema.namespaceLookup(
|
||||||
|
block,
|
||||||
|
src,
|
||||||
|
type_info_ty.getNamespace().?,
|
||||||
|
"StructField",
|
||||||
|
)).?;
|
||||||
|
try sema.mod.declareDeclDependency(sema.owner_decl, struct_field_ty_decl);
|
||||||
|
try sema.ensureDeclAnalyzed(struct_field_ty_decl);
|
||||||
|
var buffer: Value.ToTypeBuffer = undefined;
|
||||||
|
break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct_ty = try sema.resolveTypeFields(block, src, ty);
|
||||||
|
const struct_fields = struct_ty.structFields();
|
||||||
|
const struct_field_vals = try fields_anon_decl.arena().alloc(Value, struct_fields.count());
|
||||||
|
const layout = struct_ty.containerLayout();
|
||||||
|
|
||||||
|
for (struct_field_vals) |*field_val, i| {
|
||||||
|
const field = struct_fields.values()[i];
|
||||||
|
const name = struct_fields.keys()[i];
|
||||||
|
const name_val = v: {
|
||||||
|
var anon_decl = try block.startAnonDecl(src);
|
||||||
|
defer anon_decl.deinit();
|
||||||
|
const bytes = try anon_decl.arena().dupeZ(u8, name);
|
||||||
|
const new_decl = try anon_decl.finish(
|
||||||
|
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
|
||||||
|
try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
|
||||||
|
);
|
||||||
|
break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct_field_fields = try fields_anon_decl.arena().create([5]Value);
|
||||||
|
const opt_default_val = if (field.default_val.tag() == .unreachable_value)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
field.default_val;
|
||||||
|
const default_val_ptr = try sema.optRefValue(block, src, field.ty, opt_default_val);
|
||||||
|
const alignment = switch (layout) {
|
||||||
|
.Auto, .Extern => field.normalAlignment(target),
|
||||||
|
.Packed => field.packedAlignment(),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct_field_fields.* = .{
|
||||||
|
// name: []const u8,
|
||||||
|
name_val,
|
||||||
|
// field_type: type,
|
||||||
|
try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty),
|
||||||
|
// default_value: ?*const anyopaque,
|
||||||
|
try default_val_ptr.copy(fields_anon_decl.arena()),
|
||||||
|
// is_comptime: bool,
|
||||||
|
Value.makeBool(field.is_comptime),
|
||||||
|
// alignment: comptime_int,
|
||||||
|
try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
|
||||||
|
};
|
||||||
|
field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), struct_field_fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields_val = v: {
|
||||||
|
const new_decl = try fields_anon_decl.finish(
|
||||||
|
try Type.Tag.array.create(fields_anon_decl.arena(), .{
|
||||||
|
.len = struct_field_vals.len,
|
||||||
|
.elem_type = struct_field_ty,
|
||||||
|
}),
|
||||||
|
try Value.Tag.array.create(
|
||||||
|
fields_anon_decl.arena(),
|
||||||
|
try fields_anon_decl.arena().dupe(Value, struct_field_vals),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace());
|
||||||
|
|
||||||
|
const field_values = try sema.arena.create([4]Value);
|
||||||
|
field_values.* = .{
|
||||||
|
// layout: ContainerLayout,
|
||||||
|
try Value.Tag.enum_field_index.create(
|
||||||
|
sema.arena,
|
||||||
|
@enumToInt(layout),
|
||||||
|
),
|
||||||
|
// fields: []const StructField,
|
||||||
|
fields_val,
|
||||||
|
// decls: []const Declaration,
|
||||||
|
decls_val,
|
||||||
|
// is_tuple: bool,
|
||||||
|
Value.makeBool(struct_ty.isTuple()),
|
||||||
|
};
|
||||||
|
|
||||||
|
return sema.addConstant(
|
||||||
|
type_info_ty,
|
||||||
|
try Value.Tag.@"union".create(sema.arena, .{
|
||||||
|
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Struct)),
|
||||||
|
.val = try Value.Tag.@"struct".create(sema.arena, field_values),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
.Opaque => {
|
||||||
|
// TODO: look into memoizing this result.
|
||||||
|
|
||||||
const opaque_ty = try sema.resolveTypeFields(block, src, ty);
|
const opaque_ty = try sema.resolveTypeFields(block, src, ty);
|
||||||
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace());
|
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace());
|
||||||
|
|
||||||
|
|||||||
@ -249,26 +249,38 @@ fn testUnion() !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "type info: struct info" {
|
test "type info: struct info" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testStruct();
|
try testStruct();
|
||||||
comptime try testStruct();
|
comptime try testStruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testStruct() !void {
|
fn testStruct() !void {
|
||||||
const unpacked_struct_info = @typeInfo(TestUnpackedStruct);
|
const unpacked_struct_info = @typeInfo(TestStruct);
|
||||||
try expect(unpacked_struct_info.Struct.is_tuple == false);
|
try expect(unpacked_struct_info.Struct.is_tuple == false);
|
||||||
try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32));
|
try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32));
|
||||||
try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4);
|
try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4);
|
||||||
try expectEqualStrings("foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*);
|
try expect(mem.eql(u8, "foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*));
|
||||||
|
}
|
||||||
|
|
||||||
const struct_info = @typeInfo(TestStruct);
|
const TestStruct = struct {
|
||||||
|
fieldA: u32 = 4,
|
||||||
|
fieldB: *const [6:0]u8 = "foobar",
|
||||||
|
};
|
||||||
|
|
||||||
|
test "type info: packed struct info" {
|
||||||
|
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||||
|
|
||||||
|
try testPackedStruct();
|
||||||
|
comptime try testPackedStruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testPackedStruct() !void {
|
||||||
|
const struct_info = @typeInfo(TestPackedStruct);
|
||||||
try expect(struct_info == .Struct);
|
try expect(struct_info == .Struct);
|
||||||
try expect(struct_info.Struct.is_tuple == false);
|
try expect(struct_info.Struct.is_tuple == false);
|
||||||
try expect(struct_info.Struct.layout == .Packed);
|
try expect(struct_info.Struct.layout == .Packed);
|
||||||
try expect(struct_info.Struct.fields.len == 4);
|
try expect(struct_info.Struct.fields.len == 4);
|
||||||
try expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize));
|
try expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize));
|
||||||
try expect(struct_info.Struct.fields[2].field_type == *TestStruct);
|
try expect(struct_info.Struct.fields[2].field_type == *TestPackedStruct);
|
||||||
try expect(struct_info.Struct.fields[2].default_value == null);
|
try expect(struct_info.Struct.fields[2].default_value == null);
|
||||||
try expect(@ptrCast(*const u32, struct_info.Struct.fields[3].default_value.?).* == 4);
|
try expect(@ptrCast(*const u32, struct_info.Struct.fields[3].default_value.?).* == 4);
|
||||||
try expect(struct_info.Struct.fields[3].alignment == 1);
|
try expect(struct_info.Struct.fields[3].alignment == 1);
|
||||||
@ -276,12 +288,7 @@ fn testStruct() !void {
|
|||||||
try expect(struct_info.Struct.decls[0].is_pub);
|
try expect(struct_info.Struct.decls[0].is_pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestUnpackedStruct = struct {
|
const TestPackedStruct = packed struct {
|
||||||
fieldA: u32 = 4,
|
|
||||||
fieldB: *const [6:0]u8 = "foobar",
|
|
||||||
};
|
|
||||||
|
|
||||||
const TestStruct = packed struct {
|
|
||||||
fieldA: usize align(2 * @alignOf(usize)),
|
fieldA: usize align(2 * @alignOf(usize)),
|
||||||
fieldB: void,
|
fieldB: void,
|
||||||
fieldC: *Self,
|
fieldC: *Self,
|
||||||
@ -329,18 +336,16 @@ fn testFunction() !void {
|
|||||||
const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
|
const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
|
||||||
try expect(fn_aligned_info.Fn.alignment == 4);
|
try expect(fn_aligned_info.Fn.alignment == 4);
|
||||||
|
|
||||||
const test_instance: TestStruct = undefined;
|
const test_instance: TestPackedStruct = undefined;
|
||||||
const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo));
|
const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo));
|
||||||
try expect(bound_fn_info == .BoundFn);
|
try expect(bound_fn_info == .BoundFn);
|
||||||
try expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);
|
try expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestPackedStruct);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn foo(a: usize, b: bool, ...) callconv(.C) usize;
|
extern fn foo(a: usize, b: bool, ...) callconv(.C) usize;
|
||||||
extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize;
|
extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize;
|
||||||
|
|
||||||
test "typeInfo with comptime parameter in struct fn def" {
|
test "typeInfo with comptime parameter in struct fn def" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
pub fn func(comptime x: f32) void {
|
pub fn func(comptime x: f32) void {
|
||||||
_ = x;
|
_ = x;
|
||||||
@ -408,8 +413,6 @@ test "sentinel of opaque pointer type" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "@typeInfo does not force declarations into existence" {
|
test "@typeInfo does not force declarations into existence" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
x: i32,
|
x: i32,
|
||||||
|
|
||||||
@ -436,8 +439,6 @@ test "type info for async frames" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "Declarations are returned in declaration order" {
|
test "Declarations are returned in declaration order" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
const a = 1;
|
const a = 1;
|
||||||
const b = 2;
|
const b = 2;
|
||||||
@ -461,16 +462,12 @@ test "Struct.is_tuple" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "StructField.is_comptime" {
|
test "StructField.is_comptime" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
const info = @typeInfo(struct { x: u8 = 3, comptime y: u32 = 5 }).Struct;
|
const info = @typeInfo(struct { x: u8 = 3, comptime y: u32 = 5 }).Struct;
|
||||||
try expect(!info.fields[0].is_comptime);
|
try expect(!info.fields[0].is_comptime);
|
||||||
try expect(info.fields[1].is_comptime);
|
try expect(info.fields[1].is_comptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "typeInfo resolves usingnamespace declarations" {
|
test "typeInfo resolves usingnamespace declarations" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
const A = struct {
|
const A = struct {
|
||||||
pub const f1 = 42;
|
pub const f1 = 42;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user