Sema: implement declarations for @typeInfo

In the behavior test listings, I had to move type_info.zig test import
to a section that did not include the x86 backend because it got to the
point where adding another test to the file, even if it was an empty
test that just returned immediately, caused a runtime failure when
executing the test binary.

Anyway, type info for opaques is implemented, and the declarations slice
is shared between it, enums, and unions.

Still TODO is the `data` field of a `Declaration`. I want to consider
removing it from the data returned from `@typeInfo` and introducing
`@declInfo` or similar for this data. This would avoid the complexity of
a lazy mechanism.
This commit is contained in:
Andrew Kelley 2022-01-18 18:10:15 -07:00
parent cd594d10bd
commit 8375b71f75
5 changed files with 591 additions and 457 deletions

View File

@ -9677,12 +9677,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
};
if (ty.getNamespace()) |namespace| {
if (namespace.decls.count() != 0) {
return sema.fail(block, src, "TODO: implement zirTypeInfo for Enum which has declarations", .{});
}
}
const decls_val = Value.initTag(.empty_array);
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespace());
const field_values = try sema.arena.create([5]Value);
field_values.* = .{
@ -9773,12 +9768,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
};
if (ty.getNamespace()) |namespace| {
if (namespace.decls.count() != 0) {
return sema.fail(block, src, "TODO: implement zirTypeInfo for Union which has declarations", .{});
}
}
const decls_val = Value.initTag(.empty_array);
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespace());
const enum_tag_ty_val = if (union_ty.unionTagType()) |tag_ty| v: {
const ty_val = try Value.Tag.ty.create(sema.arena, tag_ty);
@ -9810,14 +9800,105 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
);
},
.Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}),
.Opaque => {
// TODO: look into memoizing this result.
var fields_anon_decl = try block.startAnonDecl();
defer fields_anon_decl.deinit();
const opaque_ty = try sema.resolveTypeFields(block, src, ty);
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace());
const field_values = try sema.arena.create([1]Value);
field_values.* = .{
// decls: []const Declaration,
decls_val,
};
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.Opaque)),
.val = try Value.Tag.@"struct".create(sema.arena, field_values),
}),
);
},
.ErrorSet => return sema.fail(block, src, "TODO: implement zirTypeInfo for ErrorSet", .{}),
.BoundFn => @panic("TODO remove this type from the language and compiler"),
.Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}),
.Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}),
.AnyFrame => return sema.fail(block, src, "TODO: implement zirTypeInfo for AnyFrame", .{}),
}
}
fn typeInfoDecls(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
type_info_ty: Type,
opt_namespace: ?*Module.Namespace,
) CompileError!Value {
const namespace = opt_namespace orelse return Value.initTag(.empty_array);
const decls_len = namespace.decls.count();
if (decls_len == 0) return Value.initTag(.empty_array);
var decls_anon_decl = try block.startAnonDecl();
defer decls_anon_decl.deinit();
const declaration_ty = t: {
const declaration_ty_decl = (try sema.namespaceLookup(
block,
src,
type_info_ty.getNamespace().?,
"EnumField",
)).?;
try sema.mod.declareDeclDependency(sema.owner_decl, declaration_ty_decl);
try sema.ensureDeclAnalyzed(declaration_ty_decl);
var buffer: Value.ToTypeBuffer = undefined;
break :t try declaration_ty_decl.val.toType(&buffer).copy(decls_anon_decl.arena());
};
const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len);
for (decls_vals) |*decls_val, i| {
const decl = namespace.decls.values()[i];
const name = namespace.decls.keys()[i];
const name_val = v: {
var anon_decl = try block.startAnonDecl();
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(decls_anon_decl.arena(), new_decl);
};
const is_pub = if (decl.is_pub) Value.@"true" else Value.@"false";
const fields = try decls_anon_decl.arena().create([3]Value);
fields.* = .{
//name: []const u8,
name_val,
//is_pub: bool,
is_pub,
//data: Data,
Value.undef, // TODO
};
decls_val.* = try Value.Tag.@"struct".create(decls_anon_decl.arena(), 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.array.create(
decls_anon_decl.arena(),
try decls_anon_decl.arena().dupe(Value, decls_vals),
),
);
return try Value.Tag.decl_ref.create(sema.arena, new_decl);
}
fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
_ = block;
const zir_datas = sema.code.instructions.items(.data);

