aboutsummaryrefslogtreecommitdiff
path: root/lib/std/meta/trait.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-09-26 01:54:45 -0400
committerGitHub <noreply@github.com>2019-09-26 01:54:45 -0400
commit68bb3945708c43109c48bda3664176307d45b62c (patch)
treeafb9731e10cef9d192560b52cd9ae2cf179775c4 /lib/std/meta/trait.zig
parent6128bc728d1e1024a178c16c2149f5b1a167a013 (diff)
parent4637e8f9699af9c3c6cf4df50ef5bb67c7a318a4 (diff)
downloadzig-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.zig363
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));
+}