diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2019-09-26 01:54:45 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-09-26 01:54:45 -0400 |
| commit | 68bb3945708c43109c48bda3664176307d45b62c (patch) | |
| tree | afb9731e10cef9d192560b52cd9ae2cf179775c4 /lib/std/meta/trait.zig | |
| parent | 6128bc728d1e1024a178c16c2149f5b1a167a013 (diff) | |
| parent | 4637e8f9699af9c3c6cf4df50ef5bb67c7a318a4 (diff) | |
| download | zig-68bb3945708c43109c48bda3664176307d45b62c.tar.gz zig-68bb3945708c43109c48bda3664176307d45b62c.zip | |
Merge pull request #3315 from ziglang/mv-std-lib
Move std/ to lib/std/
Diffstat (limited to 'lib/std/meta/trait.zig')
| -rw-r--r-- | lib/std/meta/trait.zig | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig new file mode 100644 index 0000000000..43be7f3dfb --- /dev/null +++ b/lib/std/meta/trait.zig @@ -0,0 +1,363 @@ +const std = @import("../std.zig"); +const builtin = @import("builtin"); +const mem = std.mem; +const debug = std.debug; +const testing = std.testing; +const warn = debug.warn; + +const meta = @import("../meta.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([_]TraitFn{ + hasFn("add"), + hasField("x"), + hasField("y"), + }); + testing.expect(isVector(Vector2)); + testing.expect(!isVector(u8)); +} + +/// +pub fn hasFn(comptime name: []const u8) TraitFn { + const Closure = struct { + pub fn trait(comptime T: type) bool { + if (!comptime isContainer(T)) return false; + if (!comptime @hasDecl(T, name)) return false; + const DeclType = @typeOf(@field(T, name)); + const decl_type_id = @typeId(DeclType); + return decl_type_id == builtin.TypeId.Fn; + } + }; + return Closure.trait; +} + +test "std.meta.trait.hasFn" { + const TestStruct = struct { + pub fn useless() void {} + }; + + testing.expect(hasFn("useless")(TestStruct)); + testing.expect(!hasFn("append")(TestStruct)); + testing.expect(!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, + }; + + testing.expect(hasField("value")(TestStruct)); + testing.expect(!hasField("value")(*TestStruct)); + testing.expect(!hasField("x")(TestStruct)); + testing.expect(!hasField("x")(**TestStruct)); + testing.expect(!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" { + testing.expect(is(builtin.TypeId.Int)(u8)); + testing.expect(!is(builtin.TypeId.Int)(f32)); + testing.expect(is(builtin.TypeId.Pointer)(*u8)); + testing.expect(is(builtin.TypeId.Void)(void)); + testing.expect(!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" { + testing.expect(!isPtrTo(builtin.TypeId.Struct)(struct {})); + testing.expect(isPtrTo(builtin.TypeId.Struct)(*struct {})); + testing.expect(!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 {}; + + testing.expect(isExtern(TestExStruct)); + testing.expect(!isExtern(TestStruct)); + testing.expect(!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 {}; + + testing.expect(isPacked(TestPStruct)); + testing.expect(!isPacked(TestStruct)); + testing.expect(!isPacked(u8)); +} + +/// +pub fn isUnsignedInt(comptime T: type) bool { + return switch (@typeId(T)) { + builtin.TypeId.Int => !@typeInfo(T).Int.is_signed, + else => false, + }; +} + +test "isUnsignedInt" { + testing.expect(isUnsignedInt(u32) == true); + testing.expect(isUnsignedInt(comptime_int) == false); + testing.expect(isUnsignedInt(i64) == false); + testing.expect(isUnsignedInt(f64) == false); +} + +/// +pub fn isSignedInt(comptime T: type) bool { + return switch (@typeId(T)) { + builtin.TypeId.ComptimeInt => true, + builtin.TypeId.Int => @typeInfo(T).Int.is_signed, + else => false, + }; +} + +test "isSignedInt" { + testing.expect(isSignedInt(u32) == false); + testing.expect(isSignedInt(comptime_int) == true); + testing.expect(isSignedInt(i64) == true); + testing.expect(isSignedInt(f64) == false); +} + +/// +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; + testing.expect(isSingleItemPtr(@typeOf(&array[0]))); + testing.expect(!isSingleItemPtr(@typeOf(array))); + testing.expect(!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]); + testing.expect(isManyItemPtr(@typeOf(mip))); + testing.expect(!isManyItemPtr(@typeOf(array))); + testing.expect(!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; + testing.expect(isSlice(@typeOf(array[0..]))); + testing.expect(!isSlice(@typeOf(array))); + testing.expect(!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..]; + + testing.expect(isIndexable(@typeOf(array))); + testing.expect(isIndexable(@typeOf(&array))); + testing.expect(isIndexable(@typeOf(slice))); + testing.expect(!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, + }; + + testing.expect(isNumber(u32)); + testing.expect(isNumber(f32)); + testing.expect(isNumber(u64)); + testing.expect(isNumber(@typeOf(102))); + testing.expect(isNumber(@typeOf(102.123))); + testing.expect(!isNumber([]u8)); + testing.expect(!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); + testing.expect(isConstPtr(*const @typeOf(t))); + testing.expect(isConstPtr(@typeOf(&c))); + testing.expect(!isConstPtr(*@typeOf(t))); + testing.expect(!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, + }; + + testing.expect(isContainer(TestStruct)); + testing.expect(isContainer(TestUnion)); + testing.expect(isContainer(TestEnum)); + testing.expect(!isContainer(u8)); +} |