View File

@ -768,7 +768,12 @@ pub const Value = extern union {
return allocator.dupe(u8, adjusted_bytes);
},
.enum_literal => return allocator.dupe(u8, val.castTag(.enum_literal).?.data),
.repeated => @panic("TODO implement toAllocatedBytes for this Value tag"),
.repeated => {
const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt());
const result = try allocator.alloc(u8, @intCast(usize, ty.arrayLen()));
std.mem.set(u8, result, byte);
return result;
},
.decl_ref => {
const decl = val.castTag(.decl_ref).?.data;
const decl_val = try decl.value();
@ -776,7 +781,15 @@ pub const Value = extern union {
},
.the_only_possible_value => return &[_]u8{},
.slice => return toAllocatedBytes(val.castTag(.slice).?.data.ptr, ty, allocator),
else => unreachable,
else => {
const result = try allocator.alloc(u8, @intCast(usize, ty.arrayLen()));
var elem_value_buf: ElemValueBuffer = undefined;
for (result) |*elem, i| {
const elem_val = val.elemValueBuffer(i, &elem_value_buf);
elem.* = @intCast(u8, elem_val.toUnsignedInt());
}
return result;
},
}
}

View File

@ -11,7 +11,6 @@ test {
_ = @import("behavior/hasdecl.zig");
_ = @import("behavior/hasfield.zig");
_ = @import("behavior/pub_enum.zig");
_ = @import("behavior/type_info.zig");
_ = @import("behavior/type.zig");
_ = @import("behavior/bugs/655.zig");
_ = @import("behavior/bool.zig");
@ -56,6 +55,7 @@ test {
_ = @import("behavior/this.zig");
_ = @import("behavior/truncate.zig");
_ = @import("behavior/try.zig");
_ = @import("behavior/type_info.zig");
_ = @import("behavior/undefined.zig");
_ = @import("behavior/underscore.zig");
_ = @import("behavior/usingnamespace.zig");
@ -194,7 +194,6 @@ test {
_ = @import("behavior/truncate_stage1.zig");
_ = @import("behavior/tuple.zig");
_ = @import("behavior/type_stage1.zig");
_ = @import("behavior/type_info_stage1.zig");
_ = @import("behavior/typename.zig");
_ = @import("behavior/union_stage1.zig");
_ = @import("behavior/union_with_members.zig");

View File

@ -57,3 +57,484 @@ test "type info: value is correctly copied" {
try expect(@typeInfo([]u32).Pointer.size == .Slice);
}
}
test "type info: tag type, void info" {
try testBasic();
comptime try testBasic();
}
fn testBasic() !void {
try expect(@typeInfo(TypeInfo).Union.tag_type == TypeId);
const void_info = @typeInfo(void);
try expect(void_info == TypeId.Void);
try expect(void_info.Void == {});
}
test "type info: pointer type info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testPointer();
comptime try testPointer();
}
fn testPointer() !void {
const u32_ptr_info = @typeInfo(*u32);
try expect(u32_ptr_info == .Pointer);
try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One);
try expect(u32_ptr_info.Pointer.is_const == false);
try expect(u32_ptr_info.Pointer.is_volatile == false);
try expect(u32_ptr_info.Pointer.alignment == @alignOf(u32));
try expect(u32_ptr_info.Pointer.child == u32);
try expect(u32_ptr_info.Pointer.sentinel == null);
}
test "type info: unknown length pointer type info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testUnknownLenPtr();
comptime try testUnknownLenPtr();
}
fn testUnknownLenPtr() !void {
const u32_ptr_info = @typeInfo([*]const volatile f64);
try expect(u32_ptr_info == .Pointer);
try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
try expect(u32_ptr_info.Pointer.is_const == true);
try expect(u32_ptr_info.Pointer.is_volatile == true);
try expect(u32_ptr_info.Pointer.sentinel == null);
try expect(u32_ptr_info.Pointer.alignment == @alignOf(f64));
try expect(u32_ptr_info.Pointer.child == f64);
}
test "type info: null terminated pointer type info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testNullTerminatedPtr();
comptime try testNullTerminatedPtr();
}
fn testNullTerminatedPtr() !void {
const ptr_info = @typeInfo([*:0]u8);
try expect(ptr_info == .Pointer);
try expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
try expect(ptr_info.Pointer.is_const == false);
try expect(ptr_info.Pointer.is_volatile == false);
try expect(ptr_info.Pointer.sentinel.? == 0);
try expect(@typeInfo([:0]u8).Pointer.sentinel != null);
}
test "type info: slice type info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testSlice();
comptime try testSlice();
}
fn testSlice() !void {
const u32_slice_info = @typeInfo([]u32);
try expect(u32_slice_info == .Pointer);
try expect(u32_slice_info.Pointer.size == .Slice);
try expect(u32_slice_info.Pointer.is_const == false);
try expect(u32_slice_info.Pointer.is_volatile == false);
try expect(u32_slice_info.Pointer.alignment == 4);
try expect(u32_slice_info.Pointer.child == u32);
}
test "type info: array type info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testArray();
comptime try testArray();
}
fn testArray() !void {
{
const info = @typeInfo([42]u8);
try expect(info == .Array);
try expect(info.Array.len == 42);
try expect(info.Array.child == u8);
try expect(info.Array.sentinel == null);
}
{
const info = @typeInfo([10:0]u8);
try expect(info.Array.len == 10);
try expect(info.Array.child == u8);
try expect(info.Array.sentinel.? == @as(u8, 0));
try expect(@sizeOf([10:0]u8) == info.Array.len + 1);
}
}
test "type info: error set, error union info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testErrorSet();
comptime try testErrorSet();
}
fn testErrorSet() !void {
const TestErrorSet = error{
First,
Second,
Third,
};
const error_set_info = @typeInfo(TestErrorSet);
try expect(error_set_info == .ErrorSet);
try expect(error_set_info.ErrorSet.?.len == 3);
try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First"));
const error_union_info = @typeInfo(TestErrorSet!usize);
try expect(error_union_info == .ErrorUnion);
try expect(error_union_info.ErrorUnion.error_set == TestErrorSet);
try expect(error_union_info.ErrorUnion.payload == usize);
const global_info = @typeInfo(anyerror);
try expect(global_info == .ErrorSet);
try expect(global_info.ErrorSet == null);
}
test "type info: enum info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testEnum();
comptime try testEnum();
}
fn testEnum() !void {
const Os = enum {
Windows,
Macos,
Linux,
FreeBSD,
};
const os_info = @typeInfo(Os);
try expect(os_info == .Enum);
try expect(os_info.Enum.layout == .Auto);
try expect(os_info.Enum.fields.len == 4);
try expect(mem.eql(u8, os_info.Enum.fields[1].name, "Macos"));
try expect(os_info.Enum.fields[3].value == 3);
try expect(os_info.Enum.tag_type == u2);
try expect(os_info.Enum.decls.len == 0);
}
test "type info: union info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testUnion();
comptime try testUnion();
}
fn testUnion() !void {
const typeinfo_info = @typeInfo(TypeInfo);
try expect(typeinfo_info == .Union);
try expect(typeinfo_info.Union.layout == .Auto);
try expect(typeinfo_info.Union.tag_type.? == TypeId);
try expect(typeinfo_info.Union.fields.len == 25);
try expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
try expect(typeinfo_info.Union.decls.len == 22);
const TestNoTagUnion = union {
Foo: void,
Bar: u32,
};
const notag_union_info = @typeInfo(TestNoTagUnion);
try expect(notag_union_info == .Union);
try expect(notag_union_info.Union.tag_type == null);
try expect(notag_union_info.Union.layout == .Auto);
try expect(notag_union_info.Union.fields.len == 2);
try expect(notag_union_info.Union.fields[0].alignment == @alignOf(void));
try expect(notag_union_info.Union.fields[1].field_type == u32);
try expect(notag_union_info.Union.fields[1].alignment == @alignOf(u32));
const TestExternUnion = extern union {
foo: *anyopaque,
};
const extern_union_info = @typeInfo(TestExternUnion);
try expect(extern_union_info.Union.layout == .Extern);
try expect(extern_union_info.Union.tag_type == null);
try expect(extern_union_info.Union.fields[0].field_type == *anyopaque);
}
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);
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].default_value.? == 4);
try expectEqualStrings("foobar", unpacked_struct_info.Struct.fields[1].default_value.?);
const struct_info = @typeInfo(TestStruct);
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].default_value == null);
try expect(struct_info.Struct.fields[3].default_value.? == 4);
try expect(struct_info.Struct.fields[3].alignment == 1);
try expect(struct_info.Struct.decls.len == 2);
try expect(struct_info.Struct.decls[0].is_pub);
try expect(!struct_info.Struct.decls[0].data.Fn.is_extern);
try expect(struct_info.Struct.decls[0].data.Fn.lib_name == null);
try expect(struct_info.Struct.decls[0].data.Fn.return_type == void);
try expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void);
}
const TestUnpackedStruct = struct {
fieldA: u32 = 4,
fieldB: *const [6:0]u8 = "foobar",
};
const TestStruct = packed struct {
fieldA: usize align(2 * @alignOf(usize)),
fieldB: void,
fieldC: *Self,
fieldD: u32 = 4,
pub fn foo(self: *const Self) void {
_ = self;
}
const Self = @This();
};
test "type info: opaque info" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
try testOpaque();
comptime try testOpaque();
}
fn testOpaque() !void {
const Foo = opaque {
const A = 1;
fn b() void {}
};
const foo_info = @typeInfo(Foo);
try expect(foo_info.Opaque.decls.len == 2);
}
test "type info: function type info" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// wasm doesn't support align attributes on functions
if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest;
try testFunction();
comptime try testFunction();
}
fn testFunction() !void {
const fn_info = @typeInfo(@TypeOf(foo));
try expect(fn_info == .Fn);
try expect(fn_info.Fn.alignment > 0);
try expect(fn_info.Fn.calling_convention == .C);
try expect(!fn_info.Fn.is_generic);
try expect(fn_info.Fn.args.len == 2);
try expect(fn_info.Fn.is_var_args);
try expect(fn_info.Fn.return_type.? == usize);
const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
try expect(fn_aligned_info.Fn.alignment == 4);
const test_instance: TestStruct = 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);
}
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;
}
};
comptime var info = @typeInfo(S);
_ = info;
}
test "type info: vectors" {
try testVector();
comptime try testVector();
}
fn testVector() !void {
const vec_info = @typeInfo(std.meta.Vector(4, i32));
try expect(vec_info == .Vector);
try expect(vec_info.Vector.len == 4);
try expect(vec_info.Vector.child == i32);
}
test "type info: anyframe and anyframe->T" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testAnyFrame();
comptime try testAnyFrame();
}
fn testAnyFrame() !void {
{
const anyframe_info = @typeInfo(anyframe->i32);
try expect(anyframe_info == .AnyFrame);
try expect(anyframe_info.AnyFrame.child.? == i32);
}
{
const anyframe_info = @typeInfo(anyframe);
try expect(anyframe_info == .AnyFrame);
try expect(anyframe_info.AnyFrame.child == null);
}
}
test "type info: pass to function" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
_ = passTypeInfo(@typeInfo(void));
_ = comptime passTypeInfo(@typeInfo(void));
}
fn passTypeInfo(comptime info: TypeInfo) type {
_ = info;
return void;
}
test "type info: TypeId -> TypeInfo impl cast" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
_ = passTypeInfo(TypeId.Void);
_ = comptime passTypeInfo(TypeId.Void);
}
test "type info: extern fns with and without lib names" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
extern fn bar1() void;
extern "cool" fn bar2() void;
};
const info = @typeInfo(S);
comptime {
for (info.Struct.decls) |decl| {
if (std.mem.eql(u8, decl.name, "bar1")) {
try expect(decl.data.Fn.lib_name == null);
} else {
try expectEqualStrings("cool", decl.data.Fn.lib_name.?);
}
}
}
}
test "data field is a compile-time value" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
const Bar = @as(isize, -1);
};
comptime try expect(@typeInfo(S).Struct.decls[0].data.Var == isize);
}
test "sentinel of opaque pointer type" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const c_void_info = @typeInfo(*anyopaque);
try expect(c_void_info.Pointer.sentinel == null);
}
test "@typeInfo does not force declarations into existence" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
x: i32,
fn doNotReferenceMe() void {
@compileError("test failed");
}
};
comptime try expect(@typeInfo(S).Struct.fields.len == 1);
}
test "default value for a anytype field" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct { x: anytype };
try expect(@typeInfo(S).Struct.fields[0].default_value == null);
}
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "type info for async frames" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
switch (@typeInfo(@Frame(add))) {
.Frame => |frame| {
try expect(frame.function == add);
},
else => unreachable,
}
}
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;
const c = 3;
const d = 4;
const e = 5;
};
const d = @typeInfo(S).Struct.decls;
try expect(std.mem.eql(u8, d[0].name, "a"));
try expect(std.mem.eql(u8, d[1].name, "b"));
try expect(std.mem.eql(u8, d[2].name, "c"));
try expect(std.mem.eql(u8, d[3].name, "d"));
try expect(std.mem.eql(u8, d[4].name, "e"));
}
test "Struct.is_tuple" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try expect(@typeInfo(@TypeOf(.{0})).Struct.is_tuple);
try expect(!@typeInfo(@TypeOf(.{ .a = 0 })).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;
};
const B = struct {
const f0 = 42;
usingnamespace A;
};
try expect(@typeInfo(B).Struct.decls.len == 2);
//a
}

