std: remove meta.trait

In general, I don't like the idea of std.meta.trait, and so I am
providing some guidance by deleting the entire namespace from the
standard library and compiler codebase.

My main criticism is that it's overcomplicated machinery that bloats
compile times and is ultimately unnecessary given the existence of Zig's
strong type system and reference traces.

Users who want this can create a third party package that provides this
functionality.

closes #18051
This commit is contained in:
Andrew Kelley 2023-11-21 15:23:44 -07:00
parent 994e191643
commit d5e21a4f1a
23 changed files with 389 additions and 974 deletions

View file

@ -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"

View file

@ -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;
}

View file

@ -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);
}
};
}

View file

@ -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| {

View file

@ -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);

View file

@ -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));

View file

@ -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));

View file

@ -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;
}

View file

@ -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);

View file

@ -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.

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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),

View file

@ -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" {

View file

@ -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)));
}

View file

@ -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)));
}

View file

@ -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`.

View file

@ -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;
}

View file

@ -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.

View file

@ -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)

View file

@ -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);

View file

@ -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'