| author | |
| committer | |
| log | 9ea253b9c4085b9f577c4d7634da69b2d40d6cce |
| tree | 9c3b50e9ff50432b6d7b465e5e43803f4cc3c296 |
| parent | 23567cdd984322c7428d43c8cbf8ecd40a2b7ec6 |
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 |
| 9958 | 9958 | // layout: ContainerLayout, |
| 9959 | 9959 | try Value.Tag.enum_field_index.create( |
| 9960 | 9960 | sema.arena, |
| 9961 | @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto), | |
| 9961 | @enumToInt(union_ty.containerLayout()), | |
| 9962 | 9962 | ), |
| 9963 | 9963 | |
| 9964 | 9964 | // tag_type: ?type, |
| ... | ... | @@ -9977,13 +9977,112 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 9977 | 9977 | }), |
| 9978 | 9978 | ); |
| 9979 | 9979 | }, |
| 9980 | .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}), | |
| 9981 | .Opaque => { | |
| 9980 | .Struct => { | |
| 9982 | 9981 | // TODO: look into memoizing this result. |
| 9983 | 9982 | |
| 9984 | 9983 | var fields_anon_decl = try block.startAnonDecl(src); |
| 9985 | 9984 | defer fields_anon_decl.deinit(); |
| 9986 | 9985 | |
| 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 | ||
| 9987 | 10086 | const opaque_ty = try sema.resolveTypeFields(block, src, ty); |
| 9988 | 10087 | const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace()); |
| 9989 | 10088 |
test/behavior/type_info.zig+21-24| ... | ... | @@ -249,26 +249,38 @@ fn testUnion() !void { |
| 249 | 249 | } |
| 250 | 250 | |
| 251 | 251 | test "type info: struct info" { |
| 252 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 253 | ||
| 254 | 252 | try testStruct(); |
| 255 | 253 | comptime try testStruct(); |
| 256 | 254 | } |
| 257 | 255 | |
| 258 | 256 | fn testStruct() !void { |
| 259 | const unpacked_struct_info = @typeInfo(TestUnpackedStruct); | |
| 257 | const unpacked_struct_info = @typeInfo(TestStruct); | |
| 260 | 258 | try expect(unpacked_struct_info.Struct.is_tuple == false); |
| 261 | 259 | try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32)); |
| 262 | 260 | 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 | ||
| 264 | const TestStruct = struct { | |
| 265 | fieldA: u32 = 4, | |
| 266 | fieldB: *const [6:0]u8 = "foobar", | |
| 267 | }; | |
| 264 | 268 | |
| 265 | const struct_info = @typeInfo(TestStruct); | |
| 269 | test "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 | ||
| 276 | fn testPackedStruct() !void { | |
| 277 | const struct_info = @typeInfo(TestPackedStruct); | |
| 266 | 278 | try expect(struct_info == .Struct); |
| 267 | 279 | try expect(struct_info.Struct.is_tuple == false); |
| 268 | 280 | try expect(struct_info.Struct.layout == .Packed); |
| 269 | 281 | try expect(struct_info.Struct.fields.len == 4); |
| 270 | 282 | 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); | |
| 272 | 284 | try expect(struct_info.Struct.fields[2].default_value == null); |
| 273 | 285 | try expect(@ptrCast(*const u32, struct_info.Struct.fields[3].default_value.?).* == 4); |
| 274 | 286 | try expect(struct_info.Struct.fields[3].alignment == 1); |
| ... | ... | @@ -276,12 +288,7 @@ fn testStruct() !void { |
| 276 | 288 | try expect(struct_info.Struct.decls[0].is_pub); |
| 277 | 289 | } |
| 278 | 290 | |
| 279 | const TestUnpackedStruct = struct { | |
| 280 | fieldA: u32 = 4, | |
| 281 | fieldB: *const [6:0]u8 = "foobar", | |
| 282 | }; | |
| 283 | ||
| 284 | const TestStruct = packed struct { | |
| 291 | const TestPackedStruct = packed struct { | |
| 285 | 292 | fieldA: usize align(2 * @alignOf(usize)), |
| 286 | 293 | fieldB: void, |
| 287 | 294 | fieldC: *Self, |
| ... | ... | @@ -329,18 +336,16 @@ fn testFunction() !void { |
| 329 | 336 | const fn_aligned_info = @typeInfo(@TypeOf(fooAligned)); |
| 330 | 337 | try expect(fn_aligned_info.Fn.alignment == 4); |
| 331 | 338 | |
| 332 | const test_instance: TestStruct = undefined; | |
| 339 | const test_instance: TestPackedStruct = undefined; | |
| 333 | 340 | const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo)); |
| 334 | 341 | 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); | |
| 336 | 343 | } |
| 337 | 344 | |
| 338 | 345 | extern fn foo(a: usize, b: bool, ...) callconv(.C) usize; |
| 339 | 346 | extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; |
| 340 | 347 | |
| 341 | 348 | test "typeInfo with comptime parameter in struct fn def" { |
| 342 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 343 | ||
| 344 | 349 | const S = struct { |
| 345 | 350 | pub fn func(comptime x: f32) void { |
| 346 | 351 | _ = x; |
| ... | ... | @@ -408,8 +413,6 @@ test "sentinel of opaque pointer type" { |
| 408 | 413 | } |
| 409 | 414 | |
| 410 | 415 | test "@typeInfo does not force declarations into existence" { |
| 411 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 412 | ||
| 413 | 416 | const S = struct { |
| 414 | 417 | x: i32, |
| 415 | 418 | |
| ... | ... | @@ -436,8 +439,6 @@ test "type info for async frames" { |
| 436 | 439 | } |
| 437 | 440 | |
| 438 | 441 | test "Declarations are returned in declaration order" { |
| 439 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 440 | ||
| 441 | 442 | const S = struct { |
| 442 | 443 | const a = 1; |
| 443 | 444 | const b = 2; |
| ... | ... | @@ -461,16 +462,12 @@ test "Struct.is_tuple" { |
| 461 | 462 | } |
| 462 | 463 | |
| 463 | 464 | test "StructField.is_comptime" { |
| 464 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 465 | ||
| 466 | 465 | const info = @typeInfo(struct { x: u8 = 3, comptime y: u32 = 5 }).Struct; |
| 467 | 466 | try expect(!info.fields[0].is_comptime); |
| 468 | 467 | try expect(info.fields[1].is_comptime); |
| 469 | 468 | } |
| 470 | 469 | |
| 471 | 470 | test "typeInfo resolves usingnamespace declarations" { |
| 472 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 473 | ||
| 474 | 471 | const A = struct { |
| 475 | 472 | pub const f1 = 42; |
| 476 | 473 | }; |