View File

@ -1,440 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
const mem = std.mem;
const TypeInfo = std.builtin.TypeInfo;
const TypeId = std.builtin.TypeId;
const expect = std.testing.expect;
const expectEqualStrings = std.testing.expectEqualStrings;
test "type info: tag type, void info" {
try testBasic();
comptime try testBasic();
}
fn testBasic() !void {
try expect(@typeInfo(TypeInfo).Union.tag_type == TypeId);
const void_info = @typeInfo(void);
try expect(void_info == TypeId.Void);
try expect(void_info.Void == {});
}
test "type info: pointer type info" {
try testPointer();
comptime try testPointer();
}
fn testPointer() !void {
const u32_ptr_info = @typeInfo(*u32);
try expect(u32_ptr_info == .Pointer);
try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One);
try expect(u32_ptr_info.Pointer.is_const == false);
try expect(u32_ptr_info.Pointer.is_volatile == false);
try expect(u32_ptr_info.Pointer.alignment == @alignOf(u32));
try expect(u32_ptr_info.Pointer.child == u32);
try expect(u32_ptr_info.Pointer.sentinel == null);
}
test "type info: unknown length pointer type info" {
try testUnknownLenPtr();
comptime try testUnknownLenPtr();
}
fn testUnknownLenPtr() !void {
const u32_ptr_info = @typeInfo([*]const volatile f64);
try expect(u32_ptr_info == .Pointer);
try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
try expect(u32_ptr_info.Pointer.is_const == true);
try expect(u32_ptr_info.Pointer.is_volatile == true);
try expect(u32_ptr_info.Pointer.sentinel == null);
try expect(u32_ptr_info.Pointer.alignment == @alignOf(f64));
try expect(u32_ptr_info.Pointer.child == f64);
}
test "type info: null terminated pointer type info" {
try testNullTerminatedPtr();
comptime try testNullTerminatedPtr();
}
fn testNullTerminatedPtr() !void {
const ptr_info = @typeInfo([*:0]u8);
try expect(ptr_info == .Pointer);
try expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
try expect(ptr_info.Pointer.is_const == false);
try expect(ptr_info.Pointer.is_volatile == false);
try expect(ptr_info.Pointer.sentinel.? == 0);
try expect(@typeInfo([:0]u8).Pointer.sentinel != null);
}
test "type info: slice type info" {
try testSlice();
comptime try testSlice();
}
fn testSlice() !void {
const u32_slice_info = @typeInfo([]u32);
try expect(u32_slice_info == .Pointer);
try expect(u32_slice_info.Pointer.size == .Slice);
try expect(u32_slice_info.Pointer.is_const == false);
try expect(u32_slice_info.Pointer.is_volatile == false);
try expect(u32_slice_info.Pointer.alignment == 4);
try expect(u32_slice_info.Pointer.child == u32);
}
test "type info: array type info" {
try testArray();
comptime try testArray();
}
fn testArray() !void {
{
const info = @typeInfo([42]u8);
try expect(info == .Array);
try expect(info.Array.len == 42);
try expect(info.Array.child == u8);
try expect(info.Array.sentinel == null);
}
{
const info = @typeInfo([10:0]u8);
try expect(info.Array.len == 10);
try expect(info.Array.child == u8);
try expect(info.Array.sentinel.? == @as(u8, 0));
try expect(@sizeOf([10:0]u8) == info.Array.len + 1);
}
}
test "type info: error set, error union info" {
try testErrorSet();
comptime try testErrorSet();
}
fn testErrorSet() !void {
const TestErrorSet = error{
First,
Second,
Third,
};
const error_set_info = @typeInfo(TestErrorSet);
try expect(error_set_info == .ErrorSet);
try expect(error_set_info.ErrorSet.?.len == 3);
try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First"));
const error_union_info = @typeInfo(TestErrorSet!usize);
try expect(error_union_info == .ErrorUnion);
try expect(error_union_info.ErrorUnion.error_set == TestErrorSet);
try expect(error_union_info.ErrorUnion.payload == usize);
const global_info = @typeInfo(anyerror);
try expect(global_info == .ErrorSet);
try expect(global_info.ErrorSet == null);
}
test "type info: enum info" {
try testEnum();
comptime try testEnum();
}
fn testEnum() !void {
const Os = enum {
Windows,
Macos,
Linux,
FreeBSD,
};
const os_info = @typeInfo(Os);
try expect(os_info == .Enum);
try expect(os_info.Enum.layout == .Auto);
try expect(os_info.Enum.fields.len == 4);
try expect(mem.eql(u8, os_info.Enum.fields[1].name, "Macos"));
try expect(os_info.Enum.fields[3].value == 3);
try expect(os_info.Enum.tag_type == u2);
try expect(os_info.Enum.decls.len == 0);
}
test "type info: union info" {
try testUnion();
comptime try testUnion();
}
fn testUnion() !void {
const typeinfo_info = @typeInfo(TypeInfo);
try expect(typeinfo_info == .Union);
try expect(typeinfo_info.Union.layout == .Auto);
try expect(typeinfo_info.Union.tag_type.? == TypeId);
try expect(typeinfo_info.Union.fields.len == 25);
try expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
try expect(typeinfo_info.Union.decls.len == 22);
const TestNoTagUnion = union {
Foo: void,
Bar: u32,
};
const notag_union_info = @typeInfo(TestNoTagUnion);
try expect(notag_union_info == .Union);
try expect(notag_union_info.Union.tag_type == null);
try expect(notag_union_info.Union.layout == .Auto);
try expect(notag_union_info.Union.fields.len == 2);
try expect(notag_union_info.Union.fields[0].alignment == @alignOf(void));
try expect(notag_union_info.Union.fields[1].field_type == u32);
try expect(notag_union_info.Union.fields[1].alignment == @alignOf(u32));
const TestExternUnion = extern union {
foo: *anyopaque,
};
const extern_union_info = @typeInfo(TestExternUnion);
try expect(extern_union_info.Union.layout == .Extern);
try expect(extern_union_info.Union.tag_type == null);
try expect(extern_union_info.Union.fields[0].field_type == *anyopaque);
}
test "type info: struct info" {
try testStruct();
comptime try testStruct();
}
fn testStruct() !void {
const unpacked_struct_info = @typeInfo(TestUnpackedStruct);
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].default_value.? == 4);
try expectEqualStrings("foobar", unpacked_struct_info.Struct.fields[1].default_value.?);
const struct_info = @typeInfo(TestStruct);
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].default_value == null);
try expect(struct_info.Struct.fields[3].default_value.? == 4);
try expect(struct_info.Struct.fields[3].alignment == 1);
try expect(struct_info.Struct.decls.len == 2);
try expect(struct_info.Struct.decls[0].is_pub);
try expect(!struct_info.Struct.decls[0].data.Fn.is_extern);
try expect(struct_info.Struct.decls[0].data.Fn.lib_name == null);
try expect(struct_info.Struct.decls[0].data.Fn.return_type == void);
try expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void);
}
const TestUnpackedStruct = struct {
fieldA: u32 = 4,
fieldB: *const [6:0]u8 = "foobar",
};
const TestStruct = packed struct {
fieldA: usize align(2 * @alignOf(usize)),
fieldB: void,
fieldC: *Self,
fieldD: u32 = 4,
pub fn foo(self: *const Self) void {
_ = self;
}
const Self = @This();
};
test "type info: opaque info" {
try testOpaque();
comptime try testOpaque();
}
fn testOpaque() !void {
const Foo = opaque {
const A = 1;
fn b() void {}
};
const foo_info = @typeInfo(Foo);
try expect(foo_info.Opaque.decls.len == 2);
}
test "type info: function type info" {
// wasm doesn't support align attributes on functions
if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest;
try testFunction();
comptime try testFunction();
}
fn testFunction() !void {
const fn_info = @typeInfo(@TypeOf(foo));
try expect(fn_info == .Fn);
try expect(fn_info.Fn.alignment > 0);
try expect(fn_info.Fn.calling_convention == .C);
try expect(!fn_info.Fn.is_generic);
try expect(fn_info.Fn.args.len == 2);
try expect(fn_info.Fn.is_var_args);
try expect(fn_info.Fn.return_type.? == usize);
const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
try expect(fn_aligned_info.Fn.alignment == 4);
const test_instance: TestStruct = 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);
}
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" {
const S = struct {
pub fn func(comptime x: f32) void {
_ = x;
}
};
comptime var info = @typeInfo(S);
_ = info;
}
test "type info: vectors" {
try testVector();
comptime try testVector();
}
fn testVector() !void {
const vec_info = @typeInfo(std.meta.Vector(4, i32));
try expect(vec_info == .Vector);
try expect(vec_info.Vector.len == 4);
try expect(vec_info.Vector.child == i32);
}
test "type info: anyframe and anyframe->T" {
try testAnyFrame();
comptime try testAnyFrame();
}
fn testAnyFrame() !void {
{
const anyframe_info = @typeInfo(anyframe->i32);
try expect(anyframe_info == .AnyFrame);
try expect(anyframe_info.AnyFrame.child.? == i32);
}
{
const anyframe_info = @typeInfo(anyframe);
try expect(anyframe_info == .AnyFrame);
try expect(anyframe_info.AnyFrame.child == null);
}
}
test "type info: pass to function" {
_ = passTypeInfo(@typeInfo(void));
_ = comptime passTypeInfo(@typeInfo(void));
}
fn passTypeInfo(comptime info: TypeInfo) type {
_ = info;
return void;
}
test "type info: TypeId -> TypeInfo impl cast" {
_ = passTypeInfo(TypeId.Void);
_ = comptime passTypeInfo(TypeId.Void);
}
test "type info: extern fns with and without lib names" {
const S = struct {
extern fn bar1() void;
extern "cool" fn bar2() void;
};
const info = @typeInfo(S);
comptime {
for (info.Struct.decls) |decl| {
if (std.mem.eql(u8, decl.name, "bar1")) {
try expect(decl.data.Fn.lib_name == null);
} else {
try expectEqualStrings("cool", decl.data.Fn.lib_name.?);
}
}
}
}
test "data field is a compile-time value" {
const S = struct {
const Bar = @as(isize, -1);
};
comptime try expect(@typeInfo(S).Struct.decls[0].data.Var == isize);
}
test "sentinel of opaque pointer type" {
const c_void_info = @typeInfo(*anyopaque);
try expect(c_void_info.Pointer.sentinel == null);
}
test "@typeInfo does not force declarations into existence" {
const S = struct {
x: i32,
fn doNotReferenceMe() void {
@compileError("test failed");
}
};
comptime try expect(@typeInfo(S).Struct.fields.len == 1);
}
test "default value for a var-typed field" {
const S = struct { x: anytype };
try expect(@typeInfo(S).Struct.fields[0].default_value == null);
}
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "type info for async frames" {
switch (@typeInfo(@Frame(add))) {
.Frame => |frame| {
try expect(frame.function == add);
},
else => unreachable,
}
}
test "Declarations are returned in declaration order" {
const S = struct {
const a = 1;
const b = 2;
const c = 3;
const d = 4;
const e = 5;
};
const d = @typeInfo(S).Struct.decls;
try expect(std.mem.eql(u8, d[0].name, "a"));
try expect(std.mem.eql(u8, d[1].name, "b"));
try expect(std.mem.eql(u8, d[2].name, "c"));
try expect(std.mem.eql(u8, d[3].name, "d"));
try expect(std.mem.eql(u8, d[4].name, "e"));
}
test "Struct.is_tuple" {
try expect(@typeInfo(@TypeOf(.{0})).Struct.is_tuple);
try expect(!@typeInfo(@TypeOf(.{ .a = 0 })).Struct.is_tuple);
}
test "StructField.is_comptime" {
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" {
const A = struct {
pub const f1 = 42;
};
const B = struct {
const f0 = 42;
usingnamespace A;
};
try expect(@typeInfo(B).Struct.decls.len == 2);
//a
}