From c3890a117e88d36f6f43d3a1b93a8ea7b8651ab2 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 27 Jun 2018 10:20:34 -0500 Subject: [PATCH 01/14] Fix old syntax in rand Ziggurat somehow did not get updated to latest syntax --- std/rand/ziggurat.zig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig index 774d3bd52a06..f7a1359f1766 100644 --- a/std/rand/ziggurat.zig +++ b/std/rand/ziggurat.zig @@ -84,12 +84,12 @@ fn ZigTableGen( for (tables.x[2..256]) |*entry, i| { const last = tables.x[2 + i - 1]; - *entry = f_inv(v / last + f(last)); + entry.* = f_inv(v / last + f(last)); } tables.x[256] = 0; for (tables.f[0..]) |*entry, i| { - *entry = f(tables.x[i]); + entry.* = f(tables.x[i]); } return tables; @@ -160,3 +160,7 @@ test "ziggurant exp dist sanity" { _ = prng.random.floatExp(f64); } } + +test "ziggurat table gen" { + const table = NormDist; +} From 47cf2aa9e4a3995fde758e01a67503959be2e590 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 27 Jun 2018 10:22:21 -0500 Subject: [PATCH 02/14] Fix broken float casts f32 float casts somehow not updated to latest syntax --- std/rand/index.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/rand/index.zig b/std/rand/index.zig index 13694f4c09a0..7daa558f135f 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -116,7 +116,7 @@ pub const Random = struct { pub fn floatNorm(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.NormDist); switch (T) { - f32 => return f32(value), + f32 => return @floatCast(f32, value), f64 => return value, else => @compileError("unknown floating point type"), } @@ -128,7 +128,7 @@ pub const Random = struct { pub fn floatExp(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.ExpDist); switch (T) { - f32 => return f32(value), + f32 => return @floatCast(f32, value), f64 => return value, else => @compileError("unknown floating point type"), } From ec472e24d5f398630847e5ed1adbd821e487dd6d Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 18 Oct 2018 10:09:25 -0500 Subject: [PATCH 03/14] Added std.meta --- std/meta/index.zig | 691 +++++++++++++++++++++++++++++++++++++++++++++ std/meta/trait.zig | 495 ++++++++++++++++++++++++++++++++ 2 files changed, 1186 insertions(+) create mode 100644 std/meta/index.zig create mode 100644 std/meta/trait.zig diff --git a/std/meta/index.zig b/std/meta/index.zig new file mode 100644 index 000000000000..debd098d708b --- /dev/null +++ b/std/meta/index.zig @@ -0,0 +1,691 @@ +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; + +///Given a container or a pointer to a container, returns the container type. +/// Otherwise it returns the type as-is. +pub fn UnwrapContainer(comptime T: type) type +{ + comptime + { + if(trait.isPtrTo(builtin.TypeId.Struct)(T) + or trait.isPtrTo(builtin.TypeId.Union)(T)) return Child(T); + return T; + } +} + +test "std.meta.UnwrapContainer" +{ + const x = u8(10); + debug.assert(UnwrapContainer(*u8) == *u8); + debug.assert(UnwrapContainer(u8) == u8); + + const S = struct. + { + f: u8, + }; + + const U = union. + { + a: u8, + b: u16, + }; + + debug.assert(UnwrapContainer(S) == S); + debug.assert(UnwrapContainer(*S) == S); + debug.assert(UnwrapContainer(**S) == **S); + + debug.assert(UnwrapContainer(U) == U); + debug.assert(UnwrapContainer(*U) == U); + debug.assert(UnwrapContainer(**U) == **U); +} + +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) u8 { + 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) u32 { + return switch (@typeInfo(T)) { + TypeId.Pointer => |info| info.alignment, + else => @compileError("Expected pointer type, found '" ++ @typeName(T) ++ "'"), + }; +} + +test "std.meta.alignment" { + 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(UnwrapContainer(@typeOf(u))) +{ + const T = @typeOf(u); + if(comptime trait.isSingleItemPtr(T)) return @TagType(Child(T))(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); + +} + +//see: bytesOf +fn BytesReturnType(comptime P: type) type +{ + return if(trait.isConstPtr(P)) []const u8 else []u8; +} +///Given a pointer to a single item, returns a slice of the underlying bytes, preserving the +/// pointer's const-ness. +pub fn bytes(ptr: var) BytesReturnType(@typeOf(ptr)) +{ + const P = @typeOf(ptr); + if(comptime !trait.isSingleItemPtr(P)) @compileError("meta.bytes requires a single item " + ++ "pointer, passed " ++ @typeName(P)); + const T = Child(P); + + const ReturnType = comptime BytesReturnType(P); + const IntermediateType = comptime (if(trait.isConstPtr(P)) [*]const u8 else [*]u8); + + var slice: ReturnType = undefined; + slice.ptr = @ptrCast(IntermediateType, ptr); + slice.len = @sizeOf(T); + return @sliceToBytes(slice); +} + +test "std.meta.bytes" +{ + 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, bytes(&deadbeef), deadbeef_bytes)); + + var codeface = u32(0xC0DEFACE); + for(bytes(&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, bytes(&inst), "\xBE\xEF\xDE\xA1")); +} + +/// Provides the size of a type padded to the nearest byte, instead of however the +/// compiler actually pads it. Useful for determining the size of a type as it would +/// likely be stored to disk. +pub fn byteAlignedSizeOf(comptime T: type) usize +{ + comptime + { + switch(@typeId(T)) + { + builtin.TypeId.Bool, + builtin.TypeId.Int, + builtin.TypeId.Float => return @sizeOf(T), + builtin.TypeId.Enum => return @sizeOf(@TagType(T)), + builtin.TypeId.Array => + { + return byteAlignedSizeOf(Child(T)) * T.len; + }, + builtin.TypeId.Struct => + { + const info = @typeInfo(T).Struct; + + if(info.layout == builtin.TypeInfo.ContainerLayout.Packed) return @sizeOf(T); + + var size = usize(0); + for(info.fields) |field| + { + size += byteAlignedSizeOf(field.field_type); + } + return size; + }, + builtin.TypeId.Union => + { + const info = @typeInfo(T).Union; + var largest = usize(0); + for(info.fields) |field| + { + const size = byteAlignedSizeOf(field.field_type); + if(size > largest) largest = size; + } + return largest; + }, + else => return 0, + } + } +} + +test "std.meta.byteAlignedSizeOf" +{ + const UnpackedS = struct. + { + a: u24, + b: u8, + }; + + debug.assert(byteAlignedSizeOf(UnpackedS) == 4); + + const UnpackedWithSub = struct. + { + a: u8, + b: u8, + unpacked_s: UnpackedS, + }; + + debug.assert(byteAlignedSizeOf(UnpackedWithSub) == 4 + 2); + + const PackedS = packed struct. + { + b0: bool, + b1: bool, + b2: bool, + b3: bool, + b4: bool, + b5: bool, + b6: bool, + b7: bool, + }; + + const UnpackedWithPackedSub = struct. + { + a: u8, + b: u8, + unpacked_s: UnpackedS, + packed_s: PackedS, + }; + + debug.assert(byteAlignedSizeOf(UnpackedWithPackedSub) == 4 + 2 + 1); +} + +///Compares two structs or (tagged) unions for equality on a field level +/// so that padding bytes are not relevant. +pub fn containerEql(container_a: var, container_b: var) bool +{ + const T = UnwrapContainer(@typeOf(container_a)); + debug.assert(UnwrapContainer(@typeOf(container_b)) == T); + + return switch(@typeId(T)) + { + builtin.TypeId.Struct => + { + const info = @typeInfo(T).Struct; + + inline for(info.fields) |field_info| + { + switch(@typeId(field_info.field_type)) + { + builtin.TypeId.Struct, + builtin.TypeId.Union => + { + const eql = containerEql(@field(container_a, field_info.name), + @field(container_b, field_info.name)); + if(!eql) return false; + }, + builtin.TypeId.Array => + { + const eql = mem.eql(Child(field_info.field_type), + @field(container_a, field_info.name), + @field(container_b, field_info.name)); + if(!eql) return false; + }, + else => + { + const eql = @field(container_a, field_info.name) + == @field(container_b, field_info.name); + if(!eql) return false; + }, + } + } + return true; + }, + builtin.TypeId.Union => + { + const info = @typeInfo(T).Union; + + if(info.tag_type) |_| + { + const tag_a = activeTag(container_a); + const tag_b = activeTag(container_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)) + { + switch(@typeId(field_info.field_type)) + { + builtin.TypeId.Struct, + builtin.TypeId.Union => + { + return containerEql(@field(container_a, field_info.name), + @field(container_b, field_info.name)); + }, + builtin.TypeId.Array => + { + return mem.eql(field_info.field_type, + @field(container_a, field_info.name)[0..], + @field(container_b, field_info.name)[0..]); + }, + else => + { + return @field(container_a, field_info.name) + == @field(container_b, field_info.name); + }, + } + } + } + unreachable; + } + @compileError("ContainerEql can only operate on tagged unions."); + }, + else => @compileError("ContainerEql requires struct or tagged union for comparison."), + }; +} + +test "std.meta.containerEql" +{ + 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(containerEql(&s_1, &s_3)); + debug.assert(!containerEql(&s_1, &s_2)); + debug.assert(containerEql(&u_1, &u_3)); + debug.assert(!containerEql(&u_1, &u_2)); +} \ No newline at end of file diff --git a/std/meta/trait.zig b/std/meta/trait.zig new file mode 100644 index 000000000000..cee4a3a3dbca --- /dev/null +++ b/std/meta/trait.zig @@ -0,0 +1,495 @@ +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 + { + comptime + { + for(traits) |t| + { + if(!t(T)) return false; + } + return true; + } + } + }; + return Closure.trait; +} + +test "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 + { + comptime + { + const T = meta.UnwrapContainer(_T); + 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, + }; + + for(defs) |def| + { + if(mem.eql(u8, def.name, name)) return def.is_pub; + } + + return false; + } + } + }; + return Closure.trait; +} + +test "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 + { + comptime + { + if(!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 "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 + { + comptime + { + const T = meta.UnwrapContainer(_T); + 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, + }; + + for(fields) |field| + { + if(mem.eql(u8, field.name, name)) return true; + } + + return false; + } + } + }; + return Closure.trait; +} + +test "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 + { + comptime + { + return id == @typeId(T); + } + } + }; + return Closure.trait; +} + +test "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 + { + comptime + { + if(!isSingleItemPtr(T)) return false; + return id == @typeId(meta.Child(T)); + } + } + }; + return Closure.trait; +} + +test "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 +{ + comptime + { + 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 "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 +{ + comptime + { + 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 "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 +{ + comptime + { + if(is(builtin.TypeId.Pointer)(T)) + { + const info = @typeInfo(T); + return info.Pointer.size == builtin.TypeInfo.Pointer.Size.One; + } + return false; + } +} + +test "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 +{ + comptime + { + if(is(builtin.TypeId.Pointer)(T)) + { + const info = @typeInfo(T); + return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Many; + } + return false; + } +} + +test "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 +{ + comptime + { + if(is(builtin.TypeId.Pointer)(T)) + { + const info = @typeInfo(T); + return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Slice; + } + return false; + } +} + +test "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 +{ + comptime + { + if(is(builtin.TypeId.Pointer)(T)) + { + const info = @typeInfo(T); + return info.Pointer.size != builtin.TypeInfo.Pointer.Size.One; + } + return is(builtin.TypeId.Array)(T); + } +} + +test "isIndexable" +{ + const array = []u8.{0} ** 10; + const slice = array[0..]; + + debug.assert(isIndexable(@typeOf(array))); + debug.assert(isIndexable(@typeOf(slice))); + debug.assert(!isIndexable(meta.Child(@typeOf(slice)))); +} + +/// + +pub fn isNumber(comptime T: type) bool +{ + comptime + { + return switch(@typeId(T)) + { + builtin.TypeId.Int, + builtin.TypeId.Float, + builtin.TypeId.ComptimeInt, + builtin.TypeId.ComptimeFloat => true, + else => false, + }; + } +} + +test "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 +{ + comptime + { + if(!is(builtin.TypeId.Pointer)(T)) return false; + const info = @typeInfo(T); + return info.Pointer.is_const; + } +} + +test "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 +{ + comptime + { + const info = @typeInfo(T); + return switch(info) + { + builtin.TypeId.Struct => true, + builtin.TypeId.Union => true, + builtin.TypeId.Enum => true, + else => false, + }; + } +} + +test "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 From 2dbab730cf7ef58be26e7b6a11fe0f4b06e3406c Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 18 Oct 2018 11:24:05 -0500 Subject: [PATCH 04/14] Changes based on review feedback --- std/meta/index.zig | 111 ++++----------------------------------------- 1 file changed, 10 insertions(+), 101 deletions(-) diff --git a/std/meta/index.zig b/std/meta/index.zig index debd098d708b..2b54a7177229 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -9,44 +9,6 @@ pub const trait = @import("trait.zig"); const TypeId = builtin.TypeId; const TypeInfo = builtin.TypeInfo; -///Given a container or a pointer to a container, returns the container type. -/// Otherwise it returns the type as-is. -pub fn UnwrapContainer(comptime T: type) type -{ - comptime - { - if(trait.isPtrTo(builtin.TypeId.Struct)(T) - or trait.isPtrTo(builtin.TypeId.Union)(T)) return Child(T); - return T; - } -} - -test "std.meta.UnwrapContainer" -{ - const x = u8(10); - debug.assert(UnwrapContainer(*u8) == *u8); - debug.assert(UnwrapContainer(u8) == u8); - - const S = struct. - { - f: u8, - }; - - const U = union. - { - a: u8, - b: u16, - }; - - debug.assert(UnwrapContainer(S) == S); - debug.assert(UnwrapContainer(*S) == S); - debug.assert(UnwrapContainer(**S) == **S); - - debug.assert(UnwrapContainer(U) == U); - debug.assert(UnwrapContainer(*U) == U); - debug.assert(UnwrapContainer(**U) == **U); -} - pub fn tagName(v: var) []const u8 { const T = @typeOf(v); switch (@typeInfo(T)) { @@ -391,10 +353,9 @@ test "std.meta.TagType" { ///Returns the active tag of a tagged union -pub fn activeTag(u: var) @TagType(UnwrapContainer(@typeOf(u))) +pub fn activeTag(u: var) @TagType(@typeOf(u)) { const T = @typeOf(u); - if(comptime trait.isSingleItemPtr(T)) return @TagType(Child(T))(u.*); return @TagType(T)(u); } @@ -420,59 +381,9 @@ test "std.meta.activeTag" } -//see: bytesOf -fn BytesReturnType(comptime P: type) type -{ - return if(trait.isConstPtr(P)) []const u8 else []u8; -} -///Given a pointer to a single item, returns a slice of the underlying bytes, preserving the -/// pointer's const-ness. -pub fn bytes(ptr: var) BytesReturnType(@typeOf(ptr)) -{ - const P = @typeOf(ptr); - if(comptime !trait.isSingleItemPtr(P)) @compileError("meta.bytes requires a single item " - ++ "pointer, passed " ++ @typeName(P)); - const T = Child(P); - - const ReturnType = comptime BytesReturnType(P); - const IntermediateType = comptime (if(trait.isConstPtr(P)) [*]const u8 else [*]u8); - - var slice: ReturnType = undefined; - slice.ptr = @ptrCast(IntermediateType, ptr); - slice.len = @sizeOf(T); - return @sliceToBytes(slice); -} - -test "std.meta.bytes" -{ - 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, bytes(&deadbeef), deadbeef_bytes)); - - var codeface = u32(0xC0DEFACE); - for(bytes(&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, bytes(&inst), "\xBE\xEF\xDE\xA1")); -} - -/// Provides the size of a type padded to the nearest byte, instead of however the -/// compiler actually pads it. Useful for determining the size of a type as it would -/// likely be stored to disk. +///Provides the size of a type padded to the nearest byte, instead of however the +/// compiler actually pads it, ignoring the actual memory layout. Useful for determining the size +/// of a type as it would likely be stored to disk or writen to a socket. pub fn byteAlignedSizeOf(comptime T: type) usize { comptime @@ -560,11 +471,9 @@ test "std.meta.byteAlignedSizeOf" ///Compares two structs or (tagged) unions for equality on a field level /// so that padding bytes are not relevant. -pub fn containerEql(container_a: var, container_b: var) bool +pub fn containerEql(container_a: var, container_b: @typeOf(container_a)) bool { - const T = UnwrapContainer(@typeOf(container_a)); - debug.assert(UnwrapContainer(@typeOf(container_b)) == T); - + const T = @typeOf(container_a); return switch(@typeId(T)) { builtin.TypeId.Struct => @@ -684,8 +593,8 @@ test "std.meta.containerEql" const u_2 = U.{ .s = s_1, }; const u_3 = U.{ .f = 24, }; - debug.assert(containerEql(&s_1, &s_3)); - debug.assert(!containerEql(&s_1, &s_2)); - debug.assert(containerEql(&u_1, &u_3)); - debug.assert(!containerEql(&u_1, &u_2)); + debug.assert(containerEql(s_1, s_3)); + debug.assert(!containerEql(s_1, s_2)); + debug.assert(containerEql(u_1, u_3)); + debug.assert(!containerEql(u_1, u_2)); } \ No newline at end of file From fd33c34f3fb98beb396ddb0c1252f68629d649b6 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 18 Oct 2018 12:11:57 -0500 Subject: [PATCH 05/14] Removed internal use of "UnwrapContainer". Added *[N]T as indexable. --- std/meta/index.zig | 33 ++++++++++++++++++++++++++++ std/meta/trait.zig | 54 ++++++++++++++++++++++------------------------ 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/std/meta/index.zig b/std/meta/index.zig index 2b54a7177229..7e04fdcb4b48 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -9,6 +9,39 @@ pub const trait = @import("trait.zig"); const TypeId = builtin.TypeId; const TypeInfo = builtin.TypeInfo; +pub fn UnwrapContainer(comptime T: type) type +{ + if(trait.isPtrTo(builtin.TypeId.Struct)(T) + or trait.isPtrTo(builtin.TypeId.Union)(T)) return T.Child; + return T; +} + +test "UnwrapContainer" +{ + const x = u8(10); + debug.assert(UnwrapContainer(*u8) == *u8); + debug.assert(UnwrapContainer(u8) == u8); + + const S = struct. + { + f: u8, + }; + + const U = union. + { + a: u8, + b: u16, + }; + + debug.assert(UnwrapContainer(S) == S); + debug.assert(UnwrapContainer(*S) == S); + debug.assert(UnwrapContainer(**S) == **S); + + debug.assert(UnwrapContainer(U) == U); + debug.assert(UnwrapContainer(*U) == U); + debug.assert(UnwrapContainer(**U) == **U); +} + pub fn tagName(v: var) []const u8 { const T = @typeOf(v); switch (@typeInfo(T)) { diff --git a/std/meta/trait.zig b/std/meta/trait.zig index cee4a3a3dbca..307a112aa40d 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -13,8 +13,6 @@ fn traitFnWorkaround(comptime T: type) bool return false; } -/// - pub const TraitFn = @typeOf(traitFnWorkaround); /// @@ -31,10 +29,7 @@ pub fn multiTrait(comptime traits: TraitList) TraitFn { comptime { - for(traits) |t| - { - if(!t(T)) return false; - } + for(traits) |t| if(!t(T)) return false; return true; } } @@ -42,7 +37,7 @@ pub fn multiTrait(comptime traits: TraitList) TraitFn return Closure.trait; } -test "multiTrait" +test "std.trait.multiTrait" { const Vector2 = struct. { @@ -80,11 +75,10 @@ pub fn hasDef(comptime name: []const u8) TraitFn { const Closure = struct. { - pub fn trait(comptime _T: type) bool + pub fn trait(comptime T: type) bool { comptime { - const T = meta.UnwrapContainer(_T); const info = @typeInfo(T); const defs = switch(info) { @@ -106,7 +100,7 @@ pub fn hasDef(comptime name: []const u8) TraitFn return Closure.trait; } -test "hasDef" +test "std.trait.hasDef" { const TestStruct = struct. { @@ -120,7 +114,7 @@ test "hasDef" debug.assert(hasDef("value")(TestStruct)); debug.assert(!hasDef("value")(TestStructFail)); - debug.assert(hasDef("value")(*TestStruct)); + debug.assert(!hasDef("value")(*TestStruct)); debug.assert(!hasDef("value")(**TestStructFail)); debug.assert(!hasDef("x")(TestStruct)); debug.assert(!hasDef("value")(u8)); @@ -145,7 +139,7 @@ pub fn hasFn(comptime name: []const u8) TraitFn return Closure.trait; } -test "hasFn" +test "std.trait.hasFn" { const TestStruct = struct. { @@ -162,11 +156,10 @@ pub fn hasField(comptime name: []const u8) TraitFn { const Closure = struct. { - pub fn trait(comptime _T: type) bool + pub fn trait(comptime T: type) bool { comptime { - const T = meta.UnwrapContainer(_T); const info = @typeInfo(T); const fields = switch(info) { @@ -188,7 +181,7 @@ pub fn hasField(comptime name: []const u8) TraitFn return Closure.trait; } -test "hasField" +test "std.trait.hasField" { const TestStruct = struct. { @@ -196,7 +189,7 @@ test "hasField" }; debug.assert(hasField("value")(TestStruct)); - 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)); @@ -219,7 +212,7 @@ pub fn is(comptime id: builtin.TypeId) TraitFn return Closure.trait; } -test "is" +test "std.trait.is" { debug.assert(is(builtin.TypeId.Int)(u8)); debug.assert(!is(builtin.TypeId.Int)(f32)); @@ -246,7 +239,7 @@ pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn return Closure.trait; } -test "isPtrTo" +test "std.trait.isPtrTo" { debug.assert(!isPtrTo(builtin.TypeId.Struct)(struct.{})); debug.assert(isPtrTo(builtin.TypeId.Struct)(*struct.{})); @@ -275,7 +268,7 @@ pub fn isExtern(comptime T: type) bool } } -test "isExtern" +test "std.trait.isExtern" { const TestExStruct = extern struct.{}; const TestStruct = struct.{}; @@ -303,7 +296,7 @@ pub fn isPacked(comptime T: type) bool } } -test "isPacked" +test "std.trait.isPacked" { const TestPStruct = packed struct.{}; const TestStruct = struct.{}; @@ -328,7 +321,7 @@ pub fn isSingleItemPtr(comptime T: type) bool } } -test "isSingleItemPtr" +test "std.trait.isSingleItemPtr" { const array = []u8.{0} ** 10; debug.assert(isSingleItemPtr(@typeOf(&array[0]))); @@ -351,7 +344,7 @@ pub fn isManyItemPtr(comptime T: type) bool } } -test "isManyItemPtr" +test "std.trait.isManyItemPtr" { const array = []u8.{0} ** 10; const mip = @ptrCast([*]const u8, &array[0]); @@ -375,7 +368,7 @@ pub fn isSlice(comptime T: type) bool } } -test "isSlice" +test "std.trait.isSlice" { const array = []u8.{0} ** 10; debug.assert(isSlice(@typeOf(array[0..]))); @@ -392,13 +385,18 @@ pub fn isIndexable(comptime T: type) bool if(is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); - return info.Pointer.size != builtin.TypeInfo.Pointer.Size.One; + if(info.Pointer.size == builtin.TypeInfo.Pointer.Size.One) + { + if(is(builtin.TypeId.Array)(meta.Child(T))) return true; + return false; + } + return true; } return is(builtin.TypeId.Array)(T); } } -test "isIndexable" +test "std.trait.isIndexable" { const array = []u8.{0} ** 10; const slice = array[0..]; @@ -425,7 +423,7 @@ pub fn isNumber(comptime T: type) bool } } -test "isNumber" +test "std.trait.isNumber" { const NotANumber = struct. { @@ -453,7 +451,7 @@ pub fn isConstPtr(comptime T: type) bool } } -test "isConstPtr" +test "std.trait.isConstPtr" { var t = u8(0); const c = u8(0); @@ -480,7 +478,7 @@ pub fn isContainer(comptime T: type) bool } } -test "isContainer" +test "std.trait.isContainer" { const TestStruct = struct.{}; const TestUnion = union.{ a: void, }; From aba87ffd38617faf0afdc2500f39f098ce3776e8 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 18 Oct 2018 12:14:10 -0500 Subject: [PATCH 06/14] Added test case for indexable *[N]T --- std/meta/trait.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/std/meta/trait.zig b/std/meta/trait.zig index 307a112aa40d..a71d152246d6 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -402,6 +402,7 @@ test "std.trait.isIndexable" 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)))); } From 6ddf6d7929c4e765585679f041cf68025d4dc00c Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 18 Oct 2018 14:05:50 -0500 Subject: [PATCH 07/14] Removed byteAlignedSizeOf, removed comptime blocks --- std/meta/index.zig | 90 +----------------- std/meta/trait.zig | 227 ++++++++++++++++++--------------------------- 2 files changed, 92 insertions(+), 225 deletions(-) diff --git a/std/meta/index.zig b/std/meta/index.zig index 7e04fdcb4b48..cfd9cb2db34c 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -143,7 +143,7 @@ pub fn Child(comptime T: type) type { 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," + else => @compileError("Expected promise, pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"), }; } @@ -414,94 +414,6 @@ test "std.meta.activeTag" } -///Provides the size of a type padded to the nearest byte, instead of however the -/// compiler actually pads it, ignoring the actual memory layout. Useful for determining the size -/// of a type as it would likely be stored to disk or writen to a socket. -pub fn byteAlignedSizeOf(comptime T: type) usize -{ - comptime - { - switch(@typeId(T)) - { - builtin.TypeId.Bool, - builtin.TypeId.Int, - builtin.TypeId.Float => return @sizeOf(T), - builtin.TypeId.Enum => return @sizeOf(@TagType(T)), - builtin.TypeId.Array => - { - return byteAlignedSizeOf(Child(T)) * T.len; - }, - builtin.TypeId.Struct => - { - const info = @typeInfo(T).Struct; - - if(info.layout == builtin.TypeInfo.ContainerLayout.Packed) return @sizeOf(T); - - var size = usize(0); - for(info.fields) |field| - { - size += byteAlignedSizeOf(field.field_type); - } - return size; - }, - builtin.TypeId.Union => - { - const info = @typeInfo(T).Union; - var largest = usize(0); - for(info.fields) |field| - { - const size = byteAlignedSizeOf(field.field_type); - if(size > largest) largest = size; - } - return largest; - }, - else => return 0, - } - } -} - -test "std.meta.byteAlignedSizeOf" -{ - const UnpackedS = struct. - { - a: u24, - b: u8, - }; - - debug.assert(byteAlignedSizeOf(UnpackedS) == 4); - - const UnpackedWithSub = struct. - { - a: u8, - b: u8, - unpacked_s: UnpackedS, - }; - - debug.assert(byteAlignedSizeOf(UnpackedWithSub) == 4 + 2); - - const PackedS = packed struct. - { - b0: bool, - b1: bool, - b2: bool, - b3: bool, - b4: bool, - b5: bool, - b6: bool, - b7: bool, - }; - - const UnpackedWithPackedSub = struct. - { - a: u8, - b: u8, - unpacked_s: UnpackedS, - packed_s: PackedS, - }; - - debug.assert(byteAlignedSizeOf(UnpackedWithPackedSub) == 4 + 2 + 1); -} - ///Compares two structs or (tagged) unions for equality on a field level /// so that padding bytes are not relevant. pub fn containerEql(container_a: var, container_b: @typeOf(container_a)) bool diff --git a/std/meta/trait.zig b/std/meta/trait.zig index a71d152246d6..17cf23173714 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -27,11 +27,8 @@ pub fn multiTrait(comptime traits: TraitList) TraitFn { pub fn trait(comptime T: type) bool { - comptime - { - for(traits) |t| if(!t(T)) return false; - return true; - } + inline for(traits) |t| if(!t(T)) return false; + return true; } }; return Closure.trait; @@ -77,24 +74,21 @@ pub fn hasDef(comptime name: []const u8) TraitFn { pub fn trait(comptime T: type) bool { - comptime + const info = @typeInfo(T); + const defs = switch(info) { - 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, - }; - - for(defs) |def| - { - if(mem.eql(u8, def.name, name)) return def.is_pub; - } - - return false; + 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; @@ -127,13 +121,10 @@ pub fn hasFn(comptime name: []const u8) TraitFn { pub fn trait(comptime T: type) bool { - comptime - { - if(!hasDef(name)(T)) return false; - const DefType = @typeOf(@field(T, name)); - const def_type_id = @typeId(DefType); - return def_type_id == builtin.TypeId.Fn; - } + 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; @@ -158,24 +149,21 @@ pub fn hasField(comptime name: []const u8) TraitFn { pub fn trait(comptime T: type) bool { - comptime + const info = @typeInfo(T); + const fields = switch(info) { - 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, - }; - - for(fields) |field| - { - if(mem.eql(u8, field.name, name)) return true; - } - - return false; + 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; @@ -203,10 +191,7 @@ pub fn is(comptime id: builtin.TypeId) TraitFn { pub fn trait(comptime T: type) bool { - comptime - { - return id == @typeId(T); - } + return id == @typeId(T); } }; return Closure.trait; @@ -229,11 +214,8 @@ pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn { pub fn trait(comptime T: type) bool { - comptime - { - if(!isSingleItemPtr(T)) return false; - return id == @typeId(meta.Child(T)); - } + if(!comptime isSingleItemPtr(T)) return false; + return id == @typeId(meta.Child(T)); } }; return Closure.trait; @@ -254,18 +236,15 @@ test "std.trait.isPtrTo" // Fns yet. Should be isExternType? pub fn isExtern(comptime T: type) bool { - comptime + const Extern = builtin.TypeInfo.ContainerLayout.Extern; + const info = @typeInfo(T); + return switch(info) { - 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, - }; - } + 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.trait.isExtern" @@ -282,18 +261,15 @@ test "std.trait.isExtern" pub fn isPacked(comptime T: type) bool { - comptime + const Packed = builtin.TypeInfo.ContainerLayout.Packed; + const info = @typeInfo(T); + return switch(info) { - 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, - }; - } + 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.trait.isPacked" @@ -310,15 +286,12 @@ test "std.trait.isPacked" pub fn isSingleItemPtr(comptime T: type) bool { - comptime + if(comptime is(builtin.TypeId.Pointer)(T)) { - if(is(builtin.TypeId.Pointer)(T)) - { - const info = @typeInfo(T); - return info.Pointer.size == builtin.TypeInfo.Pointer.Size.One; - } - return false; + const info = @typeInfo(T); + return info.Pointer.size == builtin.TypeInfo.Pointer.Size.One; } + return false; } test "std.trait.isSingleItemPtr" @@ -333,15 +306,12 @@ test "std.trait.isSingleItemPtr" pub fn isManyItemPtr(comptime T: type) bool { - comptime + if(comptime is(builtin.TypeId.Pointer)(T)) { - if(is(builtin.TypeId.Pointer)(T)) - { - const info = @typeInfo(T); - return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Many; - } - return false; + const info = @typeInfo(T); + return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Many; } + return false; } test "std.trait.isManyItemPtr" @@ -357,15 +327,12 @@ test "std.trait.isManyItemPtr" pub fn isSlice(comptime T: type) bool { - comptime + if(comptime is(builtin.TypeId.Pointer)(T)) { - if(is(builtin.TypeId.Pointer)(T)) - { - const info = @typeInfo(T); - return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Slice; - } - return false; + const info = @typeInfo(T); + return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Slice; } + return false; } test "std.trait.isSlice" @@ -380,20 +347,17 @@ test "std.trait.isSlice" pub fn isIndexable(comptime T: type) bool { - comptime - { - if(is(builtin.TypeId.Pointer)(T)) - { - const info = @typeInfo(T); - if(info.Pointer.size == builtin.TypeInfo.Pointer.Size.One) - { - if(is(builtin.TypeId.Array)(meta.Child(T))) return true; - return false; - } - return true; - } - return is(builtin.TypeId.Array)(T); - } + 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.trait.isIndexable" @@ -411,17 +375,14 @@ test "std.trait.isIndexable" pub fn isNumber(comptime T: type) bool { - comptime + return switch(@typeId(T)) { - return switch(@typeId(T)) - { - builtin.TypeId.Int, - builtin.TypeId.Float, - builtin.TypeId.ComptimeInt, - builtin.TypeId.ComptimeFloat => true, - else => false, - }; - } + builtin.TypeId.Int, + builtin.TypeId.Float, + builtin.TypeId.ComptimeInt, + builtin.TypeId.ComptimeFloat => true, + else => false, + }; } test "std.trait.isNumber" @@ -444,12 +405,9 @@ test "std.trait.isNumber" pub fn isConstPtr(comptime T: type) bool { - comptime - { - if(!is(builtin.TypeId.Pointer)(T)) return false; - const info = @typeInfo(T); - return info.Pointer.is_const; - } + if(!comptime is(builtin.TypeId.Pointer)(T)) return false; + const info = @typeInfo(T); + return info.Pointer.is_const; } test "std.trait.isConstPtr" @@ -466,17 +424,14 @@ test "std.trait.isConstPtr" pub fn isContainer(comptime T: type) bool { - comptime + const info = @typeInfo(T); + return switch(info) { - const info = @typeInfo(T); - return switch(info) - { - builtin.TypeId.Struct => true, - builtin.TypeId.Union => true, - builtin.TypeId.Enum => true, - else => false, - }; - } + builtin.TypeId.Struct => true, + builtin.TypeId.Union => true, + builtin.TypeId.Enum => true, + else => false, + }; } test "std.trait.isContainer" From 6ea5c805cfa5f59493119d6266953619a5354a1e Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 18 Oct 2018 14:54:13 -0500 Subject: [PATCH 08/14] Add meta to std/index.zig --- std/index.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/std/index.zig b/std/index.zig index 18489969df69..54f5eb6ff888 100644 --- a/std/index.zig +++ b/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"); From bd3b34e27cc1b0a079266c69886219f1eb908cba Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 19 Oct 2018 08:29:57 -0500 Subject: [PATCH 09/14] meta.alignment changes --- std/meta/index.zig | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/std/meta/index.zig b/std/meta/index.zig index cfd9cb2db34c..2f062358b54c 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -110,7 +110,7 @@ test "std.meta.tagName" { debug.assert(mem.eql(u8, tagName(u2b), "D")); } -pub fn bitCount(comptime T: type) u8 { +pub fn bitCount(comptime T: type) u32 { return switch (@typeInfo(T)) { TypeId.Int => |info| info.bits, TypeId.Float => |info| info.bits, @@ -123,11 +123,10 @@ test "std.meta.bitCount" { debug.assert(bitCount(f32) == 32); } -pub fn alignment(comptime T: type) u32 { - return switch (@typeInfo(T)) { - TypeId.Pointer => |info| info.alignment, - else => @compileError("Expected pointer type, found '" ++ @typeName(T) ++ "'"), - }; +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" { From 4be1a895bdfae63a37f6fe30309399f6c61e4110 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 19 Oct 2018 12:22:22 -0500 Subject: [PATCH 10/14] Genericized meta.eql, added std.mem byte functions --- std/mem.zig | 158 +++++++++++++++++++++++++++++++++++++++++++++ std/meta/index.zig | 145 ++++++++++++++++++++++++----------------- 2 files changed, 244 insertions(+), 59 deletions(-) diff --git a/std/mem.zig b/std/mem.zig index 908d54e02b6d..690c501b0453 100644 --- a/std/mem.zig +++ b/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,159 @@ 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))); + return if(comptime trait.isConstPtr(P)) *const [size]u8 else *[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).*; +} + +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)); + } + + return if(comptime trait.isConstPtr(B)) *const T else *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)), + @alignCast(comptime meta.alignment(T), 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 SubArrayReturnType(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) + SubArrayReturnType(@typeOf(ptr), length) +{ + debug.assert(start + length <= ptr.*.len); + + const ReturnType = SubArrayReturnType(@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 diff --git a/std/meta/index.zig b/std/meta/index.zig index 2f062358b54c..6b1dfe5ba796 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -130,6 +130,7 @@ pub fn alignment(comptime T: type) u29 { } 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); @@ -413,12 +414,13 @@ test "std.meta.activeTag" } -///Compares two structs or (tagged) unions for equality on a field level -/// so that padding bytes are not relevant. -pub fn containerEql(container_a: var, container_b: @typeOf(container_a)) bool +///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(container_a); - return switch(@typeId(T)) + const T = @typeOf(a); + + switch(@typeId(T)) { builtin.TypeId.Struct => { @@ -426,40 +428,30 @@ pub fn containerEql(container_a: var, container_b: @typeOf(container_a)) bool inline for(info.fields) |field_info| { - switch(@typeId(field_info.field_type)) - { - builtin.TypeId.Struct, - builtin.TypeId.Union => - { - const eql = containerEql(@field(container_a, field_info.name), - @field(container_b, field_info.name)); - if(!eql) return false; - }, - builtin.TypeId.Array => - { - const eql = mem.eql(Child(field_info.field_type), - @field(container_a, field_info.name), - @field(container_b, field_info.name)); - if(!eql) return false; - }, - else => - { - const eql = @field(container_a, field_info.name) - == @field(container_b, field_info.name); - if(!eql) return false; - }, - } + 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(container_a); - const tag_b = activeTag(container_b); + const tag_a = activeTag(a); + const tag_b = activeTag(b); if(tag_a != tag_b) return false; inline for(info.fields) |field_info| @@ -467,37 +459,39 @@ pub fn containerEql(container_a: var, container_b: @typeOf(container_a)) bool const enum_field = field_info.enum_field.?; if(enum_field.value == @enumToInt(tag_a)) { - switch(@typeId(field_info.field_type)) - { - builtin.TypeId.Struct, - builtin.TypeId.Union => - { - return containerEql(@field(container_a, field_info.name), - @field(container_b, field_info.name)); - }, - builtin.TypeId.Array => - { - return mem.eql(field_info.field_type, - @field(container_a, field_info.name)[0..], - @field(container_b, field_info.name)[0..]); - }, - else => - { - return @field(container_a, field_info.name) - == @field(container_b, field_info.name); - }, - } + return eql(@field(a, enum_field.name), + @field(b, enum_field.name)); } } - unreachable; + return false; } - @compileError("ContainerEql can only operate on tagged unions."); + + //This is the only reasonable way to handle an untagged union, + // but it will report a false negative in many circumstances. + return std.mem.eql(u8, mem.asBytes(&a), mem.asBytes(&b)); }, - else => @compileError("ContainerEql requires struct or tagged union for comparison."), - }; + 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.containerEql" + +test "std.meta.eql" { const S = struct. { @@ -537,8 +531,41 @@ test "std.meta.containerEql" const u_2 = U.{ .s = s_1, }; const u_3 = U.{ .f = 24, }; - debug.assert(containerEql(s_1, s_3)); - debug.assert(!containerEql(s_1, s_2)); - debug.assert(containerEql(u_1, u_3)); - debug.assert(!containerEql(u_1, u_2)); + 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))); + + const UExt = extern union. + { + i: i32, + f: f32, + }; + + const uext1 = UExt.{ .i = 8675, }; + const uext2 = UExt.{ .f = @bitCast(f32, i32(8675)), }; + + debug.assert(eql(uext1, uext2)); } \ No newline at end of file From fa8d2a73620926b97daa84a405bdb69bc4d279e2 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 19 Oct 2018 12:23:35 -0500 Subject: [PATCH 11/14] Removed UnwrapContainer as no longer necessary and probably useless. --- std/meta/index.zig | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/std/meta/index.zig b/std/meta/index.zig index 6b1dfe5ba796..c6d0c9d112db 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -9,39 +9,6 @@ pub const trait = @import("trait.zig"); const TypeId = builtin.TypeId; const TypeInfo = builtin.TypeInfo; -pub fn UnwrapContainer(comptime T: type) type -{ - if(trait.isPtrTo(builtin.TypeId.Struct)(T) - or trait.isPtrTo(builtin.TypeId.Union)(T)) return T.Child; - return T; -} - -test "UnwrapContainer" -{ - const x = u8(10); - debug.assert(UnwrapContainer(*u8) == *u8); - debug.assert(UnwrapContainer(u8) == u8); - - const S = struct. - { - f: u8, - }; - - const U = union. - { - a: u8, - b: u16, - }; - - debug.assert(UnwrapContainer(S) == S); - debug.assert(UnwrapContainer(*S) == S); - debug.assert(UnwrapContainer(**S) == **S); - - debug.assert(UnwrapContainer(U) == U); - debug.assert(UnwrapContainer(*U) == U); - debug.assert(UnwrapContainer(**U) == **U); -} - pub fn tagName(v: var) []const u8 { const T = @typeOf(v); switch (@typeInfo(T)) { From 741b733fefbec4696034ef1feff77530e24a0a1f Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 19 Oct 2018 12:53:17 -0500 Subject: [PATCH 12/14] compile error when calling meta.eql on untagged unions --- std/index.zig | 1 + std/meta/index.zig | 15 +-------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/std/index.zig b/std/index.zig index 54f5eb6ff888..55ad016bb1f4 100644 --- a/std/index.zig +++ b/std/index.zig @@ -75,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"); diff --git a/std/meta/index.zig b/std/meta/index.zig index c6d0c9d112db..5bbbc1f9ae1a 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -433,9 +433,7 @@ pub fn eql(a: var, b: @typeOf(a)) bool return false; } - //This is the only reasonable way to handle an untagged union, - // but it will report a false negative in many circumstances. - return std.mem.eql(u8, mem.asBytes(&a), mem.asBytes(&b)); + @compileError("cannot compare untagged union type " ++ @typeName(T)); }, builtin.TypeId.Array => { @@ -524,15 +522,4 @@ test "std.meta.eql" 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))); - - const UExt = extern union. - { - i: i32, - f: f32, - }; - - const uext1 = UExt.{ .i = 8675, }; - const uext2 = UExt.{ .f = @bitCast(f32, i32(8675)), }; - - debug.assert(eql(uext1, uext2)); } \ No newline at end of file From c3f64fb91e0cd608926660bece29125418d8fe80 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 19 Oct 2018 14:24:30 -0500 Subject: [PATCH 13/14] Altered asBytes and bytesAsValue to have more appropriate alignment. Removed to* variations as they are trivially replicated with 2 characters at the as* callsites. --- std/mem.zig | 40 ++++++++-------------------------------- std/meta/trait.zig | 30 +++++++++++++++--------------- 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/std/mem.zig b/std/mem.zig index 690c501b0453..e7fa1d11e392 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -874,7 +874,9 @@ fn AsBytesReturnType(comptime P: type) type ++ "pointer, passed " ++ @typeName(P)); const size = usize(@sizeOf(meta.Child(P))); - return if(comptime trait.isConstPtr(P)) *const [size]u8 else *[size]u8; + 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. @@ -911,12 +913,6 @@ test "std.mem.asBytes" 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).*; -} - fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { const size = usize(@sizeOf(T)); @@ -926,15 +922,14 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type @compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B)); } - return if(comptime trait.isConstPtr(B)) *const T else *T; + return if(comptime trait.isConstPtr(B)) *align(1) const T else *align(1) 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)), - @alignCast(comptime meta.alignment(T), bytes)); + return @ptrCast(BytesAsValueReturnType(T, @typeOf(bytes)), bytes); } test "std.mem.bytesAsValue" @@ -972,26 +967,7 @@ test "std.mem.bytesAsValue" 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 SubArrayReturnType(comptime T: type, comptime length: usize) type +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)); @@ -999,11 +975,11 @@ fn SubArrayReturnType(comptime T: type, comptime length: usize) type ///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) - SubArrayReturnType(@typeOf(ptr), length) + SubArrayPtrReturnType(@typeOf(ptr), length) { debug.assert(start + length <= ptr.*.len); - const ReturnType = SubArrayReturnType(@typeOf(ptr), length); + const ReturnType = SubArrayPtrReturnType(@typeOf(ptr), length); const T = meta.Child(meta.Child(@typeOf(ptr))); return @ptrCast(ReturnType, &ptr[start]); } diff --git a/std/meta/trait.zig b/std/meta/trait.zig index 17cf23173714..4f5ab801d796 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -34,7 +34,7 @@ pub fn multiTrait(comptime traits: TraitList) TraitFn return Closure.trait; } -test "std.trait.multiTrait" +test "std.meta.trait.multiTrait" { const Vector2 = struct. { @@ -94,7 +94,7 @@ pub fn hasDef(comptime name: []const u8) TraitFn return Closure.trait; } -test "std.trait.hasDef" +test "std.meta.trait.hasDef" { const TestStruct = struct. { @@ -130,7 +130,7 @@ pub fn hasFn(comptime name: []const u8) TraitFn return Closure.trait; } -test "std.trait.hasFn" +test "std.meta.trait.hasFn" { const TestStruct = struct. { @@ -169,7 +169,7 @@ pub fn hasField(comptime name: []const u8) TraitFn return Closure.trait; } -test "std.trait.hasField" +test "std.meta.trait.hasField" { const TestStruct = struct. { @@ -197,7 +197,7 @@ pub fn is(comptime id: builtin.TypeId) TraitFn return Closure.trait; } -test "std.trait.is" +test "std.meta.trait.is" { debug.assert(is(builtin.TypeId.Int)(u8)); debug.assert(!is(builtin.TypeId.Int)(f32)); @@ -221,7 +221,7 @@ pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn return Closure.trait; } -test "std.trait.isPtrTo" +test "std.meta.trait.isPtrTo" { debug.assert(!isPtrTo(builtin.TypeId.Struct)(struct.{})); debug.assert(isPtrTo(builtin.TypeId.Struct)(*struct.{})); @@ -247,7 +247,7 @@ pub fn isExtern(comptime T: type) bool }; } -test "std.trait.isExtern" +test "std.meta.trait.isExtern" { const TestExStruct = extern struct.{}; const TestStruct = struct.{}; @@ -272,7 +272,7 @@ pub fn isPacked(comptime T: type) bool }; } -test "std.trait.isPacked" +test "std.meta.trait.isPacked" { const TestPStruct = packed struct.{}; const TestStruct = struct.{}; @@ -294,7 +294,7 @@ pub fn isSingleItemPtr(comptime T: type) bool return false; } -test "std.trait.isSingleItemPtr" +test "std.meta.trait.isSingleItemPtr" { const array = []u8.{0} ** 10; debug.assert(isSingleItemPtr(@typeOf(&array[0]))); @@ -314,7 +314,7 @@ pub fn isManyItemPtr(comptime T: type) bool return false; } -test "std.trait.isManyItemPtr" +test "std.meta.trait.isManyItemPtr" { const array = []u8.{0} ** 10; const mip = @ptrCast([*]const u8, &array[0]); @@ -335,7 +335,7 @@ pub fn isSlice(comptime T: type) bool return false; } -test "std.trait.isSlice" +test "std.meta.trait.isSlice" { const array = []u8.{0} ** 10; debug.assert(isSlice(@typeOf(array[0..]))); @@ -360,7 +360,7 @@ pub fn isIndexable(comptime T: type) bool return comptime is(builtin.TypeId.Array)(T); } -test "std.trait.isIndexable" +test "std.meta.trait.isIndexable" { const array = []u8.{0} ** 10; const slice = array[0..]; @@ -385,7 +385,7 @@ pub fn isNumber(comptime T: type) bool }; } -test "std.trait.isNumber" +test "std.meta.trait.isNumber" { const NotANumber = struct. { @@ -410,7 +410,7 @@ pub fn isConstPtr(comptime T: type) bool return info.Pointer.is_const; } -test "std.trait.isConstPtr" +test "std.meta.trait.isConstPtr" { var t = u8(0); const c = u8(0); @@ -434,7 +434,7 @@ pub fn isContainer(comptime T: type) bool }; } -test "std.trait.isContainer" +test "std.meta.trait.isContainer" { const TestStruct = struct.{}; const TestUnion = union.{ a: void, }; From 31eb0685b330e329dcd69e23bd32e938fb68673f Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 19 Oct 2018 14:49:42 -0500 Subject: [PATCH 14/14] Was convinced to re-add to* variations for convenience & saftey. Made bytesAsValue alignment match alignment of byte buffer. I'm not 100% sure that's actually a good idea though. --- std/mem.zig | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/std/mem.zig b/std/mem.zig index e7fa1d11e392..ca93e0d2d1ec 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -913,6 +913,30 @@ test "std.mem.asBytes" 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)); @@ -922,7 +946,9 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type @compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B)); } - return if(comptime trait.isConstPtr(B)) *align(1) const T else *align(1) T; + 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 @@ -967,6 +993,25 @@ test "std.mem.bytesAsValue" 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));