diff --git a/src/Sema.zig b/src/Sema.zig index f939ebc912..a153be5119 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9958,7 +9958,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // layout: ContainerLayout, try Value.Tag.enum_field_index.create( sema.arena, - @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto), + @enumToInt(union_ty.containerLayout()), ), // 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", .{}), - .Opaque => { + .Struct => { // TODO: look into memoizing this result. var fields_anon_decl = try block.startAnonDecl(src); 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 decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace()); diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index fc5b268832..1aafd4f82b 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -249,26 +249,38 @@ fn testUnion() !void { } test "type info: struct info" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try testStruct(); comptime try testStruct(); } 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.fields[0].alignment == @alignOf(u32)); 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.is_tuple == false); try expect(struct_info.Struct.layout == .Packed); 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[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(@ptrCast(*const u32, struct_info.Struct.fields[3].default_value.?).* == 4); 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); } -const TestUnpackedStruct = struct { - fieldA: u32 = 4, - fieldB: *const [6:0]u8 = "foobar", -}; - -const TestStruct = packed struct { +const TestPackedStruct = packed struct { fieldA: usize align(2 * @alignOf(usize)), fieldB: void, fieldC: *Self, @@ -329,18 +336,16 @@ fn testFunction() !void { const fn_aligned_info = @typeInfo(@TypeOf(fooAligned)); 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)); 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 fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; test "typeInfo with comptime parameter in struct fn def" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const S = struct { pub fn func(comptime x: f32) void { _ = x; @@ -408,8 +413,6 @@ test "sentinel of opaque pointer type" { } test "@typeInfo does not force declarations into existence" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const S = struct { x: i32, @@ -436,8 +439,6 @@ test "type info for async frames" { } test "Declarations are returned in declaration order" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const S = struct { const a = 1; const b = 2; @@ -461,16 +462,12 @@ test "Struct.is_tuple" { } 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; try expect(!info.fields[0].is_comptime); try expect(info.fields[1].is_comptime); } test "typeInfo resolves usingnamespace declarations" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const A = struct { pub const f1 = 42; };