Commit 2a3fdd52ce

tgschultz <tgschultz@gmail.com>
2018-10-19 23:19:22
Add std.meta (#1662)
Implement std.meta
1 parent b9a53c2
Changed files (4)
std/meta/index.zig
@@ -0,0 +1,525 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const debug = std.debug;
+const mem = std.mem;
+const math = std.math;
+
+pub const trait = @import("trait.zig");
+
+const TypeId = builtin.TypeId;
+const TypeInfo = builtin.TypeInfo;
+
+pub fn tagName(v: var) []const u8 {
+    const T = @typeOf(v);
+    switch (@typeInfo(T)) {
+        TypeId.Enum => |info| {
+            const Tag = info.tag_type;
+            inline for (info.fields) |field| {
+                if (field.value == @enumToInt(v)) return field.name;
+            }
+
+            unreachable;
+        },
+        TypeId.Union => |info| {
+            const UnionTag = if(info.tag_type) |UT| UT else @compileError("union is untagged");
+            const Tag = @typeInfo(UnionTag).Enum.tag_type;
+            inline for (info.fields) |field| {
+                if (field.enum_field.?.value == @enumToInt(UnionTag(v)))
+                    return field.name;
+            }
+
+            unreachable;
+        },
+        TypeId.ErrorSet => |info| {
+            inline for (info.errors) |err| {
+                if (err.value == @errorToInt(v)) return err.name;
+            }
+
+            unreachable;
+        },
+        else => @compileError("expected enum, error set or union type, found '" 
+            ++ @typeName(T) ++ "'"),
+    }
+}
+
+test "std.meta.tagName" {
+    const E1 = enum.{
+        A,
+        B,
+    };
+    const E2 = enum(u8).{
+        C = 33,
+        D,
+    };
+    const U1 = union(enum).{
+        G: u8,
+        H: u16,
+    };
+    const U2 = union(E2).{
+        C: u8,
+        D: u16,
+    };
+
+    var u1g = U1.{ .G = 0 };
+    var u1h = U1.{ .H = 0 };
+    var u2a = U2.{ .C = 0 };
+    var u2b = U2.{ .D = 0 };
+
+    debug.assert(mem.eql(u8, tagName(E1.A), "A"));
+    debug.assert(mem.eql(u8, tagName(E1.B), "B"));
+    debug.assert(mem.eql(u8, tagName(E2.C), "C"));
+    debug.assert(mem.eql(u8, tagName(E2.D), "D"));
+    debug.assert(mem.eql(u8, tagName(error.E), "E"));
+    debug.assert(mem.eql(u8, tagName(error.F), "F"));
+    debug.assert(mem.eql(u8, tagName(u1g), "G"));
+    debug.assert(mem.eql(u8, tagName(u1h), "H"));
+    debug.assert(mem.eql(u8, tagName(u2a), "C"));
+    debug.assert(mem.eql(u8, tagName(u2b), "D"));
+}
+
+pub fn bitCount(comptime T: type) u32 {
+    return switch (@typeInfo(T)) {
+        TypeId.Int => |info| info.bits,
+        TypeId.Float => |info| info.bits,
+        else => @compileError("Expected int or float type, found '" ++ @typeName(T) ++ "'"),
+    };
+}
+
+test "std.meta.bitCount" {
+    debug.assert(bitCount(u8) == 8);
+    debug.assert(bitCount(f32) == 32);
+}
+
+pub fn alignment(comptime T: type) u29 {
+    //@alignOf works on non-pointer types
+    const P = if(comptime trait.is(TypeId.Pointer)(T)) T else *T;
+    return @typeInfo(P).Pointer.alignment;
+}
+
+test "std.meta.alignment" {
+    debug.assert(alignment(u8) == 1);
+    debug.assert(alignment(*align(1) u8) == 1);
+    debug.assert(alignment(*align(2) u8) == 2);
+    debug.assert(alignment([]align(1) u8) == 1);
+    debug.assert(alignment([]align(2) u8) == 2);
+}
+
+pub fn Child(comptime T: type) type {
+    return switch (@typeInfo(T)) {
+        TypeId.Array => |info| info.child,
+        TypeId.Pointer => |info| info.child,
+        TypeId.Optional => |info| info.child,
+        TypeId.Promise => |info| if(info.child) |child| child else null,
+        else => @compileError("Expected promise, pointer, optional, or array type, " 
+            ++ "found '" ++ @typeName(T) ++ "'"),
+    };
+}
+
+test "std.meta.Child" {
+    debug.assert(Child([1]u8) == u8);
+    debug.assert(Child(*u8) == u8);
+    debug.assert(Child([]u8) == u8);
+    debug.assert(Child(?u8) == u8);
+    debug.assert(Child(promise->u8) == u8);
+}
+
+pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
+    return switch (@typeInfo(T)) {
+        TypeId.Struct => |info| info.layout,
+        TypeId.Enum => |info| info.layout,
+        TypeId.Union => |info| info.layout,
+        else => @compileError("Expected struct, enum or union type, found '" 
+            ++ @typeName(T) ++ "'"),
+    };
+}
+
+test "std.meta.containerLayout" {
+    const E1 = enum.{
+        A,
+    };
+    const E2 = packed enum.{
+        A,
+    };
+    const E3 = extern enum.{
+        A,
+    };
+    const S1 = struct.{};
+    const S2 = packed struct.{};
+    const S3 = extern struct.{};
+    const U1 = union.{
+        a: u8,
+    };
+    const U2 = packed union.{
+        a: u8,
+    };
+    const U3 = extern union.{
+        a: u8,
+    };
+
+    debug.assert(containerLayout(E1) == TypeInfo.ContainerLayout.Auto);
+    debug.assert(containerLayout(E2) == TypeInfo.ContainerLayout.Packed);
+    debug.assert(containerLayout(E3) == TypeInfo.ContainerLayout.Extern);
+    debug.assert(containerLayout(S1) == TypeInfo.ContainerLayout.Auto);
+    debug.assert(containerLayout(S2) == TypeInfo.ContainerLayout.Packed);
+    debug.assert(containerLayout(S3) == TypeInfo.ContainerLayout.Extern);
+    debug.assert(containerLayout(U1) == TypeInfo.ContainerLayout.Auto);
+    debug.assert(containerLayout(U2) == TypeInfo.ContainerLayout.Packed);
+    debug.assert(containerLayout(U3) == TypeInfo.ContainerLayout.Extern);
+}
+
+pub fn definitions(comptime T: type) []TypeInfo.Definition {
+    return switch (@typeInfo(T)) {
+        TypeId.Struct => |info| info.defs,
+        TypeId.Enum => |info| info.defs,
+        TypeId.Union => |info| info.defs,
+        else => @compileError("Expected struct, enum or union type, found '" 
+            ++ @typeName(T) ++ "'"),
+    };
+}
+
+test "std.meta.definitions" {
+    const E1 = enum.{
+        A,
+
+        fn a() void {}
+    };
+    const S1 = struct.{
+        fn a() void {}
+    };
+    const U1 = union.{
+        a: u8,
+
+        fn a() void {}
+    };
+
+    const defs = comptime [][]TypeInfo.Definition.{
+        definitions(E1),
+        definitions(S1),
+        definitions(U1),
+    };
+
+    inline for (defs) |def| {
+        debug.assert(def.len == 1);
+        debug.assert(comptime mem.eql(u8, def[0].name, "a"));
+    }
+}
+
+pub fn definitionInfo(comptime T: type, comptime def_name: []const u8) TypeInfo.Definition {
+    inline for (comptime definitions(T)) |def| {
+        if (comptime mem.eql(u8, def.name, def_name))
+            return def;
+    }
+
+    @compileError("'" ++ @typeName(T) ++ "' has no definition '" ++ def_name ++ "'");
+}
+
+test "std.meta.definitionInfo" {
+    const E1 = enum.{
+        A,
+
+        fn a() void {}
+    };
+    const S1 = struct.{
+        fn a() void {}
+    };
+    const U1 = union.{
+        a: u8,
+
+        fn a() void {}
+    };
+
+    const infos = comptime []TypeInfo.Definition.{
+        definitionInfo(E1, "a"),
+        definitionInfo(S1, "a"),
+        definitionInfo(U1, "a"),
+    };
+
+    inline for (infos) |info| {
+        debug.assert(comptime mem.eql(u8, info.name, "a"));
+        debug.assert(!info.is_pub);
+    }
+}
+
+pub fn fields(comptime T: type) switch (@typeInfo(T)) {
+    TypeId.Struct => []TypeInfo.StructField,
+    TypeId.Union => []TypeInfo.UnionField,
+    TypeId.ErrorSet => []TypeInfo.Error,
+    TypeId.Enum => []TypeInfo.EnumField,
+    else => @compileError("Expected struct, union, error set or enum type, found '"
+        ++ @typeName(T) ++ "'"),
+} {
+    return switch (@typeInfo(T)) {
+        TypeId.Struct => |info| info.fields,
+        TypeId.Union => |info| info.fields,
+        TypeId.Enum => |info| info.fields,
+        TypeId.ErrorSet => |info| info.errors,
+        else => @compileError("Expected struct, union, error set or enum type, found '"
+            ++ @typeName(T) ++ "'"),
+    };
+}
+
+test "std.meta.fields" {
+    const E1 = enum.{
+        A,
+    };
+    const E2 = error.{A};
+    const S1 = struct.{
+        a: u8,
+    };
+    const U1 = union.{
+        a: u8,
+    };
+
+    const e1f = comptime fields(E1);
+    const e2f = comptime fields(E2);
+    const sf = comptime fields(S1);
+    const uf = comptime fields(U1);
+
+    debug.assert(e1f.len == 1);
+    debug.assert(e2f.len == 1);
+    debug.assert(sf.len == 1);
+    debug.assert(uf.len == 1);
+    debug.assert(mem.eql(u8, e1f[0].name, "A"));
+    debug.assert(mem.eql(u8, e2f[0].name, "A"));
+    debug.assert(mem.eql(u8, sf[0].name, "a"));
+    debug.assert(mem.eql(u8, uf[0].name, "a"));
+    debug.assert(comptime sf[0].field_type == u8);
+    debug.assert(comptime uf[0].field_type == u8);
+}
+
+pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) {
+    TypeId.Struct => TypeInfo.StructField,
+    TypeId.Union => TypeInfo.UnionField,
+    TypeId.ErrorSet => TypeInfo.Error,
+    TypeId.Enum => TypeInfo.EnumField,
+    else => @compileError("Expected struct, union, error set or enum type, found '" 
+        ++ @typeName(T) ++ "'"),
+} {
+    inline for (comptime fields(T)) |field| {
+        if (comptime mem.eql(u8, field.name, field_name))
+            return field;
+    }
+
+    @compileError("'" ++ @typeName(T) ++ "' has no field '" ++ field_name ++ "'");
+}
+
+test "std.meta.fieldInfo" {
+    const E1 = enum.{
+        A,
+    };
+    const E2 = error.{A};
+    const S1 = struct.{
+        a: u8,
+    };
+    const U1 = union.{
+        a: u8,
+    };
+
+    const e1f = comptime fieldInfo(E1, "A");
+    const e2f = comptime fieldInfo(E2, "A");
+    const sf = comptime fieldInfo(S1, "a");
+    const uf = comptime fieldInfo(U1, "a");
+
+    debug.assert(mem.eql(u8, e1f.name, "A"));
+    debug.assert(mem.eql(u8, e2f.name, "A"));
+    debug.assert(mem.eql(u8, sf.name, "a"));
+    debug.assert(mem.eql(u8, uf.name, "a"));
+    debug.assert(comptime sf.field_type == u8);
+    debug.assert(comptime uf.field_type == u8);
+}
+
+pub fn TagType(comptime T: type) type {
+    return switch (@typeInfo(T)) {
+        TypeId.Enum => |info| info.tag_type,
+        TypeId.Union => |info| if(info.tag_type) |Tag| Tag else null,
+        else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"),
+    };
+}
+
+test "std.meta.TagType" {
+    const E = enum(u8).{
+        C = 33,
+        D,
+    };
+    const U = union(E).{
+        C: u8,
+        D: u16,
+    };
+
+    debug.assert(TagType(E) == u8);
+    debug.assert(TagType(U) == E);
+}
+
+
+
+///Returns the active tag of a tagged union
+pub fn activeTag(u: var) @TagType(@typeOf(u))
+{
+    const T = @typeOf(u);
+    return @TagType(T)(u);
+}
+
+test "std.meta.activeTag"
+{
+    const UE = enum.
+    {
+        Int,
+        Float,
+    };
+    
+    const U = union(UE).
+    {
+        Int: u32,
+        Float: f32,
+    };
+    
+    var u = U.{ .Int = 32, };
+    debug.assert(activeTag(u) == UE.Int);
+    
+    u = U.{ .Float = 112.9876, };
+    debug.assert(activeTag(u) == UE.Float);
+
+}
+
+///Compares two of any type for equality. Containers are compared on a field-by-field basis,
+/// where possible. Pointers are not followed.
+pub fn eql(a: var, b: @typeOf(a)) bool
+{
+    const T = @typeOf(a);
+    
+    switch(@typeId(T))
+    {
+        builtin.TypeId.Struct =>
+        {
+            const info = @typeInfo(T).Struct;
+            
+            inline for(info.fields) |field_info|
+            {
+                if(!eql(@field(a, field_info.name), 
+                    @field(b, field_info.name))) return false;
+            }
+            return true;
+        },
+        builtin.TypeId.ErrorUnion =>
+        {
+            if(a) |a_p|
+            {
+                if(b) |b_p| return eql(a_p, b_p) else |_| return false;
+            }
+            else |a_e|
+            {
+                if(b) |_| return false else |b_e| return a_e == b_e;
+            }
+        },
+        builtin.TypeId.Union => 
+        {
+            const info = @typeInfo(T).Union;
+            
+            if(info.tag_type) |_|
+            {
+                const tag_a = activeTag(a);
+                const tag_b = activeTag(b);
+                if(tag_a != tag_b) return false;
+                
+                inline for(info.fields) |field_info|
+                {
+                    const enum_field = field_info.enum_field.?;
+                    if(enum_field.value == @enumToInt(tag_a))
+                    {
+                        return eql(@field(a, enum_field.name),
+                            @field(b, enum_field.name));
+                    }
+                }
+                return false;
+            }
+            
+            @compileError("cannot compare untagged union type " ++ @typeName(T));
+        },
+        builtin.TypeId.Array => 
+        {
+            if(a.len != b.len) return false;
+            for(a) |e, i| if(!eql(e, b[i])) return false;
+            return true;
+        },
+        builtin.TypeId.Pointer => 
+        {
+            const info = @typeInfo(T).Pointer;
+            switch(info.size)
+            {
+                builtin.TypeInfo.Pointer.Size.One,
+                builtin.TypeInfo.Pointer.Size.Many => return a == b,
+                builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
+            }
+        },
+        else => return a == b,
+    }
+}
+
+
+test "std.meta.eql"
+{
+    const S = struct.
+    {
+        a: u32,
+        b: f64,
+        c: [5]u8,
+    };
+    
+    const U = union(enum).
+    {
+        s: S,
+        f: f32,
+    };
+    
+    const s_1 = S.
+    {
+        .a = 134,
+        .b = 123.3,
+        .c = "12345",
+    };
+    
+    const s_2 = S.
+    {
+        .a = 1,
+        .b = 123.3,
+        .c = "54321",
+    };
+    
+    const s_3 = S.
+    {
+        .a = 134,
+        .b = 123.3,
+        .c = "12345",
+    };
+    
+    const u_1 = U.{ .f = 24, };
+    const u_2 = U.{ .s = s_1, };
+    const u_3 = U.{ .f = 24, };
+    
+    debug.assert(eql(s_1, s_3));
+    debug.assert(eql(&s_1, &s_1));
+    debug.assert(!eql(&s_1, &s_3));
+    debug.assert(eql(u_1, u_3));
+    debug.assert(!eql(u_1, u_2));
+    
+    var a1 = "abcdef";
+    var a2 = "abcdef";
+    var a3 = "ghijkl";
+    
+    debug.assert(eql(a1, a2));
+    debug.assert(!eql(a1, a3));
+    debug.assert(!eql(a1[0..], a2[0..]));
+    
+    const EU = struct.
+    {
+        fn tst(err: bool) !u8
+        {
+            if(err) return error.Error;
+            return u8(5);
+        }
+    };
+    
+    debug.assert(eql(EU.tst(true), EU.tst(true)));
+    debug.assert(eql(EU.tst(false), EU.tst(false)));
+    debug.assert(!eql(EU.tst(false), EU.tst(true)));
+}
\ No newline at end of file
std/meta/trait.zig
@@ -0,0 +1,449 @@
+const std = @import("std");
+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)(error));
+}
+
+///
+
+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));
+}
+
+///
\ No newline at end of file
std/index.zig
@@ -32,6 +32,7 @@ pub const io = @import("io.zig");
 pub const json = @import("json.zig");
 pub const macho = @import("macho.zig");
 pub const math = @import("math/index.zig");
