diff --git a/CMakeLists.txt b/CMakeLists.txt index 1717f8f816..18e627b641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,7 +290,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/mem/Allocator.zig" "${CMAKE_SOURCE_DIR}/lib/std/meta.zig" "${CMAKE_SOURCE_DIR}/lib/std/meta/trailer_flags.zig" - "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig" "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig" "${CMAKE_SOURCE_DIR}/lib/std/os.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index b82a037e14..015c554082 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -4,8 +4,6 @@ const assert = debug.assert; const testing = std.testing; const math = std.math; const mem = std.mem; -const meta = std.meta; -const trait = meta.trait; const autoHash = std.hash.autoHash; const Wyhash = std.hash.Wyhash; const Allocator = mem.Allocator; @@ -2341,13 +2339,13 @@ test "reIndex" { test "auto store_hash" { const HasCheapEql = AutoArrayHashMap(i32, i32); const HasExpensiveEql = AutoArrayHashMap([32]i32, i32); - try testing.expect(meta.fieldInfo(HasCheapEql.Data, .hash).type == void); - try testing.expect(meta.fieldInfo(HasExpensiveEql.Data, .hash).type != void); + try testing.expect(std.meta.fieldInfo(HasCheapEql.Data, .hash).type == void); + try testing.expect(std.meta.fieldInfo(HasExpensiveEql.Data, .hash).type != void); const HasCheapEqlUn = AutoArrayHashMapUnmanaged(i32, i32); const HasExpensiveEqlUn = AutoArrayHashMapUnmanaged([32]i32, i32); - try testing.expect(meta.fieldInfo(HasCheapEqlUn.Data, .hash).type == void); - try testing.expect(meta.fieldInfo(HasExpensiveEqlUn.Data, .hash).type != void); + try testing.expect(std.meta.fieldInfo(HasCheapEqlUn.Data, .hash).type == void); + try testing.expect(std.meta.fieldInfo(HasExpensiveEqlUn.Data, .hash).type != void); } test "sort" { @@ -2434,12 +2432,12 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K) return struct { fn hash(ctx: Context, key: K) u32 { _ = ctx; - if (comptime trait.hasUniqueRepresentation(K)) { - return @as(u32, @truncate(Wyhash.hash(0, std.mem.asBytes(&key)))); + if (std.meta.hasUniqueRepresentation(K)) { + return @truncate(Wyhash.hash(0, std.mem.asBytes(&key))); } else { var hasher = Wyhash.init(0); autoHash(&hasher, key); - return @as(u32, @truncate(hasher.final())); + return @truncate(hasher.final()); } } }.hash; @@ -2450,7 +2448,7 @@ pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K fn eql(ctx: Context, a: K, b: K, b_index: usize) bool { _ = b_index; _ = ctx; - return meta.eql(a, b); + return std.meta.eql(a, b); } }.eql; } diff --git a/lib/std/atomic/Atomic.zig b/lib/std/atomic/Atomic.zig index 892e9633d5..33610d556c 100644 --- a/lib/std/atomic/Atomic.zig +++ b/lib/std/atomic/Atomic.zig @@ -153,163 +153,155 @@ pub fn Atomic(comptime T: type) type { return @atomicRmw(T, &self.value, op, value, ordering); } - fn exportWhen(comptime condition: bool, comptime functions: type) type { - return if (condition) functions else struct {}; + pub inline fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.Add, value, ordering); } - pub usingnamespace exportWhen(std.meta.trait.isNumber(T), struct { - pub inline fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.Add, value, ordering); + pub inline fn fetchSub(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.Sub, value, ordering); + } + + pub inline fn fetchMin(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.Min, value, ordering); + } + + pub inline fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.Max, value, ordering); + } + + pub inline fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.And, value, ordering); + } + + pub inline fn fetchNand(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.Nand, value, ordering); + } + + pub inline fn fetchOr(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.Or, value, ordering); + } + + pub inline fn fetchXor(self: *Self, value: T, comptime ordering: Ordering) T { + return self.rmw(.Xor, value, ordering); + } + + const Bit = std.math.Log2Int(T); + const BitRmwOp = enum { + Set, + Reset, + Toggle, + }; + + pub inline fn bitSet(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { + return bitRmw(self, .Set, bit, ordering); + } + + pub inline fn bitReset(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { + return bitRmw(self, .Reset, bit, ordering); + } + + pub inline fn bitToggle(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { + return bitRmw(self, .Toggle, bit, ordering); + } + + inline fn bitRmw(self: *Self, comptime op: BitRmwOp, bit: Bit, comptime ordering: Ordering) u1 { + // x86 supports dedicated bitwise instructions + if (comptime builtin.target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) { + // TODO: this causes std lib test failures when enabled + if (false) { + return x86BitRmw(self, op, bit, ordering); + } } - pub inline fn fetchSub(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.Sub, value, ordering); - } - - pub inline fn fetchMin(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.Min, value, ordering); - } - - pub inline fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.Max, value, ordering); - } - }); - - pub usingnamespace exportWhen(std.meta.trait.isIntegral(T), struct { - pub inline fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.And, value, ordering); - } - - pub inline fn fetchNand(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.Nand, value, ordering); - } - - pub inline fn fetchOr(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.Or, value, ordering); - } - - pub inline fn fetchXor(self: *Self, value: T, comptime ordering: Ordering) T { - return self.rmw(.Xor, value, ordering); - } - - const Bit = std.math.Log2Int(T); - const BitRmwOp = enum { - Set, - Reset, - Toggle, + const mask = @as(T, 1) << bit; + const value = switch (op) { + .Set => self.fetchOr(mask, ordering), + .Reset => self.fetchAnd(~mask, ordering), + .Toggle => self.fetchXor(mask, ordering), }; - pub inline fn bitSet(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { - return bitRmw(self, .Set, bit, ordering); - } + return @intFromBool(value & mask != 0); + } - pub inline fn bitReset(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { - return bitRmw(self, .Reset, bit, ordering); - } + inline fn x86BitRmw(self: *Self, comptime op: BitRmwOp, bit: Bit, comptime ordering: Ordering) u1 { + const old_bit: u8 = switch (@sizeOf(T)) { + 2 => switch (op) { + .Set => asm volatile ("lock btsw %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Reset => asm volatile ("lock btrw %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Toggle => asm volatile ("lock btcw %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + }, + 4 => switch (op) { + .Set => asm volatile ("lock btsl %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Reset => asm volatile ("lock btrl %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Toggle => asm volatile ("lock btcl %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + }, + 8 => switch (op) { + .Set => asm volatile ("lock btsq %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Reset => asm volatile ("lock btrq %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Toggle => asm volatile ("lock btcq %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*m" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + }, + else => @compileError("Invalid atomic type " ++ @typeName(T)), + }; - pub inline fn bitToggle(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { - return bitRmw(self, .Toggle, bit, ordering); - } + // TODO: emit appropriate tsan fence if compiling with tsan + _ = ordering; - inline fn bitRmw(self: *Self, comptime op: BitRmwOp, bit: Bit, comptime ordering: Ordering) u1 { - // x86 supports dedicated bitwise instructions - if (comptime builtin.target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) { - // TODO: this causes std lib test failures when enabled - if (false) { - return x86BitRmw(self, op, bit, ordering); - } - } - - const mask = @as(T, 1) << bit; - const value = switch (op) { - .Set => self.fetchOr(mask, ordering), - .Reset => self.fetchAnd(~mask, ordering), - .Toggle => self.fetchXor(mask, ordering), - }; - - return @intFromBool(value & mask != 0); - } - - inline fn x86BitRmw(self: *Self, comptime op: BitRmwOp, bit: Bit, comptime ordering: Ordering) u1 { - const old_bit: u8 = switch (@sizeOf(T)) { - 2 => switch (op) { - .Set => asm volatile ("lock btsw %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Reset => asm volatile ("lock btrw %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Toggle => asm volatile ("lock btcw %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - }, - 4 => switch (op) { - .Set => asm volatile ("lock btsl %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Reset => asm volatile ("lock btrl %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Toggle => asm volatile ("lock btcl %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - }, - 8 => switch (op) { - .Set => asm volatile ("lock btsq %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Reset => asm volatile ("lock btrq %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Toggle => asm volatile ("lock btcq %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*m" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - }, - else => @compileError("Invalid atomic type " ++ @typeName(T)), - }; - - // TODO: emit appropriate tsan fence if compiling with tsan - _ = ordering; - - return @as(u1, @intCast(old_bit)); - } - }); + return @intCast(old_bit); + } }; } diff --git a/lib/std/enums.zig b/lib/std/enums.zig index 0ee090dae1..d35ac81c1a 100644 --- a/lib/std/enums.zig +++ b/lib/std/enums.zig @@ -201,7 +201,7 @@ pub fn nameCast(comptime E: type, comptime value: anytype) E { if (V == E) break :blk value; const name: ?[]const u8 = switch (@typeInfo(V)) { .EnumLiteral, .Enum => @tagName(value), - .Pointer => if (std.meta.trait.isZigString(V)) value else null, + .Pointer => value, else => null, }; if (name) |n| { diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index e21b1b9af1..b93138d979 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -478,7 +478,7 @@ pub fn formatType( return formatAddress(value, options, writer); } - if (comptime std.meta.trait.hasFn("format")(T)) { + if (std.meta.hasFn(T, "format")) { return try value.format(actual_fmt, options, writer); } @@ -611,15 +611,12 @@ pub fn formatType( else => {}, } } - if (comptime std.meta.trait.isZigString(info.child)) { - for (value, 0..) |item, i| { - comptime checkTextFmt(actual_fmt); - if (i != 0) try formatBuf(", ", options, writer); - try formatBuf(item, options, writer); - } - return; + for (value, 0..) |item, i| { + comptime checkTextFmt(actual_fmt); + if (i != 0) try formatBuf(", ", options, writer); + try formatBuf(item, options, writer); } - invalidFmtError(fmt, value); + return; }, .Enum, .Union, .Struct => { return formatType(value.*, actual_fmt, options, writer, max_depth); diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index 78e2cab104..b7321c0b9e 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -1,7 +1,6 @@ const std = @import("std"); const assert = std.debug.assert; const mem = std.mem; -const meta = std.meta; /// Describes how pointer types should be hashed. pub const HashStrategy = enum { @@ -69,7 +68,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { else => @TypeOf(hasher), }; - if (strat == .Shallow and comptime meta.trait.hasUniqueRepresentation(Key)) { + if (strat == .Shallow and std.meta.hasUniqueRepresentation(Key)) { @call(.always_inline, Hasher.update, .{ hasher, mem.asBytes(&key) }); return; } @@ -97,7 +96,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { .signedness = .unsigned, } }), @bitCast(key)), strat), .unsigned => { - if (comptime meta.trait.hasUniqueRepresentation(Key)) { + if (std.meta.hasUniqueRepresentation(Key)) { @call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key) }); } else { // Take only the part containing the key value, the remaining @@ -120,7 +119,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { .Array => hashArray(hasher, key, strat), .Vector => |info| { - if (comptime meta.trait.hasUniqueRepresentation(Key)) { + if (std.meta.hasUniqueRepresentation(Key)) { hasher.update(mem.asBytes(&key)); } else { comptime var i = 0; @@ -140,7 +139,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { .Union => |info| { if (info.tag_type) |tag_type| { - const tag = meta.activeTag(key); + const tag = std.meta.activeTag(key); hash(hasher, tag, strat); inline for (info.fields) |field| { if (@field(tag_type, field.name) == tag) { @@ -166,27 +165,21 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { } } -fn typeContainsSlice(comptime K: type) bool { - comptime { - if (meta.trait.isSlice(K)) { - return true; - } - if (meta.trait.is(.Struct)(K)) { - inline for (@typeInfo(K).Struct.fields) |field| { +inline fn typeContainsSlice(comptime K: type) bool { + return switch (@typeInfo(K)) { + .Pointer => |info| info.size == .Slice, + + inline .Struct, .Union => |info| { + inline for (info.fields) |field| { if (typeContainsSlice(field.type)) { return true; } } - } - if (meta.trait.is(.Union)(K)) { - inline for (@typeInfo(K).Union.fields) |field| { - if (typeContainsSlice(field.type)) { - return true; - } - } - } - return false; - } + return false; + }, + + else => false, + }; } /// Provides generic hashing for any eligible type. @@ -236,7 +229,7 @@ fn testHashDeepRecursive(key: anytype) u64 { test "typeContainsSlice" { comptime { - try testing.expect(!typeContainsSlice(meta.Tag(std.builtin.Type))); + try testing.expect(!typeContainsSlice(std.meta.Tag(std.builtin.Type))); try testing.expect(typeContainsSlice([]const u8)); try testing.expect(!typeContainsSlice(u8)); diff --git a/lib/std/hash/xxhash.zig b/lib/std/hash/xxhash.zig index eef9c9654a..88ec3ba372 100644 --- a/lib/std/hash/xxhash.zig +++ b/lib/std/hash/xxhash.zig @@ -185,8 +185,6 @@ pub const XxHash64 = struct { } pub fn update(self: *XxHash64, input: anytype) void { - validateType(@TypeOf(input)); - if (input.len < 32 - self.buf_len) { @memcpy(self.buf[self.buf_len..][0..input.len], input); self.buf_len += input.len; @@ -232,8 +230,6 @@ pub const XxHash64 = struct { }; pub fn hash(seed: u64, input: anytype) u64 { - validateType(@TypeOf(input)); - if (input.len < 32) { return finalize(seed +% prime_5, 0, input); } else { @@ -315,8 +311,6 @@ pub const XxHash32 = struct { } pub fn update(self: *XxHash32, input: []const u8) void { - validateType(@TypeOf(input)); - if (input.len < 16 - self.buf_len) { @memcpy(self.buf[self.buf_len..][0..input.len], input); self.buf_len += input.len; @@ -416,8 +410,6 @@ pub const XxHash32 = struct { } pub fn hash(seed: u32, input: anytype) u32 { - validateType(@TypeOf(input)); - if (input.len < 16) { return finalize(seed +% prime_5, 0, input); } else { @@ -587,8 +579,6 @@ pub const XxHash3 = struct { // Public API - Oneshot pub fn hash(seed: u64, input: anytype) u64 { - validateType(@TypeOf(input)); - const secret = &default_secret; if (input.len > 240) return hashLong(seed, input); if (input.len > 128) return hash240(seed, input, secret); @@ -709,8 +699,6 @@ pub const XxHash3 = struct { } pub fn update(self: *XxHash3, input: anytype) void { - validateType(@TypeOf(input)); - self.total_len += input.len; std.debug.assert(self.buffered <= self.buffer.len); @@ -783,18 +771,6 @@ pub const XxHash3 = struct { const verify = @import("verify.zig"); -fn validateType(comptime T: type) void { - comptime { - if (!((std.meta.trait.isSlice(T) or - std.meta.trait.is(.Array)(T) or - std.meta.trait.isPtrTo(.Array)(T)) and - std.meta.Elem(T) == u8)) - { - @compileError("expect a slice, array or pointer to array of u8, got " ++ @typeName(T)); - } - } -} - fn testExpect(comptime H: type, seed: anytype, input: []const u8, expected: u64) !void { try expectEqual(expected, H.hash(seed, input)); diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 2353c8962e..b900a9a347 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -4,8 +4,6 @@ const assert = std.debug.assert; const autoHash = std.hash.autoHash; const math = std.math; const mem = std.mem; -const meta = std.meta; -const trait = meta.trait; const Allocator = mem.Allocator; const Wyhash = std.hash.Wyhash; @@ -24,7 +22,7 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K) return struct { fn hash(ctx: Context, key: K) u64 { _ = ctx; - if (comptime trait.hasUniqueRepresentation(K)) { + if (std.meta.hasUniqueRepresentation(K)) { return Wyhash.hash(0, std.mem.asBytes(&key)); } else { var hasher = Wyhash.init(0); @@ -39,7 +37,7 @@ pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K return struct { fn eql(ctx: Context, a: K, b: K) bool { _ = ctx; - return meta.eql(a, b); + return std.meta.eql(a, b); } }.eql; } diff --git a/lib/std/io/bit_reader.zig b/lib/std/io/bit_reader.zig index 1e3911ff57..6f37ae5d87 100644 --- a/lib/std/io/bit_reader.zig +++ b/lib/std/io/bit_reader.zig @@ -2,7 +2,6 @@ const std = @import("../std.zig"); const io = std.io; const assert = std.debug.assert; const testing = std.testing; -const trait = std.meta.trait; const meta = std.meta; const math = std.math; @@ -43,8 +42,6 @@ pub fn BitReader(comptime endian: std.builtin.Endian, comptime ReaderType: type) /// containing them in the least significant end. The number of bits successfully /// read is placed in `out_bits`, as reaching the end of the stream is not an error. pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U { - comptime assert(trait.isUnsignedInt(U)); - //by extending the buffer to a minimum of u8 we can cover a number of edge cases // related to shifting and casting. const u_bit_count = @bitSizeOf(U); diff --git a/lib/std/io/bit_writer.zig b/lib/std/io/bit_writer.zig index 40fd603555..14e7f994ed 100644 --- a/lib/std/io/bit_writer.zig +++ b/lib/std/io/bit_writer.zig @@ -2,8 +2,6 @@ const std = @import("../std.zig"); const io = std.io; const testing = std.testing; const assert = std.debug.assert; -const trait = std.meta.trait; -const meta = std.meta; const math = std.math; /// Creates a stream which allows for writing bit fields to another stream @@ -35,7 +33,7 @@ pub fn BitWriter(comptime endian: std.builtin.Endian, comptime WriterType: type) if (bits == 0) return; const U = @TypeOf(value); - comptime assert(trait.isUnsignedInt(U)); + comptime assert(@typeInfo(U).Int.signedness == .unsigned); //by extending the buffer to a minimum of u8 we can cover a number of edge cases // related to shifting and casting. diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index c02446e53f..79ea020092 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -1,7 +1,5 @@ const std = @import("std"); const io = std.io; -const meta = std.meta; -const trait = std.trait; const DefaultPrng = std.rand.DefaultPrng; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; diff --git a/lib/std/json/static.zig b/lib/std/json/static.zig index a46bc33ecd..ea0bb6c0f2 100644 --- a/lib/std/json/static.zig +++ b/lib/std/json/static.zig @@ -247,7 +247,7 @@ pub fn innerParse( } }, .Enum => { - if (comptime std.meta.trait.hasFn("jsonParse")(T)) { + if (std.meta.hasFn(T, "jsonParse")) { return T.jsonParse(allocator, source, options); } @@ -260,7 +260,7 @@ pub fn innerParse( return sliceToEnum(T, slice); }, .Union => |unionInfo| { - if (comptime std.meta.trait.hasFn("jsonParse")(T)) { + if (std.meta.hasFn(T, "jsonParse")) { return T.jsonParse(allocator, source, options); } @@ -318,7 +318,7 @@ pub fn innerParse( return r; } - if (comptime std.meta.trait.hasFn("jsonParse")(T)) { + if (std.meta.hasFn(T, "jsonParse")) { return T.jsonParse(allocator, source, options); } @@ -581,7 +581,7 @@ pub fn innerParseFromValue( } }, .Enum => { - if (comptime std.meta.trait.hasFn("jsonParseFromValue")(T)) { + if (std.meta.hasFn(T, "jsonParseFromValue")) { return T.jsonParseFromValue(allocator, source, options); } @@ -593,7 +593,7 @@ pub fn innerParseFromValue( } }, .Union => |unionInfo| { - if (comptime std.meta.trait.hasFn("jsonParseFromValue")(T)) { + if (std.meta.hasFn(T, "jsonParseFromValue")) { return T.jsonParseFromValue(allocator, source, options); } @@ -635,7 +635,7 @@ pub fn innerParseFromValue( return r; } - if (comptime std.meta.trait.hasFn("jsonParseFromValue")(T)) { + if (std.meta.hasFn(T, "jsonParseFromValue")) { return T.jsonParseFromValue(allocator, source, options); } diff --git a/lib/std/json/stringify.zig b/lib/std/json/stringify.zig index 41dfed4778..86a000aa01 100644 --- a/lib/std/json/stringify.zig +++ b/lib/std/json/stringify.zig @@ -451,14 +451,14 @@ pub fn WriteStream( } }, .Enum, .EnumLiteral => { - if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { + if (std.meta.hasFn(T, "jsonStringify")) { return value.jsonStringify(self); } return self.stringValue(@tagName(value)); }, .Union => { - if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { + if (std.meta.hasFn(T, "jsonStringify")) { return value.jsonStringify(self); } @@ -487,7 +487,7 @@ pub fn WriteStream( } }, .Struct => |S| { - if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { + if (std.meta.hasFn(T, "jsonStringify")) { return value.jsonStringify(self); } diff --git a/lib/std/math.zig b/lib/std/math.zig index 1710cdc948..3f50e45705 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -801,7 +801,7 @@ fn testDivFloor() !void { /// zero. pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if ((comptime std.meta.trait.isNumber(T)) and denominator == 0) return error.DivisionByZero; + if (denominator == 0) return error.DivisionByZero; const info = @typeInfo(T); switch (info) { .ComptimeFloat, .Float => return @ceil(numerator / denominator), diff --git a/lib/std/mem.zig b/lib/std/mem.zig index bd0f8ca754..19f070da67 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -4,8 +4,6 @@ const debug = std.debug; const assert = debug.assert; const math = std.math; const mem = @This(); -const meta = std.meta; -const trait = meta.trait; const testing = std.testing; const Endian = std.builtin.Endian; const native_endian = builtin.cpu.arch.endian(); @@ -736,7 +734,7 @@ test "span" { } /// Helper for the return type of sliceTo() -fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type { +fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type { switch (@typeInfo(T)) { .Optional => |optional_info| { return ?SliceTo(optional_info.child, end); @@ -796,7 +794,7 @@ fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type { /// resulting slice is also sentinel terminated. /// Pointer properties such as mutability and alignment are preserved. /// C pointers are assumed to be non-null. -pub fn sliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) { +pub fn sliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) { if (@typeInfo(@TypeOf(ptr)) == .Optional) { const non_null = ptr orelse return null; return sliceTo(non_null, end); @@ -852,7 +850,7 @@ test "sliceTo" { } /// Private helper for sliceTo(). If you want the length, use sliceTo(foo, x).len -fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize { +fn lenSliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) usize { switch (@typeInfo(@TypeOf(ptr))) { .Pointer => |ptr_info| switch (ptr_info.size) { .One => switch (@typeInfo(ptr_info.child)) { @@ -1319,7 +1317,7 @@ pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?us if (needle.len > haystack.len) return null; if (needle.len == 0) return haystack.len; - if (!meta.trait.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4) + if (!std.meta.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4) return lastIndexOfLinear(T, haystack, needle); const haystack_bytes = sliceAsBytes(haystack); @@ -1350,7 +1348,7 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee return indexOfScalarPos(T, haystack, start_index, needle[0]); } - if (!meta.trait.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4) + if (!std.meta.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4) return indexOfPosLinear(T, haystack, start_index, needle); const haystack_bytes = sliceAsBytes(haystack); @@ -3368,13 +3366,7 @@ fn ReverseIterator(comptime T: type) type { /// Iterates over a slice in reverse. pub fn reverseIterator(slice: anytype) ReverseIterator(@TypeOf(slice)) { - const T = @TypeOf(slice); - if (comptime trait.isPtrTo(.Array)(T)) { - return .{ .ptr = slice, .index = slice.len }; - } else { - comptime assert(trait.isSlice(T)); - return .{ .ptr = slice.ptr, .index = slice.len }; - } + return .{ .ptr = slice.ptr, .index = slice.len }; } test "reverseIterator" { @@ -3394,7 +3386,7 @@ test "reverseIterator" { try testing.expectEqual(@as(?i32, null), it.next()); it = reverseIterator(slice); - try testing.expect(trait.isConstPtr(@TypeOf(it.nextPtr().?))); + try testing.expect(*const i32 == @TypeOf(it.nextPtr().?)); try testing.expectEqual(@as(?i32, 7), it.nextPtr().?.*); try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*); try testing.expectEqual(@as(?*const i32, null), it.nextPtr()); @@ -3414,7 +3406,7 @@ test "reverseIterator" { try testing.expectEqual(@as(?i32, null), it.next()); it = reverseIterator(ptr_to_array); - try testing.expect(trait.isConstPtr(@TypeOf(it.nextPtr().?))); + try testing.expect(*const i32 == @TypeOf(it.nextPtr().?)); try testing.expectEqual(@as(?i32, 7), it.nextPtr().?.*); try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*); try testing.expectEqual(@as(?*const i32, null), it.nextPtr()); @@ -3730,11 +3722,7 @@ fn CopyPtrAttrs( } fn AsBytesReturnType(comptime P: type) type { - if (!trait.isSingleItemPtr(P)) - @compileError("expected single item pointer, passed " ++ @typeName(P)); - - const size = @sizeOf(meta.Child(P)); - + const size = @sizeOf(std.meta.Child(P)); return CopyPtrAttrs(P, .One, [size]u8); } @@ -3818,21 +3806,13 @@ test "toBytes" { } fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { - const size = @as(usize, @sizeOf(T)); - - if (comptime !trait.is(.Pointer)(B) or - (meta.Child(B) != [size]u8 and meta.Child(B) != [size:0]u8)) - { - @compileError(std.fmt.comptimePrint("expected *[{}]u8, passed " ++ @typeName(B), .{size})); - } - return CopyPtrAttrs(B, .One, T); } /// Given a pointer to an array of bytes, returns a pointer to a value of the specified type /// backed by those bytes, preserving pointer attributes. pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { - return @as(BytesAsValueReturnType(T, @TypeOf(bytes)), @ptrCast(bytes)); + return @ptrCast(bytes); } test "bytesAsValue" { @@ -3872,7 +3852,7 @@ test "bytesAsValue" { .big => "\xA1\xDE\xEF\xBE", }; const inst2 = bytesAsValue(S, inst_bytes); - try testing.expect(meta.eql(inst, inst2.*)); + try testing.expect(std.meta.eql(inst, inst2.*)); } test "bytesAsValue preserves pointer attributes" { @@ -3905,14 +3885,6 @@ test "bytesToValue" { } fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { - if (!(trait.isSlice(bytesType) or trait.isPtrTo(.Array)(bytesType)) or meta.Elem(bytesType) != u8) { - @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType)); - } - - if (trait.isPtrTo(.Array)(bytesType) and @typeInfo(meta.Child(bytesType)).Array.len % @sizeOf(T) != 0) { - @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T)); - } - return CopyPtrAttrs(bytesType, .Slice, T); } @@ -4000,10 +3972,6 @@ test "bytesAsSlice preserves pointer attributes" { } fn SliceAsBytesReturnType(comptime Slice: type) type { - if (!trait.isSlice(Slice) and !trait.isPtrTo(.Array)(Slice)) { - @compileError("expected []T or *[_]T, passed " ++ @typeName(Slice)); - } - return CopyPtrAttrs(Slice, .Slice, u8); } @@ -4012,15 +3980,15 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { const Slice = @TypeOf(slice); // a slice of zero-bit values always occupies zero bytes - if (@sizeOf(meta.Elem(Slice)) == 0) return &[0]u8{}; + if (@sizeOf(std.meta.Elem(Slice)) == 0) return &[0]u8{}; // let's not give an undefined pointer to @ptrCast // it may be equal to zero and fail a null check - if (slice.len == 0 and comptime meta.sentinel(Slice) == null) return &[0]u8{}; + if (slice.len == 0 and std.meta.sentinel(Slice) == null) return &[0]u8{}; const cast_target = CopyPtrAttrs(Slice, .Many, u8); - return @as(cast_target, @ptrCast(slice))[0 .. slice.len * @sizeOf(meta.Elem(Slice))]; + return @as(cast_target, @ptrCast(slice))[0 .. slice.len * @sizeOf(std.meta.Elem(Slice))]; } test "sliceAsBytes" { diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 78df8c1bed..eb296fe4b5 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -5,7 +5,6 @@ const math = std.math; const testing = std.testing; const root = @import("root"); -pub const trait = @import("meta/trait.zig"); pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; const Type = std.builtin.Type; @@ -135,7 +134,8 @@ test "std.meta.Elem" { /// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value, /// or `null` if there is not one. /// Types which cannot possibly have a sentinel will be a compile error. -pub fn sentinel(comptime T: type) ?Elem(T) { +/// Result is always comptime-known. +pub inline fn sentinel(comptime T: type) ?Elem(T) { switch (@typeInfo(T)) { .Array => |info| { const sentinel_ptr = info.sentinel orelse return null; @@ -162,7 +162,7 @@ pub fn sentinel(comptime T: type) ?Elem(T) { @compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel"); } -test "std.meta.sentinel" { +test sentinel { try testSentinel(); try comptime testSentinel(); } @@ -712,8 +712,6 @@ test "std.meta.activeTag" { const TagPayloadType = TagPayload; pub fn TagPayloadByName(comptime U: type, comptime tag_name: []const u8) type { - comptime debug.assert(trait.is(.Union)(U)); - const info = @typeInfo(U).Union; inline for (info.fields) |field_info| { @@ -1117,3 +1115,124 @@ test "isError" { try std.testing.expect(isError(math.divTrunc(u8, 5, 0))); try std.testing.expect(!isError(math.divTrunc(u8, 5, 5))); } + +/// Returns true if a type has a namespace and the namespace contains `name`; +/// `false` otherwise. Result is always comptime-known. +pub inline fn hasFn(comptime T: type, comptime name: []const u8) bool { + switch (@typeInfo(T)) { + .Struct, .Union, .Enum, .Opaque => {}, + else => return false, + } + if (!@hasDecl(T, name)) + return false; + + return @typeInfo(@TypeOf(@field(T, name))) == .Fn; +} + +/// True if every value of the type `T` has a unique bit pattern representing it. +/// In other words, `T` has no unused bits and no padding. +/// Result is always comptime-known. +pub inline fn hasUniqueRepresentation(comptime T: type) bool { + return switch (@typeInfo(T)) { + else => false, // TODO can we know if it's true for some of these types ? + + .AnyFrame, + .Enum, + .ErrorSet, + .Fn, + => true, + + .Bool => false, + + .Int => |info| @sizeOf(T) * 8 == info.bits, + + .Pointer => |info| info.size != .Slice, + + .Array => |info| hasUniqueRepresentation(info.child), + + .Struct => |info| { + var sum_size = @as(usize, 0); + + inline for (info.fields) |field| { + if (!hasUniqueRepresentation(field.type)) return false; + sum_size += @sizeOf(field.type); + } + + return @sizeOf(T) == sum_size; + }, + + .Vector => |info| hasUniqueRepresentation(info.child) and + @sizeOf(T) == @sizeOf(info.child) * info.len, + }; +} + +test "hasUniqueRepresentation" { + const TestStruct1 = struct { + a: u32, + b: u32, + }; + + try testing.expect(hasUniqueRepresentation(TestStruct1)); + + const TestStruct2 = struct { + a: u32, + b: u16, + }; + + try testing.expect(!hasUniqueRepresentation(TestStruct2)); + + const TestStruct3 = struct { + a: u32, + b: u32, + }; + + try testing.expect(hasUniqueRepresentation(TestStruct3)); + + const TestStruct4 = struct { a: []const u8 }; + + try testing.expect(!hasUniqueRepresentation(TestStruct4)); + + const TestStruct5 = struct { a: TestStruct4 }; + + try testing.expect(!hasUniqueRepresentation(TestStruct5)); + + const TestUnion1 = packed union { + a: u32, + b: u16, + }; + + try testing.expect(!hasUniqueRepresentation(TestUnion1)); + + const TestUnion2 = extern union { + a: u32, + b: u16, + }; + + try testing.expect(!hasUniqueRepresentation(TestUnion2)); + + const TestUnion3 = union { + a: u32, + b: u16, + }; + + try testing.expect(!hasUniqueRepresentation(TestUnion3)); + + const TestUnion4 = union(enum) { + a: u32, + b: u16, + }; + + try testing.expect(!hasUniqueRepresentation(TestUnion4)); + + inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| { + try testing.expect(hasUniqueRepresentation(T)); + } + inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| { + try testing.expect(!hasUniqueRepresentation(T)); + } + + try testing.expect(!hasUniqueRepresentation([]u8)); + try testing.expect(!hasUniqueRepresentation([]const u8)); + + try testing.expect(hasUniqueRepresentation(@Vector(4, u16))); +} diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig deleted file mode 100644 index 3d3a1099fa..0000000000 --- a/lib/std/meta/trait.zig +++ /dev/null @@ -1,652 +0,0 @@ -const std = @import("../std.zig"); -const mem = std.mem; -const debug = std.debug; -const testing = std.testing; - -const meta = @import("../meta.zig"); - -pub const TraitFn = fn (type) bool; - -pub fn multiTrait(comptime traits: anytype) 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 "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(.{ - hasFn("add"), - hasField("x"), - hasField("y"), - }); - try testing.expect(isVector(Vector2)); - try testing.expect(!isVector(u8)); -} - -pub fn hasFn(comptime name: []const u8) TraitFn { - const Closure = struct { - pub fn trait(comptime T: type) bool { - if (!comptime isContainer(T)) return false; - if (!comptime @hasDecl(T, name)) return false; - const DeclType = @TypeOf(@field(T, name)); - return @typeInfo(DeclType) == .Fn; - } - }; - return Closure.trait; -} - -test "hasFn" { - const TestStruct = struct { - pub fn useless() void {} - }; - - try testing.expect(hasFn("useless")(TestStruct)); - try testing.expect(!hasFn("append")(TestStruct)); - try testing.expect(!hasFn("useless")(u8)); -} - -pub fn hasField(comptime name: []const u8) TraitFn { - const Closure = struct { - pub fn trait(comptime T: type) bool { - const fields = switch (@typeInfo(T)) { - .Struct => |s| s.fields, - .Union => |u| u.fields, - .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 "hasField" { - const TestStruct = struct { - value: u32, - }; - - try testing.expect(hasField("value")(TestStruct)); - try testing.expect(!hasField("value")(*TestStruct)); - try testing.expect(!hasField("x")(TestStruct)); - try testing.expect(!hasField("x")(**TestStruct)); - try testing.expect(!hasField("value")(u8)); -} - -pub fn is(comptime id: std.builtin.TypeId) TraitFn { - const Closure = struct { - pub fn trait(comptime T: type) bool { - return id == @typeInfo(T); - } - }; - return Closure.trait; -} - -test "is" { - try testing.expect(is(.Int)(u8)); - try testing.expect(!is(.Int)(f32)); - try testing.expect(is(.Pointer)(*u8)); - try testing.expect(is(.Void)(void)); - try testing.expect(!is(.Optional)(anyerror)); -} - -pub fn isPtrTo(comptime id: std.builtin.TypeId) TraitFn { - const Closure = struct { - pub fn trait(comptime T: type) bool { - if (!comptime isSingleItemPtr(T)) return false; - return id == @typeInfo(meta.Child(T)); - } - }; - return Closure.trait; -} - -test "isPtrTo" { - try testing.expect(!isPtrTo(.Struct)(struct {})); - try testing.expect(isPtrTo(.Struct)(*struct {})); - try testing.expect(!isPtrTo(.Struct)(**struct {})); -} - -pub fn isSliceOf(comptime id: std.builtin.TypeId) TraitFn { - const Closure = struct { - pub fn trait(comptime T: type) bool { - if (!comptime isSlice(T)) return false; - return id == @typeInfo(meta.Child(T)); - } - }; - return Closure.trait; -} - -test "isSliceOf" { - try testing.expect(!isSliceOf(.Struct)(struct {})); - try testing.expect(isSliceOf(.Struct)([]struct {})); - try testing.expect(!isSliceOf(.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 { - return switch (@typeInfo(T)) { - .Struct => |s| s.layout == .Extern, - .Union => |u| u.layout == .Extern, - else => false, - }; -} - -test "isExtern" { - const TestExStruct = extern struct {}; - const TestStruct = struct {}; - - try testing.expect(isExtern(TestExStruct)); - try testing.expect(!isExtern(TestStruct)); - try testing.expect(!isExtern(u8)); -} - -pub fn isPacked(comptime T: type) bool { - return switch (@typeInfo(T)) { - .Struct => |s| s.layout == .Packed, - .Union => |u| u.layout == .Packed, - else => false, - }; -} - -test "isPacked" { - const TestPStruct = packed struct {}; - const TestStruct = struct {}; - - try testing.expect(isPacked(TestPStruct)); - try testing.expect(!isPacked(TestStruct)); - try testing.expect(!isPacked(u8)); -} - -pub fn isUnsignedInt(comptime T: type) bool { - return switch (@typeInfo(T)) { - .Int => |i| i.signedness == .unsigned, - else => false, - }; -} - -test "isUnsignedInt" { - try testing.expect(isUnsignedInt(u32) == true); - try testing.expect(isUnsignedInt(comptime_int) == false); - try testing.expect(isUnsignedInt(i64) == false); - try testing.expect(isUnsignedInt(f64) == false); -} - -pub fn isSignedInt(comptime T: type) bool { - return switch (@typeInfo(T)) { - .ComptimeInt => true, - .Int => |i| i.signedness == .signed, - else => false, - }; -} - -test "isSignedInt" { - try testing.expect(isSignedInt(u32) == false); - try testing.expect(isSignedInt(comptime_int) == true); - try testing.expect(isSignedInt(i64) == true); - try testing.expect(isSignedInt(f64) == false); -} - -pub fn isSingleItemPtr(comptime T: type) bool { - if (comptime is(.Pointer)(T)) { - return @typeInfo(T).Pointer.size == .One; - } - return false; -} - -test "isSingleItemPtr" { - const array = [_]u8{0} ** 10; - try comptime testing.expect(isSingleItemPtr(@TypeOf(&array[0]))); - try comptime testing.expect(!isSingleItemPtr(@TypeOf(array))); - var runtime_zero: usize = 0; - _ = &runtime_zero; - try testing.expect(!isSingleItemPtr(@TypeOf(array[runtime_zero..1]))); -} - -pub fn isManyItemPtr(comptime T: type) bool { - if (comptime is(.Pointer)(T)) { - return @typeInfo(T).Pointer.size == .Many; - } - return false; -} - -test "isManyItemPtr" { - const array = [_]u8{0} ** 10; - const mip = @as([*]const u8, @ptrCast(&array[0])); - try testing.expect(isManyItemPtr(@TypeOf(mip))); - try testing.expect(!isManyItemPtr(@TypeOf(array))); - try testing.expect(!isManyItemPtr(@TypeOf(array[0..1]))); -} - -pub fn isSlice(comptime T: type) bool { - if (comptime is(.Pointer)(T)) { - return @typeInfo(T).Pointer.size == .Slice; - } - return false; -} - -test "isSlice" { - const array = [_]u8{0} ** 10; - var runtime_zero: usize = 0; - _ = &runtime_zero; - try testing.expect(isSlice(@TypeOf(array[runtime_zero..]))); - try testing.expect(!isSlice(@TypeOf(array))); - try testing.expect(!isSlice(@TypeOf(&array[0]))); -} - -pub fn isIndexable(comptime T: type) bool { - if (comptime is(.Pointer)(T)) { - if (@typeInfo(T).Pointer.size == .One) { - return (comptime is(.Array)(meta.Child(T))); - } - return true; - } - return comptime is(.Array)(T) or is(.Vector)(T) or isTuple(T); -} - -test "isIndexable" { - const array = [_]u8{0} ** 10; - const slice = @as([]const u8, &array); - const vector: @Vector(2, u32) = [_]u32{0} ** 2; - const tuple = .{ 1, 2, 3 }; - - try testing.expect(isIndexable(@TypeOf(array))); - try testing.expect(isIndexable(@TypeOf(&array))); - try testing.expect(isIndexable(@TypeOf(slice))); - try testing.expect(!isIndexable(meta.Child(@TypeOf(slice)))); - try testing.expect(isIndexable(@TypeOf(vector))); - try testing.expect(isIndexable(@TypeOf(tuple))); -} - -pub fn isNumber(comptime T: type) bool { - return switch (@typeInfo(T)) { - .Int, .Float, .ComptimeInt, .ComptimeFloat => true, - else => false, - }; -} - -test "isNumber" { - const NotANumber = struct { - number: u8, - }; - - try testing.expect(isNumber(u32)); - try testing.expect(isNumber(f32)); - try testing.expect(isNumber(u64)); - try testing.expect(isNumber(@TypeOf(102))); - try testing.expect(isNumber(@TypeOf(102.123))); - try testing.expect(!isNumber([]u8)); - try testing.expect(!isNumber(NotANumber)); -} - -pub fn isIntegral(comptime T: type) bool { - return switch (@typeInfo(T)) { - .Int, .ComptimeInt => true, - else => false, - }; -} - -test "isIntegral" { - try testing.expect(isIntegral(u32)); - try testing.expect(!isIntegral(f32)); - try testing.expect(isIntegral(@TypeOf(102))); - try testing.expect(!isIntegral(@TypeOf(102.123))); - try testing.expect(!isIntegral(*u8)); - try testing.expect(!isIntegral([]u8)); -} - -pub fn isFloat(comptime T: type) bool { - return switch (@typeInfo(T)) { - .Float, .ComptimeFloat => true, - else => false, - }; -} - -test "isFloat" { - try testing.expect(!isFloat(u32)); - try testing.expect(isFloat(f32)); - try testing.expect(!isFloat(@TypeOf(102))); - try testing.expect(isFloat(@TypeOf(102.123))); - try testing.expect(!isFloat(*f64)); - try testing.expect(!isFloat([]f32)); -} - -pub fn isConstPtr(comptime T: type) bool { - if (!comptime is(.Pointer)(T)) return false; - return @typeInfo(T).Pointer.is_const; -} - -test "isConstPtr" { - var t: u8 = 0; - t = t; - const c: u8 = 0; - try testing.expect(isConstPtr(*const @TypeOf(t))); - try testing.expect(isConstPtr(@TypeOf(&c))); - try testing.expect(!isConstPtr(*@TypeOf(t))); - try testing.expect(!isConstPtr(@TypeOf(6))); -} - -pub fn isContainer(comptime T: type) bool { - return switch (@typeInfo(T)) { - .Struct, .Union, .Enum, .Opaque => true, - else => false, - }; -} - -test "isContainer" { - const TestStruct = struct {}; - const TestUnion = union { - a: void, - }; - const TestEnum = enum { - A, - B, - }; - const TestOpaque = opaque {}; - - try testing.expect(isContainer(TestStruct)); - try testing.expect(isContainer(TestUnion)); - try testing.expect(isContainer(TestEnum)); - try testing.expect(isContainer(TestOpaque)); - try testing.expect(!isContainer(u8)); -} - -pub fn isTuple(comptime T: type) bool { - return is(.Struct)(T) and @typeInfo(T).Struct.is_tuple; -} - -test "isTuple" { - const t1 = struct {}; - const t2 = .{ .a = 0 }; - const t3 = .{ 1, 2, 3 }; - try testing.expect(!isTuple(t1)); - try testing.expect(!isTuple(@TypeOf(t2))); - try testing.expect(isTuple(@TypeOf(t3))); -} - -/// Returns true if the passed type will coerce to []const u8. -/// Any of the following are considered strings: -/// ``` -/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8, -/// []u8, [:S]u8, *[:S]u8, *[N:S]u8. -/// ``` -/// These types are not considered strings: -/// ``` -/// u8, [N]u8, [*]const u8, [*:0]const u8, -/// [*]const [N]u8, []const u16, []const i8, -/// *const u8, ?[]const u8, ?*const [N]u8. -/// ``` -pub fn isZigString(comptime T: type) bool { - return comptime blk: { - // Only pointer types can be strings, no optionals - const info = @typeInfo(T); - if (info != .Pointer) break :blk false; - - const ptr = &info.Pointer; - // Check for CV qualifiers that would prevent coerction to []const u8 - if (ptr.is_volatile or ptr.is_allowzero) break :blk false; - - // If it's already a slice, simple check. - if (ptr.size == .Slice) { - break :blk ptr.child == u8; - } - - // Otherwise check if it's an array type that coerces to slice. - if (ptr.size == .One) { - const child = @typeInfo(ptr.child); - if (child == .Array) { - const arr = &child.Array; - break :blk arr.child == u8; - } - } - - break :blk false; - }; -} - -test "isZigString" { - try testing.expect(isZigString([]const u8)); - try testing.expect(isZigString([]u8)); - try testing.expect(isZigString([:0]const u8)); - try testing.expect(isZigString([:0]u8)); - try testing.expect(isZigString([:5]const u8)); - try testing.expect(isZigString([:5]u8)); - try testing.expect(isZigString(*const [0]u8)); - try testing.expect(isZigString(*[0]u8)); - try testing.expect(isZigString(*const [0:0]u8)); - try testing.expect(isZigString(*[0:0]u8)); - try testing.expect(isZigString(*const [0:5]u8)); - try testing.expect(isZigString(*[0:5]u8)); - try testing.expect(isZigString(*const [10]u8)); - try testing.expect(isZigString(*[10]u8)); - try testing.expect(isZigString(*const [10:0]u8)); - try testing.expect(isZigString(*[10:0]u8)); - try testing.expect(isZigString(*const [10:5]u8)); - try testing.expect(isZigString(*[10:5]u8)); - - try testing.expect(!isZigString(u8)); - try testing.expect(!isZigString([4]u8)); - try testing.expect(!isZigString([4:0]u8)); - try testing.expect(!isZigString([*]const u8)); - try testing.expect(!isZigString([*]const [4]u8)); - try testing.expect(!isZigString([*c]const u8)); - try testing.expect(!isZigString([*c]const [4]u8)); - try testing.expect(!isZigString([*:0]const u8)); - try testing.expect(!isZigString([*:0]const u8)); - try testing.expect(!isZigString(*[]const u8)); - try testing.expect(!isZigString(?[]const u8)); - try testing.expect(!isZigString(?*const [4]u8)); - try testing.expect(!isZigString([]allowzero u8)); - try testing.expect(!isZigString([]volatile u8)); - try testing.expect(!isZigString(*allowzero [4]u8)); - try testing.expect(!isZigString(*volatile [4]u8)); -} - -pub fn hasDecls(comptime T: type, comptime names: anytype) bool { - inline for (names) |name| { - if (!@hasDecl(T, name)) - return false; - } - return true; -} - -test "hasDecls" { - const TestStruct1 = struct {}; - const TestStruct2 = struct { - pub var a: u32 = undefined; - pub var b: u32 = undefined; - c: bool, - pub fn useless() void {} - }; - - const tuple = .{ "a", "b", "c" }; - - try testing.expect(!hasDecls(TestStruct1, .{"a"})); - try testing.expect(hasDecls(TestStruct2, .{ "a", "b" })); - try testing.expect(hasDecls(TestStruct2, .{ "a", "b", "useless" })); - try testing.expect(!hasDecls(TestStruct2, .{ "a", "b", "c" })); - try testing.expect(!hasDecls(TestStruct2, tuple)); -} - -pub fn hasFields(comptime T: type, comptime names: anytype) bool { - inline for (names) |name| { - if (!@hasField(T, name)) - return false; - } - return true; -} - -test "hasFields" { - const TestStruct1 = struct {}; - const TestStruct2 = struct { - a: u32, - b: u32, - c: bool, - pub fn useless() void {} - }; - - const tuple = .{ "a", "b", "c" }; - - try testing.expect(!hasFields(TestStruct1, .{"a"})); - try testing.expect(hasFields(TestStruct2, .{ "a", "b" })); - try testing.expect(hasFields(TestStruct2, .{ "a", "b", "c" })); - try testing.expect(hasFields(TestStruct2, tuple)); - try testing.expect(!hasFields(TestStruct2, .{ "a", "b", "useless" })); -} - -pub fn hasFunctions(comptime T: type, comptime names: anytype) bool { - inline for (names) |name| { - if (!hasFn(name)(T)) - return false; - } - return true; -} - -test "hasFunctions" { - const TestStruct1 = struct {}; - const TestStruct2 = struct { - pub fn a() void {} - fn b() void {} - }; - - const tuple = .{ "a", "b", "c" }; - - try testing.expect(!hasFunctions(TestStruct1, .{"a"})); - try testing.expect(hasFunctions(TestStruct2, .{ "a", "b" })); - try testing.expect(!hasFunctions(TestStruct2, .{ "a", "b", "c" })); - try testing.expect(!hasFunctions(TestStruct2, tuple)); -} - -/// True if every value of the type `T` has a unique bit pattern representing it. -/// In other words, `T` has no unused bits and no padding. -pub fn hasUniqueRepresentation(comptime T: type) bool { - switch (@typeInfo(T)) { - else => return false, // TODO can we know if it's true for some of these types ? - - .AnyFrame, - .Enum, - .ErrorSet, - .Fn, - => return true, - - .Bool => return false, - - .Int => |info| return @sizeOf(T) * 8 == info.bits, - - .Pointer => |info| return info.size != .Slice, - - .Array => |info| return comptime hasUniqueRepresentation(info.child), - - .Struct => |info| { - var sum_size = @as(usize, 0); - - inline for (info.fields) |field| { - const FieldType = field.type; - if (comptime !hasUniqueRepresentation(FieldType)) return false; - sum_size += @sizeOf(FieldType); - } - - return @sizeOf(T) == sum_size; - }, - - .Vector => |info| return comptime hasUniqueRepresentation(info.child) and - @sizeOf(T) == @sizeOf(info.child) * info.len, - } -} - -test "hasUniqueRepresentation" { - const TestStruct1 = struct { - a: u32, - b: u32, - }; - - try testing.expect(hasUniqueRepresentation(TestStruct1)); - - const TestStruct2 = struct { - a: u32, - b: u16, - }; - - try testing.expect(!hasUniqueRepresentation(TestStruct2)); - - const TestStruct3 = struct { - a: u32, - b: u32, - }; - - try testing.expect(hasUniqueRepresentation(TestStruct3)); - - const TestStruct4 = struct { a: []const u8 }; - - try testing.expect(!hasUniqueRepresentation(TestStruct4)); - - const TestStruct5 = struct { a: TestStruct4 }; - - try testing.expect(!hasUniqueRepresentation(TestStruct5)); - - const TestUnion1 = packed union { - a: u32, - b: u16, - }; - - try testing.expect(!hasUniqueRepresentation(TestUnion1)); - - const TestUnion2 = extern union { - a: u32, - b: u16, - }; - - try testing.expect(!hasUniqueRepresentation(TestUnion2)); - - const TestUnion3 = union { - a: u32, - b: u16, - }; - - try testing.expect(!hasUniqueRepresentation(TestUnion3)); - - const TestUnion4 = union(enum) { - a: u32, - b: u16, - }; - - try testing.expect(!hasUniqueRepresentation(TestUnion4)); - - inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| { - try testing.expect(hasUniqueRepresentation(T)); - } - inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| { - try testing.expect(!hasUniqueRepresentation(T)); - } - - try testing.expect(!hasUniqueRepresentation([]u8)); - try testing.expect(!hasUniqueRepresentation([]const u8)); - - try testing.expect(hasUniqueRepresentation(@Vector(4, u16))); -} diff --git a/lib/std/rand.zig b/lib/std/rand.zig index aec461883b..708eed13be 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -3,8 +3,6 @@ //! use `std.crypto.random`. //! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will //! be faster and use substantially less stack space. -//! -//! TODO(tiehuis): Benchmark these against other reference implementations. const std = @import("std.zig"); const builtin = @import("builtin"); @@ -383,34 +381,34 @@ pub const Random = struct { /// This is useful for selecting an item from a slice where weights are not equal. /// `T` must be a numeric type capable of holding the sum of `proportions`. pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []const T) usize { - // This implementation works by summing the proportions and picking a random - // point in [0, sum). We then loop over the proportions, accumulating - // until our accumulator is greater than the random point. + // This implementation works by summing the proportions and picking a + // random point in [0, sum). We then loop over the proportions, + // accumulating until our accumulator is greater than the random point. - var sum: T = 0; - for (proportions) |v| { - sum += v; - } + const sum = s: { + var sum: T = 0; + for (proportions) |v| sum += v; + break :s sum; + }; - const point = if (comptime std.meta.trait.isSignedInt(T)) - r.intRangeLessThan(T, 0, sum) - else if (comptime std.meta.trait.isUnsignedInt(T)) - r.uintLessThan(T, sum) - else if (comptime std.meta.trait.isFloat(T)) + const point = switch (@typeInfo(T)) { + .Int => |int_info| switch (int_info.signedness) { + .signed => r.intRangeLessThan(T, 0, sum), + .unsigned => r.uintLessThan(T, sum), + }, // take care that imprecision doesn't lead to a value slightly greater than sum - @min(r.float(T) * sum, sum - std.math.floatEps(T)) - else - @compileError("weightedIndex does not support proportions of type " ++ @typeName(T)); + .Float => @min(r.float(T) * sum, sum - std.math.floatEps(T)), + else => @compileError("weightedIndex does not support proportions of type " ++ + @typeName(T)), + }; - std.debug.assert(point < sum); + assert(point < sum); var accumulator: T = 0; for (proportions, 0..) |p, index| { accumulator += p; if (point < accumulator) return index; - } - - unreachable; + } else unreachable; } /// Returns the smallest of `Index` and `usize`. diff --git a/lib/std/target.zig b/lib/std/target.zig index 3764b71d8f..bb03c8e0cd 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -823,7 +823,6 @@ pub const Target = struct { /// Returns true if any specified feature is enabled. pub fn featureSetHasAny(set: Set, features: anytype) bool { - comptime std.debug.assert(std.meta.trait.isIndexable(@TypeOf(features))); inline for (features) |feature| { if (set.isEnabled(@intFromEnum(@as(F, feature)))) return true; } @@ -832,7 +831,6 @@ pub const Target = struct { /// Returns true if every specified feature is enabled. pub fn featureSetHasAll(set: Set, features: anytype) bool { - comptime std.debug.assert(std.meta.trait.isIndexable(@TypeOf(features))); inline for (features) |feature| { if (!set.isEnabled(@intFromEnum(@as(F, feature)))) return false; } diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 4f5dae8c09..e7c118e48e 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -838,11 +838,10 @@ fn ElementType(comptime ptr: type) type { /// signedness of the given type `T`. /// Asserts `T` is an integer. fn readLeb(comptime T: type, reader: anytype) !T { - if (comptime std.meta.trait.isSignedInt(T)) { - return try leb.readILEB128(T, reader); - } else { - return try leb.readULEB128(T, reader); - } + return switch (@typeInfo(T).Int.signedness) { + .signed => try leb.readILEB128(T, reader), + .unsigned => try leb.readULEB128(T, reader), + }; } /// Reads an enum type from the given reader. diff --git a/src/translate_c.zig b/src/translate_c.zig index ec9f6977de..cd8ba8c5fc 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -4567,7 +4567,10 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { } fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node { - const fmt_s = if (comptime meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}"; + const fmt_s = switch (@typeInfo(@TypeOf(num))) { + .Int, .ComptimeInt => "{d}", + else => "{s}", + }; const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num}); if (num_kind == .float) return Tag.float_literal.create(c.arena, str) diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index a9569d1b1b..521fb7f6a2 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -542,7 +542,11 @@ test "vector division operators" { const S = struct { fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void { - if (!comptime std.meta.trait.isSignedInt(T)) { + const is_signed_int = switch (@typeInfo(T)) { + .Int => |info| info.signedness == .signed, + else => false, + }; + if (!is_signed_int) { const d0 = x / y; for (@as([4]T, d0), 0..) |v, i| { try expect(x[i] / y[i] == v); @@ -563,7 +567,11 @@ test "vector division operators" { } fn doTheTestMod(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void { - if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) { + const is_signed_int = switch (@typeInfo(T)) { + .Int => |info| info.signedness == .signed, + else => false, + }; + if (!is_signed_int and @typeInfo(T) != .Float) { const r0 = x % y; for (@as([4]T, r0), 0..) |v, i| { try expect(x[i] % y[i] == v); diff --git a/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig b/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig index 9a8d765c04..317834a9c9 100644 --- a/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig +++ b/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig @@ -5,10 +5,38 @@ pub export fn entry() void { _ = sliceAsBytes(ohnoes); _ = &ohnoes; } -fn sliceAsBytes(slice: anytype) std.meta.trait.isPtrTo(.Array)(@TypeOf(slice)) {} +fn sliceAsBytes(slice: anytype) isPtrTo(.Array)(@TypeOf(slice)) {} + +pub const TraitFn = fn (type) bool; + +pub fn isPtrTo(comptime id: std.builtin.TypeId) TraitFn { + const Closure = struct { + pub fn trait(comptime T: type) bool { + if (!comptime isSingleItemPtr(T)) return false; + return id == @typeInfo(std.meta.Child(T)); + } + }; + return Closure.trait; +} + +pub fn isSingleItemPtr(comptime T: type) bool { + if (comptime is(.Pointer)(T)) { + return @typeInfo(T).Pointer.size == .One; + } + return false; +} + +pub fn is(comptime id: std.builtin.TypeId) TraitFn { + const Closure = struct { + pub fn trait(comptime T: type) bool { + return id == @typeInfo(T); + } + }; + return Closure.trait; +} // error // backend=llvm // target=native // -// :8:63: error: expected type 'type', found 'bool' +// :8:48: error: expected type 'type', found 'bool'