authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-17 20:47:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-17 20:48:09-07:00
log9ea253b9c4085b9f577c4d7634da69b2d40d6cce
tree9c3b50e9ff50432b6d7b465e5e43803f4cc3c296
parent23567cdd984322c7428d43c8cbf8ecd40a2b7ec6

Sema: implement type info for structs


2 files changed, 123 insertions(+), 27 deletions(-)

src/Sema.zig+102-3
......@@ -9958,7 +9958,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
99589958 // layout: ContainerLayout,
99599959 try Value.Tag.enum_field_index.create(
99609960 sema.arena,
9961 @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto),
9961 @enumToInt(union_ty.containerLayout()),
99629962 ),
99639963
99649964 // tag_type: ?type,
......@@ -9977,13 +9977,112 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
99779977 }),
99789978 );
99799979 },
9980 .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}),
9981 .Opaque => {
9980 .Struct => {
99829981 // TODO: look into memoizing this result.
99839982
99849983 var fields_anon_decl = try block.startAnonDecl(src);
99859984 defer fields_anon_decl.deinit();
99869985
9986 const struct_field_ty = t: {
9987 const struct_field_ty_decl = (try sema.namespaceLookup(
9988 block,
9989 src,
9990 type_info_ty.getNamespace().?,
9991 "StructField",
9992 )).?;
9993 try sema.mod.declareDeclDependency(sema.owner_decl, struct_field_ty_decl);
9994 try sema.ensureDeclAnalyzed(struct_field_ty_decl);
9995 var buffer: Value.ToTypeBuffer = undefined;
9996 break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
9997 };
9998
9999 const struct_ty = try sema.resolveTypeFields(block, src, ty);
10000 const struct_fields = struct_ty.structFields();
10001 const struct_field_vals = try fields_anon_decl.arena().alloc(Value, struct_fields.count());
10002 const layout = struct_ty.containerLayout();
10003
10004 for (struct_field_vals) |*field_val, i| {
10005 const field = struct_fields.values()[i];
10006 const name = struct_fields.keys()[i];
10007 const name_val = v: {
10008 var anon_decl = try block.startAnonDecl(src);
10009 defer anon_decl.deinit();
10010 const bytes = try anon_decl.arena().dupeZ(u8, name);
10011 const new_decl = try anon_decl.finish(
10012 try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
10013 try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
10014 );
10015 break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl);
10016 };
10017
10018 const struct_field_fields = try fields_anon_decl.arena().create([5]Value);
10019 const opt_default_val = if (field.default_val.tag() == .unreachable_value)
10020 null
10021 else
10022 field.default_val;
10023 const default_val_ptr = try sema.optRefValue(block, src, field.ty, opt_default_val);
10024 const alignment = switch (layout) {
10025 .Auto, .Extern => field.normalAlignment(target),
10026 .Packed => field.packedAlignment(),
10027 };
10028
10029 struct_field_fields.* = .{
10030 // name: []const u8,
10031 name_val,
10032 // field_type: type,
10033 try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty),
10034 // default_value: ?*const anyopaque,
10035 try default_val_ptr.copy(fields_anon_decl.arena()),
10036 // is_comptime: bool,
10037 Value.makeBool(field.is_comptime),
10038 // alignment: comptime_int,
10039 try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
10040 };
10041 field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), struct_field_fields);
10042 }
10043
10044 const fields_val = v: {
10045 const new_decl = try fields_anon_decl.finish(
10046 try Type.Tag.array.create(fields_anon_decl.arena(), .{
10047 .len = struct_field_vals.len,
10048 .elem_type = struct_field_ty,
10049 }),
10050 try Value.Tag.array.create(
10051 fields_anon_decl.arena(),
10052 try fields_anon_decl.arena().dupe(Value, struct_field_vals),
10053 ),
10054 );
10055 break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
10056 };
10057
10058 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace());
10059
10060 const field_values = try sema.arena.create([4]Value);
10061 field_values.* = .{
10062 // layout: ContainerLayout,
10063 try Value.Tag.enum_field_index.create(
10064 sema.arena,
10065 @enumToInt(layout),
10066 ),
10067 // fields: []const StructField,
10068 fields_val,
10069 // decls: []const Declaration,
10070 decls_val,
10071 // is_tuple: bool,
10072 Value.makeBool(struct_ty.isTuple()),
10073 };
10074
10075 return sema.addConstant(
10076 type_info_ty,
10077 try Value.Tag.@"union".create(sema.arena, .{
10078 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Struct)),
10079 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
10080 }),
10081 );
10082 },
10083 .Opaque => {
10084 // TODO: look into memoizing this result.
10085
998710086 const opaque_ty = try sema.resolveTypeFields(block, src, ty);
998810087 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace());
998910088
test/behavior/type_info.zig+21-24
......@@ -249,26 +249,38 @@ fn testUnion() !void {
249249}
250250
251251test "type info: struct info" {
252 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
253
254252 try testStruct();
255253 comptime try testStruct();
256254}
257255
258256fn testStruct() !void {
259 const unpacked_struct_info = @typeInfo(TestUnpackedStruct);
257 const unpacked_struct_info = @typeInfo(TestStruct);
260258 try expect(unpacked_struct_info.Struct.is_tuple == false);
261259 try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32));
262260 try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4);
263 try expectEqualStrings("foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*);
261 try expect(mem.eql(u8, "foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*));
262}
263
264const TestStruct = struct {
265 fieldA: u32 = 4,
266 fieldB: *const [6:0]u8 = "foobar",
267};
264268
265 const struct_info = @typeInfo(TestStruct);
269test "type info: packed struct info" {
270 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
271
272 try testPackedStruct();
273 comptime try testPackedStruct();
274}
275
276fn testPackedStruct() !void {
277 const struct_info = @typeInfo(TestPackedStruct);
266278 try expect(struct_info == .Struct);
267279 try expect(struct_info.Struct.is_tuple == false);
268280 try expect(struct_info.Struct.layout == .Packed);
269281 try expect(struct_info.Struct.fields.len == 4);
270282 try expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize));
271 try expect(struct_info.Struct.fields[2].field_type == *TestStruct);
283 try expect(struct_info.Struct.fields[2].field_type == *TestPackedStruct);
272284 try expect(struct_info.Struct.fields[2].default_value == null);
273285 try expect(@ptrCast(*const u32, struct_info.Struct.fields[3].default_value.?).* == 4);
274286 try expect(struct_info.Struct.fields[3].alignment == 1);
......@@ -276,12 +288,7 @@ fn testStruct() !void {
276288 try expect(struct_info.Struct.decls[0].is_pub);
277289}
278290
279const TestUnpackedStruct = struct {
280 fieldA: u32 = 4,
281 fieldB: *const [6:0]u8 = "foobar",
282};
283
284const TestStruct = packed struct {
291const TestPackedStruct = packed struct {
285292 fieldA: usize align(2 * @alignOf(usize)),
286293 fieldB: void,
287294 fieldC: *Self,
......@@ -329,18 +336,16 @@ fn testFunction() !void {
329336 const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
330337 try expect(fn_aligned_info.Fn.alignment == 4);
331338
332 const test_instance: TestStruct = undefined;
339 const test_instance: TestPackedStruct = undefined;
333340 const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo));
334341 try expect(bound_fn_info == .BoundFn);
335 try expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);
342 try expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestPackedStruct);
336343}
337344
338345extern fn foo(a: usize, b: bool, ...) callconv(.C) usize;
339346extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize;
340347
341348test "typeInfo with comptime parameter in struct fn def" {
342 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
343
344349 const S = struct {
345350 pub fn func(comptime x: f32) void {
346351 _ = x;
......@@ -408,8 +413,6 @@ test "sentinel of opaque pointer type" {
408413}
409414
410415test "@typeInfo does not force declarations into existence" {
411 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
412
413416 const S = struct {
414417 x: i32,
415418
......@@ -436,8 +439,6 @@ test "type info for async frames" {
436439}
437440
438441test "Declarations are returned in declaration order" {
439 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
440
441442 const S = struct {
442443 const a = 1;
443444 const b = 2;
......@@ -461,16 +462,12 @@ test "Struct.is_tuple" {
461462}
462463
463464test "StructField.is_comptime" {
464 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
465
466465 const info = @typeInfo(struct { x: u8 = 3, comptime y: u32 = 5 }).Struct;
467466 try expect(!info.fields[0].is_comptime);
468467 try expect(info.fields[1].is_comptime);
469468}
470469
471470test "typeInfo resolves usingnamespace declarations" {
472 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
473
474471 const A = struct {
475472 pub const f1 = 42;
476473 };