const std = @import("../index.zig"); const builtin = @import("builtin"); const mem = std.mem; const debug = std.debug; const warn = debug.warn; const meta = @import("index.zig"); //This is necessary if we want to return generic functions directly because of how the // the type erasure works. see: #1375 fn traitFnWorkaround(comptime T: type) bool { return false; } pub const TraitFn = @typeOf(traitFnWorkaround); /// //////Trait generators //Need TraitList because compiler can't do varargs at comptime yet pub const TraitList = []const TraitFn; pub fn multiTrait(comptime traits: TraitList) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { inline for (traits) |t| if (!t(T)) return false; return true; } }; return Closure.trait; } test "std.meta.trait.multiTrait" { const Vector2 = struct { const MyType = @This(); x: u8, y: u8, pub fn add(self: MyType, other: MyType) MyType { return MyType{ .x = self.x + other.x, .y = self.y + other.y, }; } }; const isVector = multiTrait(TraitList{ hasFn("add"), hasField("x"), hasField("y"), }); debug.assert(isVector(Vector2)); debug.assert(!isVector(u8)); } /// pub fn hasDef(comptime name: []const u8) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { const info = @typeInfo(T); const defs = switch (info) { builtin.TypeId.Struct => |s| s.defs, builtin.TypeId.Union => |u| u.defs, builtin.TypeId.Enum => |e| e.defs, else => return false, }; inline for (defs) |def| { if (mem.eql(u8, def.name, name)) return def.is_pub; } return false; } }; return Closure.trait; } test "std.meta.trait.hasDef" { const TestStruct = struct { pub const value = u8(16); }; const TestStructFail = struct { const value = u8(16); }; debug.assert(hasDef("value")(TestStruct)); debug.assert(!hasDef("value")(TestStructFail)); debug.assert(!hasDef("value")(*TestStruct)); debug.assert(!hasDef("value")(**TestStructFail)); debug.assert(!hasDef("x")(TestStruct)); debug.assert(!hasDef("value")(u8)); } /// pub fn hasFn(comptime name: []const u8) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { if (!comptime hasDef(name)(T)) return false; const DefType = @typeOf(@field(T, name)); const def_type_id = @typeId(DefType); return def_type_id == builtin.TypeId.Fn; } }; return Closure.trait; } test "std.meta.trait.hasFn" { const TestStruct = struct { pub fn useless() void {} }; debug.assert(hasFn("useless")(TestStruct)); debug.assert(!hasFn("append")(TestStruct)); debug.assert(!hasFn("useless")(u8)); } /// pub fn hasField(comptime name: []const u8) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { const info = @typeInfo(T); const fields = switch (info) { builtin.TypeId.Struct => |s| s.fields, builtin.TypeId.Union => |u| u.fields, builtin.TypeId.Enum => |e| e.fields, else => return false, }; inline for (fields) |field| { if (mem.eql(u8, field.name, name)) return true; } return false; } }; return Closure.trait; } test "std.meta.trait.hasField" { const TestStruct = struct { value: u32, }; debug.assert(hasField("value")(TestStruct)); debug.assert(!hasField("value")(*TestStruct)); debug.assert(!hasField("x")(TestStruct)); debug.assert(!hasField("x")(**TestStruct)); debug.assert(!hasField("value")(u8)); } /// pub fn is(comptime id: builtin.TypeId) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { return id == @typeId(T); } }; return Closure.trait; } test "std.meta.trait.is" { debug.assert(is(builtin.TypeId.Int)(u8)); debug.assert(!is(builtin.TypeId.Int)(f32)); debug.assert(is(builtin.TypeId.Pointer)(*u8)); debug.assert(is(builtin.TypeId.Void)(void)); debug.assert(!is(builtin.TypeId.Optional)(anyerror)); } /// pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { if (!comptime isSingleItemPtr(T)) return false; return id == @typeId(meta.Child(T)); } }; return Closure.trait; } test "std.meta.trait.isPtrTo" { debug.assert(!isPtrTo(builtin.TypeId.Struct)(struct {})); debug.assert(isPtrTo(builtin.TypeId.Struct)(*struct {})); debug.assert(!isPtrTo(builtin.TypeId.Struct)(**struct {})); } ///////////Strait trait Fns //@TODO: // Somewhat limited since we can't apply this logic to normal variables, fields, or // Fns yet. Should be isExternType? pub fn isExtern(comptime T: type) bool { const Extern = builtin.TypeInfo.ContainerLayout.Extern; const info = @typeInfo(T); return switch (info) { builtin.TypeId.Struct => |s| s.layout == Extern, builtin.TypeId.Union => |u| u.layout == Extern, builtin.TypeId.Enum => |e| e.layout == Extern, else => false, }; } test "std.meta.trait.isExtern" { const TestExStruct = extern struct {}; const TestStruct = struct {}; debug.assert(isExtern(TestExStruct)); debug.assert(!isExtern(TestStruct)); debug.assert(!isExtern(u8)); } /// pub fn isPacked(comptime T: type) bool { const Packed = builtin.TypeInfo.ContainerLayout.Packed; const info = @typeInfo(T); return switch (info) { builtin.TypeId.Struct => |s| s.layout == Packed, builtin.TypeId.Union => |u| u.layout == Packed, builtin.TypeId.Enum => |e| e.layout == Packed, else => false, }; } test "std.meta.trait.isPacked" { const TestPStruct = packed struct {}; const TestStruct = struct {}; debug.assert(isPacked(TestPStruct)); debug.assert(!isPacked(TestStruct)); debug.assert(!isPacked(u8)); } /// pub fn isSingleItemPtr(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); return info.Pointer.size == builtin.TypeInfo.Pointer.Size.One; } return false; } test "std.meta.trait.isSingleItemPtr" { const array = []u8{0} ** 10; debug.assert(isSingleItemPtr(@typeOf(&array[0]))); debug.assert(!isSingleItemPtr(@typeOf(array))); debug.assert(!isSingleItemPtr(@typeOf(array[0..1]))); } /// pub fn isManyItemPtr(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Many; } return false; } test "std.meta.trait.isManyItemPtr" { const array = []u8{0} ** 10; const mip = @ptrCast([*]const u8, &array[0]); debug.assert(isManyItemPtr(@typeOf(mip))); debug.assert(!isManyItemPtr(@typeOf(array))); debug.assert(!isManyItemPtr(@typeOf(array[0..1]))); } /// pub fn isSlice(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Slice; } return false; } test "std.meta.trait.isSlice" { const array = []u8{0} ** 10; debug.assert(isSlice(@typeOf(array[0..]))); debug.assert(!isSlice(@typeOf(array))); debug.assert(!isSlice(@typeOf(&array[0]))); } /// pub fn isIndexable(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); if (info.Pointer.size == builtin.TypeInfo.Pointer.Size.One) { if (comptime is(builtin.TypeId.Array)(meta.Child(T))) return true; return false; } return true; } return comptime is(builtin.TypeId.Array)(T); } test "std.meta.trait.isIndexable" { const array = []u8{0} ** 10; const slice = array[0..]; debug.assert(isIndexable(@typeOf(array))); debug.assert(isIndexable(@typeOf(&array))); debug.assert(isIndexable(@typeOf(slice))); debug.assert(!isIndexable(meta.Child(@typeOf(slice)))); } /// pub fn isNumber(comptime T: type) bool { return switch (@typeId(T)) { builtin.TypeId.Int, builtin.TypeId.Float, builtin.TypeId.ComptimeInt, builtin.TypeId.ComptimeFloat => true, else => false, }; } test "std.meta.trait.isNumber" { const NotANumber = struct { number: u8, }; debug.assert(isNumber(u32)); debug.assert(isNumber(f32)); debug.assert(isNumber(u64)); debug.assert(isNumber(@typeOf(102))); debug.assert(isNumber(@typeOf(102.123))); debug.assert(!isNumber([]u8)); debug.assert(!isNumber(NotANumber)); } /// pub fn isConstPtr(comptime T: type) bool { if (!comptime is(builtin.TypeId.Pointer)(T)) return false; const info = @typeInfo(T); return info.Pointer.is_const; } test "std.meta.trait.isConstPtr" { var t = u8(0); const c = u8(0); debug.assert(isConstPtr(*const @typeOf(t))); debug.assert(isConstPtr(@typeOf(&c))); debug.assert(!isConstPtr(*@typeOf(t))); debug.assert(!isConstPtr(@typeOf(6))); } /// pub fn isContainer(comptime T: type) bool { const info = @typeInfo(T); return switch (info) { builtin.TypeId.Struct => true, builtin.TypeId.Union => true, builtin.TypeId.Enum => true, else => false, }; } test "std.meta.trait.isContainer" { const TestStruct = struct {}; const TestUnion = union { a: void, }; const TestEnum = enum { A, B, }; debug.assert(isContainer(TestStruct)); debug.assert(isContainer(TestUnion)); debug.assert(isContainer(TestEnum)); debug.assert(!isContainer(u8)); }