+pub const meta = @import("meta/index.zig");
 pub const mem = @import("mem.zig");
 pub const net = @import("net.zig");
 pub const os = @import("os/index.zig");
@@ -74,6 +75,7 @@ test "std" {
     _ = @import("json.zig");
     _ = @import("macho.zig");
     _ = @import("math/index.zig");
+    _ = @import("meta/index.zig");
     _ = @import("mem.zig");
     _ = @import("net.zig");
     _ = @import("heap.zig");
std/mem.zig
@@ -4,6 +4,8 @@ const assert = debug.assert;
 const math = std.math;
 const builtin = @import("builtin");
 const mem = @This();
+const meta = std.meta;
+const trait = meta.trait;
 
 pub const Allocator = struct.{
     pub const Error = error.{OutOfMemory};
@@ -863,3 +865,180 @@ pub fn endianSwap(comptime T: type, x: T) T {
 test "std.mem.endianSwap" {
     assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
 }
+
+
+
+fn AsBytesReturnType(comptime P: type) type
+{
+    if(comptime !trait.isSingleItemPtr(P)) @compileError("expected single item " 
+        ++ "pointer, passed " ++ @typeName(P));
+
+    const size = usize(@sizeOf(meta.Child(P)));
+    const alignment = comptime meta.alignment(P);
+    if(comptime trait.isConstPtr(P)) return *align(alignment) const [size]u8;
+    return *align(alignment) [size]u8;
+}
+
+///Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
+pub fn asBytes(ptr: var) AsBytesReturnType(@typeOf(ptr))
+{
+    const P = @typeOf(ptr);
+    return @ptrCast(AsBytesReturnType(P), ptr);
+}
+
+test "std.mem.asBytes"
+{
+    const deadbeef = u32(0xDEADBEEF);
+    const deadbeef_bytes = switch(builtin.endian)
+    {
+        builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
+        builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
+    };
+    
+    debug.assert(std.mem.eql(u8, asBytes(&deadbeef), deadbeef_bytes));
+    
+    var codeface = u32(0xC0DEFACE);
+    for(asBytes(&codeface).*) |*b| b.* = 0;
+    debug.assert(codeface == 0);
+    
+    const S = packed struct.
+    {
+        a: u8,
+        b: u8,
+        c: u8,
+        d: u8,
+    };
+    
+    const inst = S.{ .a = 0xBE, .b = 0xEF, .c = 0xDE, .d = 0xA1, };
+    debug.assert(std.mem.eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1"));
+}
+
+///Given any value, returns a copy of its bytes in an array.
+pub fn toBytes(value: var) [@sizeOf(@typeOf(value))]u8
+{
+    return asBytes(&value).*;
+}
+
+test "std.mem.toBytes"
+{
+    var my_bytes = toBytes(u32(0x12345678));
+    switch(builtin.endian)
+    {
+        builtin.Endian.Big => debug.assert(std.mem.eql(u8, my_bytes, "\x12\x34\x56\x78")),
+        builtin.Endian.Little => debug.assert(std.mem.eql(u8, my_bytes, "\x78\x56\x34\x12")),
+    }
+    
+    my_bytes[0] = '\x99';
+    switch(builtin.endian)
+    {
+        builtin.Endian.Big => debug.assert(std.mem.eql(u8, my_bytes, "\x99\x34\x56\x78")),
+        builtin.Endian.Little => debug.assert(std.mem.eql(u8, my_bytes, "\x99\x56\x34\x12")),
+    }
+}
+
+
+fn BytesAsValueReturnType(comptime T: type, comptime  B: type) type
+{
+    const size = usize(@sizeOf(T));
+
+    if(comptime !trait.is(builtin.TypeId.Pointer)(B) or meta.Child(B) != [size]u8)
+    {
+        @compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B));
+    }
+    
+    const alignment = comptime meta.alignment(B);
+    
+    return if(comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;
+}
+
+///Given a pointer to an array of bytes, returns a pointer to a value of the specified type
+/// backed by those bytes, preserving constness.
+pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @typeOf(bytes))
+{
+    return @ptrCast(BytesAsValueReturnType(T, @typeOf(bytes)), bytes);
+}
+
+test "std.mem.bytesAsValue"
+{
+    const deadbeef = u32(0xDEADBEEF);
+    const deadbeef_bytes = switch(builtin.endian)
+    {
+        builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
+        builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
+    };
+    
+    debug.assert(deadbeef == bytesAsValue(u32, &deadbeef_bytes).*);
+    
+    var codeface_bytes = switch(builtin.endian)
+    {
+        builtin.Endian.Big => "\xC0\xDE\xFA\xCE",
+        builtin.Endian.Little => "\xCE\xFA\xDE\xC0",
+    };
+    var codeface = bytesAsValue(u32, &codeface_bytes);
+    debug.assert(codeface.* == 0xC0DEFACE);
+    codeface.* = 0;
+    for(codeface_bytes) |b| debug.assert(b == 0);
+    
+    const S = packed struct.
+    {
+        a: u8,
+        b: u8,
+        c: u8,
+        d: u8,
+    };
+    
+    const inst = S.{ .a = 0xBE, .b = 0xEF, .c = 0xDE, .d = 0xA1, };
+    const inst_bytes = "\xBE\xEF\xDE\xA1";
+    const inst2 = bytesAsValue(S, &inst_bytes);
+    debug.assert(meta.eql(inst, inst2.*));
+}
+
+///Given a pointer to an array of bytes, returns a value of the specified type backed by a
+/// copy of those bytes.
+pub fn bytesToValue(comptime T: type, bytes: var) T
+{
+    return bytesAsValue(T, &bytes).*;
+}
+ test "std.mem.bytesToValue"
+{
+    const deadbeef_bytes = switch(builtin.endian)
+    {
+        builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
+        builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
+    };
+    
+    const deadbeef = bytesToValue(u32, deadbeef_bytes);
+    debug.assert(deadbeef == u32(0xDEADBEEF));
+}
+
+
+fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type
+{
+    if(trait.isConstPtr(T)) return *const [length]meta.Child(meta.Child(T));
+    return *[length]meta.Child(meta.Child(T));
+}
+
+///Given a pointer to an array, returns a pointer to a portion of that array, preserving constness.
+pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) 
+    SubArrayPtrReturnType(@typeOf(ptr), length)
+{
+    debug.assert(start + length <= ptr.*.len);
+    
+    const ReturnType = SubArrayPtrReturnType(@typeOf(ptr), length);
+    const T = meta.Child(meta.Child(@typeOf(ptr)));
+    return @ptrCast(ReturnType, &ptr[start]);
+}
+
+test "std.mem.subArrayPtr"
+{
+    const a1 = "abcdef";
+    const sub1 = subArrayPtr(&a1, 2, 3);
+    debug.assert(std.mem.eql(u8, sub1.*, "cde"));
+    
+    var a2 = "abcdef";
+    var sub2 = subArrayPtr(&a2, 2, 3);
+
+    debug.assert(std.mem.eql(u8, sub2, "cde"));
+    sub2[1] = 'X';
+    debug.assert(std.mem.eql(u8, a2, "abcXef"));
+}
\ No newline at end of file