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/mem/Allocator.zig"
"${CMAKE_SOURCE_DIR}/lib/std/meta.zig" "${CMAKE_SOURCE_DIR}/lib/std/meta.zig"
"${CMAKE_SOURCE_DIR}/lib/std/meta/trailer_flags.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/multi_array_list.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os.zig" "${CMAKE_SOURCE_DIR}/lib/std/os.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"

View file

@ -4,8 +4,6 @@ const assert = debug.assert;
const testing = std.testing; const testing = std.testing;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const meta = std.meta;
const trait = meta.trait;
const autoHash = std.hash.autoHash; const autoHash = std.hash.autoHash;
const Wyhash = std.hash.Wyhash; const Wyhash = std.hash.Wyhash;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
@ -2341,13 +2339,13 @@ test "reIndex" {
test "auto store_hash" { test "auto store_hash" {
const HasCheapEql = AutoArrayHashMap(i32, i32); const HasCheapEql = AutoArrayHashMap(i32, i32);
const HasExpensiveEql = AutoArrayHashMap([32]i32, i32); const HasExpensiveEql = AutoArrayHashMap([32]i32, i32);
try testing.expect(meta.fieldInfo(HasCheapEql.Data, .hash).type == void); try testing.expect(std.meta.fieldInfo(HasCheapEql.Data, .hash).type == void);
try testing.expect(meta.fieldInfo(HasExpensiveEql.Data, .hash).type != void); try testing.expect(std.meta.fieldInfo(HasExpensiveEql.Data, .hash).type != void);
const HasCheapEqlUn = AutoArrayHashMapUnmanaged(i32, i32); const HasCheapEqlUn = AutoArrayHashMapUnmanaged(i32, i32);
const HasExpensiveEqlUn = AutoArrayHashMapUnmanaged([32]i32, i32); const HasExpensiveEqlUn = AutoArrayHashMapUnmanaged([32]i32, i32);
try testing.expect(meta.fieldInfo(HasCheapEqlUn.Data, .hash).type == void); try testing.expect(std.meta.fieldInfo(HasCheapEqlUn.Data, .hash).type == void);
try testing.expect(meta.fieldInfo(HasExpensiveEqlUn.Data, .hash).type != void); try testing.expect(std.meta.fieldInfo(HasExpensiveEqlUn.Data, .hash).type != void);
} }
test "sort" { test "sort" {
@ -2434,12 +2432,12 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K)
return struct { return struct {
fn hash(ctx: Context, key: K) u32 { fn hash(ctx: Context, key: K) u32 {
_ = ctx; _ = ctx;
if (comptime trait.hasUniqueRepresentation(K)) { if (std.meta.hasUniqueRepresentation(K)) {
return @as(u32, @truncate(Wyhash.hash(0, std.mem.asBytes(&key)))); return @truncate(Wyhash.hash(0, std.mem.asBytes(&key)));
} else { } else {
var hasher = Wyhash.init(0); var hasher = Wyhash.init(0);
autoHash(&hasher, key); autoHash(&hasher, key);
return @as(u32, @truncate(hasher.final())); return @truncate(hasher.final());
} }
} }
}.hash; }.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 { fn eql(ctx: Context, a: K, b: K, b_index: usize) bool {
_ = b_index; _ = b_index;
_ = ctx; _ = ctx;
return meta.eql(a, b); return std.meta.eql(a, b);
} }
}.eql; }.eql;
} }

View file

@ -153,11 +153,6 @@ pub fn Atomic(comptime T: type) type {
return @atomicRmw(T, &self.value, op, value, ordering); return @atomicRmw(T, &self.value, op, value, ordering);
} }
fn exportWhen(comptime condition: bool, comptime functions: type) type {
return if (condition) functions else struct {};
}
pub usingnamespace exportWhen(std.meta.trait.isNumber(T), struct {
pub inline fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) T { pub inline fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) T {
return self.rmw(.Add, value, ordering); return self.rmw(.Add, value, ordering);
} }
@ -173,9 +168,7 @@ pub fn Atomic(comptime T: type) type {
pub inline fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) T { pub inline fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) T {
return self.rmw(.Max, value, ordering); 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 { pub inline fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) T {
return self.rmw(.And, value, ordering); return self.rmw(.And, value, ordering);
} }
@ -307,9 +300,8 @@ pub fn Atomic(comptime T: type) type {
// TODO: emit appropriate tsan fence if compiling with tsan // TODO: emit appropriate tsan fence if compiling with tsan
_ = ordering; _ = 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; if (V == E) break :blk value;
const name: ?[]const u8 = switch (@typeInfo(V)) { const name: ?[]const u8 = switch (@typeInfo(V)) {
.EnumLiteral, .Enum => @tagName(value), .EnumLiteral, .Enum => @tagName(value),
.Pointer => if (std.meta.trait.isZigString(V)) value else null, .Pointer => value,
else => null, else => null,
}; };
if (name) |n| { if (name) |n| {

View file

@ -478,7 +478,7 @@ pub fn formatType(
return formatAddress(value, options, writer); 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); return try value.format(actual_fmt, options, writer);
} }
@ -611,15 +611,12 @@ pub fn formatType(
else => {}, else => {},
} }
} }
if (comptime std.meta.trait.isZigString(info.child)) {
for (value, 0..) |item, i| { for (value, 0..) |item, i| {
comptime checkTextFmt(actual_fmt); comptime checkTextFmt(actual_fmt);
if (i != 0) try formatBuf(", ", options, writer); if (i != 0) try formatBuf(", ", options, writer);
try formatBuf(item, options, writer); try formatBuf(item, options, writer);
} }
return; return;
}
invalidFmtError(fmt, value);
}, },
.Enum, .Union, .Struct => { .Enum, .Union, .Struct => {
return formatType(value.*, actual_fmt, options, writer, max_depth); return formatType(value.*, actual_fmt, options, writer, max_depth);

View file

@ -1,7 +1,6 @@
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const mem = std.mem; const mem = std.mem;
const meta = std.meta;
/// Describes how pointer types should be hashed. /// Describes how pointer types should be hashed.
pub const HashStrategy = enum { pub const HashStrategy = enum {
@ -69,7 +68,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
else => @TypeOf(hasher), 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) }); @call(.always_inline, Hasher.update, .{ hasher, mem.asBytes(&key) });
return; return;
} }
@ -97,7 +96,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
.signedness = .unsigned, .signedness = .unsigned,
} }), @bitCast(key)), strat), } }), @bitCast(key)), strat),
.unsigned => { .unsigned => {
if (comptime meta.trait.hasUniqueRepresentation(Key)) { if (std.meta.hasUniqueRepresentation(Key)) {
@call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key) }); @call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key) });
} else { } else {
// Take only the part containing the key value, the remaining // 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), .Array => hashArray(hasher, key, strat),
.Vector => |info| { .Vector => |info| {
if (comptime meta.trait.hasUniqueRepresentation(Key)) { if (std.meta.hasUniqueRepresentation(Key)) {
hasher.update(mem.asBytes(&key)); hasher.update(mem.asBytes(&key));
} else { } else {
comptime var i = 0; comptime var i = 0;
@ -140,7 +139,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
.Union => |info| { .Union => |info| {
if (info.tag_type) |tag_type| { if (info.tag_type) |tag_type| {
const tag = meta.activeTag(key); const tag = std.meta.activeTag(key);
hash(hasher, tag, strat); hash(hasher, tag, strat);
inline for (info.fields) |field| { inline for (info.fields) |field| {
if (@field(tag_type, field.name) == tag) { 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 { inline fn typeContainsSlice(comptime K: type) bool {
comptime { return switch (@typeInfo(K)) {
if (meta.trait.isSlice(K)) { .Pointer => |info| info.size == .Slice,
return true;
} inline .Struct, .Union => |info| {
if (meta.trait.is(.Struct)(K)) { inline for (info.fields) |field| {
inline for (@typeInfo(K).Struct.fields) |field| {
if (typeContainsSlice(field.type)) { if (typeContainsSlice(field.type)) {
return true; 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. /// Provides generic hashing for any eligible type.
@ -236,7 +229,7 @@ fn testHashDeepRecursive(key: anytype) u64 {
test "typeContainsSlice" { test "typeContainsSlice" {
comptime { 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([]const u8));
try testing.expect(!typeContainsSlice(u8)); try testing.expect(!typeContainsSlice(u8));

View file

@ -185,8 +185,6 @@ pub const XxHash64 = struct {
} }
pub fn update(self: *XxHash64, input: anytype) void { pub fn update(self: *XxHash64, input: anytype) void {
validateType(@TypeOf(input));
if (input.len < 32 - self.buf_len) { if (input.len < 32 - self.buf_len) {
@memcpy(self.buf[self.buf_len..][0..input.len], input); @memcpy(self.buf[self.buf_len..][0..input.len], input);
self.buf_len += input.len; self.buf_len += input.len;
@ -232,8 +230,6 @@ pub const XxHash64 = struct {
}; };
pub fn hash(seed: u64, input: anytype) u64 { pub fn hash(seed: u64, input: anytype) u64 {
validateType(@TypeOf(input));
if (input.len < 32) { if (input.len < 32) {
return finalize(seed +% prime_5, 0, input); return finalize(seed +% prime_5, 0, input);
} else { } else {
@ -315,8 +311,6 @@ pub const XxHash32 = struct {
} }
pub fn update(self: *XxHash32, input: []const u8) void { pub fn update(self: *XxHash32, input: []const u8) void {
validateType(@TypeOf(input));
if (input.len < 16 - self.buf_len) { if (input.len < 16 - self.buf_len) {
@memcpy(self.buf[self.buf_len..][0..input.len], input); @memcpy(self.buf[self.buf_len..][0..input.len], input);
self.buf_len += input.len; self.buf_len += input.len;
@ -416,8 +410,6 @@ pub const XxHash32 = struct {
} }
pub fn hash(seed: u32, input: anytype) u32 { pub fn hash(seed: u32, input: anytype) u32 {
validateType(@TypeOf(input));
if (input.len < 16) { if (input.len < 16) {
return finalize(seed +% prime_5, 0, input); return finalize(seed +% prime_5, 0, input);
} else { } else {
@ -587,8 +579,6 @@ pub const XxHash3 = struct {
// Public API - Oneshot // Public API - Oneshot
pub fn hash(seed: u64, input: anytype) u64 { pub fn hash(seed: u64, input: anytype) u64 {
validateType(@TypeOf(input));
const secret = &default_secret; const secret = &default_secret;
if (input.len > 240) return hashLong(seed, input); if (input.len > 240) return hashLong(seed, input);
if (input.len > 128) return hash240(seed, input, secret); 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 { pub fn update(self: *XxHash3, input: anytype) void {
validateType(@TypeOf(input));
self.total_len += input.len; self.total_len += input.len;
std.debug.assert(self.buffered <= self.buffer.len); std.debug.assert(self.buffered <= self.buffer.len);
@ -783,18 +771,6 @@ pub const XxHash3 = struct {
const verify = @import("verify.zig"); 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 { fn testExpect(comptime H: type, seed: anytype, input: []const u8, expected: u64) !void {
try expectEqual(expected, H.hash(seed, input)); try expectEqual(expected, H.hash(seed, input));

View file

@ -4,8 +4,6 @@ const assert = std.debug.assert;
const autoHash = std.hash.autoHash; const autoHash = std.hash.autoHash;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const meta = std.meta;
const trait = meta.trait;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Wyhash = std.hash.Wyhash; const Wyhash = std.hash.Wyhash;
@ -24,7 +22,7 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K)
return struct { return struct {
fn hash(ctx: Context, key: K) u64 { fn hash(ctx: Context, key: K) u64 {
_ = ctx; _ = ctx;
if (comptime trait.hasUniqueRepresentation(K)) { if (std.meta.hasUniqueRepresentation(K)) {
return Wyhash.hash(0, std.mem.asBytes(&key)); return Wyhash.hash(0, std.mem.asBytes(&key));
} else { } else {
var hasher = Wyhash.init(0); var hasher = Wyhash.init(0);
@ -39,7 +37,7 @@ pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K
return struct { return struct {
fn eql(ctx: Context, a: K, b: K) bool { fn eql(ctx: Context, a: K, b: K) bool {
_ = ctx; _ = ctx;
return meta.eql(a, b); return std.meta.eql(a, b);
} }
}.eql; }.eql;
} }

View file

@ -2,7 +2,6 @@ const std = @import("../std.zig");
const io = std.io; const io = std.io;
const assert = std.debug.assert; const assert = std.debug.assert;
const testing = std.testing; const testing = std.testing;
const trait = std.meta.trait;
const meta = std.meta; const meta = std.meta;
const math = std.math; 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 /// 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. /// 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 { 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 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
// related to shifting and casting. // related to shifting and casting.
const u_bit_count = @bitSizeOf(U); const u_bit_count = @bitSizeOf(U);

View file

@ -2,8 +2,6 @@ const std = @import("../std.zig");
const io = std.io; const io = std.io;
const testing = std.testing; const testing = std.testing;
const assert = std.debug.assert; const assert = std.debug.assert;
const trait = std.meta.trait;
const meta = std.meta;
const math = std.math; const math = std.math;
/// Creates a stream which allows for writing bit fields to another stream /// 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; if (bits == 0) return;
const U = @TypeOf(value); 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 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
// related to shifting and casting. // related to shifting and casting.

View file

@ -1,7 +1,5 @@
const std = @import("std"); const std = @import("std");
const io = std.io; const io = std.io;
const meta = std.meta;
const trait = std.trait;
const DefaultPrng = std.rand.DefaultPrng; const DefaultPrng = std.rand.DefaultPrng;
const expect = std.testing.expect; const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;

View file

@ -247,7 +247,7 @@ pub fn innerParse(
} }
}, },
.Enum => { .Enum => {
if (comptime std.meta.trait.hasFn("jsonParse")(T)) { if (std.meta.hasFn(T, "jsonParse")) {
return T.jsonParse(allocator, source, options); return T.jsonParse(allocator, source, options);
} }
@ -260,7 +260,7 @@ pub fn innerParse(
return sliceToEnum(T, slice); return sliceToEnum(T, slice);
}, },
.Union => |unionInfo| { .Union => |unionInfo| {
if (comptime std.meta.trait.hasFn("jsonParse")(T)) { if (std.meta.hasFn(T, "jsonParse")) {
return T.jsonParse(allocator, source, options); return T.jsonParse(allocator, source, options);
} }
@ -318,7 +318,7 @@ pub fn innerParse(
return r; return r;
} }
if (comptime std.meta.trait.hasFn("jsonParse")(T)) { if (std.meta.hasFn(T, "jsonParse")) {
return T.jsonParse(allocator, source, options); return T.jsonParse(allocator, source, options);
} }
@ -581,7 +581,7 @@ pub fn innerParseFromValue(
} }
}, },
.Enum => { .Enum => {
if (comptime std.meta.trait.hasFn("jsonParseFromValue")(T)) { if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options); return T.jsonParseFromValue(allocator, source, options);
} }
@ -593,7 +593,7 @@ pub fn innerParseFromValue(
} }
}, },
.Union => |unionInfo| { .Union => |unionInfo| {
if (comptime std.meta.trait.hasFn("jsonParseFromValue")(T)) { if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options); return T.jsonParseFromValue(allocator, source, options);
} }
@ -635,7 +635,7 @@ pub fn innerParseFromValue(
return r; return r;
} }
if (comptime std.meta.trait.hasFn("jsonParseFromValue")(T)) { if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options); return T.jsonParseFromValue(allocator, source, options);
} }

View file

@ -451,14 +451,14 @@ pub fn WriteStream(
} }
}, },
.Enum, .EnumLiteral => { .Enum, .EnumLiteral => {
if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { if (std.meta.hasFn(T, "jsonStringify")) {
return value.jsonStringify(self); return value.jsonStringify(self);
} }
return self.stringValue(@tagName(value)); return self.stringValue(@tagName(value));
}, },
.Union => { .Union => {
if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { if (std.meta.hasFn(T, "jsonStringify")) {
return value.jsonStringify(self); return value.jsonStringify(self);
} }
@ -487,7 +487,7 @@ pub fn WriteStream(
} }
}, },
.Struct => |S| { .Struct => |S| {
if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { if (std.meta.hasFn(T, "jsonStringify")) {
return value.jsonStringify(self); return value.jsonStringify(self);
} }

View file

@ -801,7 +801,7 @@ fn testDivFloor() !void {
/// zero. /// zero.
pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T { pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {
@setRuntimeSafety(false); @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); const info = @typeInfo(T);
switch (info) { switch (info) {
.ComptimeFloat, .Float => return @ceil(numerator / denominator), .ComptimeFloat, .Float => return @ceil(numerator / denominator),

View file

@ -4,8 +4,6 @@ const debug = std.debug;
const assert = debug.assert; const assert = debug.assert;
const math = std.math; const math = std.math;
const mem = @This(); const mem = @This();
const meta = std.meta;
const trait = meta.trait;
const testing = std.testing; const testing = std.testing;
const Endian = std.builtin.Endian; const Endian = std.builtin.Endian;
const native_endian = builtin.cpu.arch.endian(); const native_endian = builtin.cpu.arch.endian();
@ -736,7 +734,7 @@ test "span" {
} }
/// Helper for the return type of sliceTo() /// 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)) { switch (@typeInfo(T)) {
.Optional => |optional_info| { .Optional => |optional_info| {
return ?SliceTo(optional_info.child, end); 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. /// resulting slice is also sentinel terminated.
/// Pointer properties such as mutability and alignment are preserved. /// Pointer properties such as mutability and alignment are preserved.
/// C pointers are assumed to be non-null. /// 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) { if (@typeInfo(@TypeOf(ptr)) == .Optional) {
const non_null = ptr orelse return null; const non_null = ptr orelse return null;
return sliceTo(non_null, end); 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 /// 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))) { switch (@typeInfo(@TypeOf(ptr))) {
.Pointer => |ptr_info| switch (ptr_info.size) { .Pointer => |ptr_info| switch (ptr_info.size) {
.One => switch (@typeInfo(ptr_info.child)) { .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 > haystack.len) return null;
if (needle.len == 0) return haystack.len; 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); return lastIndexOfLinear(T, haystack, needle);
const haystack_bytes = sliceAsBytes(haystack); 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]); 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); return indexOfPosLinear(T, haystack, start_index, needle);
const haystack_bytes = sliceAsBytes(haystack); const haystack_bytes = sliceAsBytes(haystack);
@ -3368,14 +3366,8 @@ fn ReverseIterator(comptime T: type) type {
/// Iterates over a slice in reverse. /// Iterates over a slice in reverse.
pub fn reverseIterator(slice: anytype) ReverseIterator(@TypeOf(slice)) { 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" { test "reverseIterator" {
{ {
@ -3394,7 +3386,7 @@ test "reverseIterator" {
try testing.expectEqual(@as(?i32, null), it.next()); try testing.expectEqual(@as(?i32, null), it.next());
it = reverseIterator(slice); 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, 7), it.nextPtr().?.*);
try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*); try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*);
try testing.expectEqual(@as(?*const i32, null), 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()); try testing.expectEqual(@as(?i32, null), it.next());
it = reverseIterator(ptr_to_array); 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, 7), it.nextPtr().?.*);
try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*); try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*);
try testing.expectEqual(@as(?*const i32, null), it.nextPtr()); try testing.expectEqual(@as(?*const i32, null), it.nextPtr());
@ -3730,11 +3722,7 @@ fn CopyPtrAttrs(
} }
fn AsBytesReturnType(comptime P: type) type { fn AsBytesReturnType(comptime P: type) type {
if (!trait.isSingleItemPtr(P)) const size = @sizeOf(std.meta.Child(P));
@compileError("expected single item pointer, passed " ++ @typeName(P));
const size = @sizeOf(meta.Child(P));
return CopyPtrAttrs(P, .One, [size]u8); return CopyPtrAttrs(P, .One, [size]u8);
} }
@ -3818,21 +3806,13 @@ test "toBytes" {
} }
fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { 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); return CopyPtrAttrs(B, .One, T);
} }
/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type /// 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. /// backed by those bytes, preserving pointer attributes.
pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { 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" { test "bytesAsValue" {
@ -3872,7 +3852,7 @@ test "bytesAsValue" {
.big => "\xA1\xDE\xEF\xBE", .big => "\xA1\xDE\xEF\xBE",
}; };
const inst2 = bytesAsValue(S, inst_bytes); 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" { test "bytesAsValue preserves pointer attributes" {
@ -3905,14 +3885,6 @@ test "bytesToValue" {
} }
fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { 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); return CopyPtrAttrs(bytesType, .Slice, T);
} }
@ -4000,10 +3972,6 @@ test "bytesAsSlice preserves pointer attributes" {
} }
fn SliceAsBytesReturnType(comptime Slice: type) type { 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); return CopyPtrAttrs(Slice, .Slice, u8);
} }
@ -4012,15 +3980,15 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
const Slice = @TypeOf(slice); const Slice = @TypeOf(slice);
// a slice of zero-bit values always occupies zero bytes // 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 // let's not give an undefined pointer to @ptrCast
// it may be equal to zero and fail a null check // 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); 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" { test "sliceAsBytes" {

View file

@ -5,7 +5,6 @@ const math = std.math;
const testing = std.testing; const testing = std.testing;
const root = @import("root"); const root = @import("root");
pub const trait = @import("meta/trait.zig");
pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
const Type = std.builtin.Type; 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, /// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value,
/// or `null` if there is not one. /// or `null` if there is not one.
/// Types which cannot possibly have a sentinel will be a compile error. /// 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)) { switch (@typeInfo(T)) {
.Array => |info| { .Array => |info| {
const sentinel_ptr = info.sentinel orelse return null; 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"); @compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel");
} }
test "std.meta.sentinel" { test sentinel {
try testSentinel(); try testSentinel();
try comptime testSentinel(); try comptime testSentinel();
} }
@ -712,8 +712,6 @@ test "std.meta.activeTag" {
const TagPayloadType = TagPayload; const TagPayloadType = TagPayload;
pub fn TagPayloadByName(comptime U: type, comptime tag_name: []const u8) type { pub fn TagPayloadByName(comptime U: type, comptime tag_name: []const u8) type {
comptime debug.assert(trait.is(.Union)(U));
const info = @typeInfo(U).Union; const info = @typeInfo(U).Union;
inline for (info.fields) |field_info| { 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, 0)));
try std.testing.expect(!isError(math.divTrunc(u8, 5, 5))); 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`. //! use `std.crypto.random`.
//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will //! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
//! be faster and use substantially less stack space. //! be faster and use substantially less stack space.
//!
//! TODO(tiehuis): Benchmark these against other reference implementations.
const std = @import("std.zig"); const std = @import("std.zig");
const builtin = @import("builtin"); 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. /// 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`. /// `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 { 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 // This implementation works by summing the proportions and picking a
// point in [0, sum). We then loop over the proportions, accumulating // random point in [0, sum). We then loop over the proportions,
// until our accumulator is greater than the random point. // accumulating until our accumulator is greater than the random point.
const sum = s: {
var sum: T = 0; var sum: T = 0;
for (proportions) |v| { for (proportions) |v| sum += v;
sum += v; break :s sum;
} };
const point = if (comptime std.meta.trait.isSignedInt(T)) const point = switch (@typeInfo(T)) {
r.intRangeLessThan(T, 0, sum) .Int => |int_info| switch (int_info.signedness) {
else if (comptime std.meta.trait.isUnsignedInt(T)) .signed => r.intRangeLessThan(T, 0, sum),
r.uintLessThan(T, sum) .unsigned => r.uintLessThan(T, sum),
else if (comptime std.meta.trait.isFloat(T)) },
// take care that imprecision doesn't lead to a value slightly greater than 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)) .Float => @min(r.float(T) * sum, sum - std.math.floatEps(T)),
else else => @compileError("weightedIndex does not support proportions of type " ++
@compileError("weightedIndex does not support proportions of type " ++ @typeName(T)); @typeName(T)),
};
std.debug.assert(point < sum); assert(point < sum);
var accumulator: T = 0; var accumulator: T = 0;
for (proportions, 0..) |p, index| { for (proportions, 0..) |p, index| {
accumulator += p; accumulator += p;
if (point < accumulator) return index; if (point < accumulator) return index;
} } else unreachable;
unreachable;
} }
/// Returns the smallest of `Index` and `usize`. /// 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. /// Returns true if any specified feature is enabled.
pub fn featureSetHasAny(set: Set, features: anytype) bool { pub fn featureSetHasAny(set: Set, features: anytype) bool {
comptime std.debug.assert(std.meta.trait.isIndexable(@TypeOf(features)));
inline for (features) |feature| { inline for (features) |feature| {
if (set.isEnabled(@intFromEnum(@as(F, feature)))) return true; 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. /// Returns true if every specified feature is enabled.
pub fn featureSetHasAll(set: Set, features: anytype) bool { pub fn featureSetHasAll(set: Set, features: anytype) bool {
comptime std.debug.assert(std.meta.trait.isIndexable(@TypeOf(features)));
inline for (features) |feature| { inline for (features) |feature| {
if (!set.isEnabled(@intFromEnum(@as(F, feature)))) return false; 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`. /// signedness of the given type `T`.
/// Asserts `T` is an integer. /// Asserts `T` is an integer.
fn readLeb(comptime T: type, reader: anytype) !T { fn readLeb(comptime T: type, reader: anytype) !T {
if (comptime std.meta.trait.isSignedInt(T)) { return switch (@typeInfo(T).Int.signedness) {
return try leb.readILEB128(T, reader); .signed => try leb.readILEB128(T, reader),
} else { .unsigned => try leb.readULEB128(T, reader),
return try leb.readULEB128(T, reader); };
}
} }
/// Reads an enum type from the given 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 { 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}); const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});
if (num_kind == .float) if (num_kind == .float)
return Tag.float_literal.create(c.arena, str) return Tag.float_literal.create(c.arena, str)

View file

@ -542,7 +542,11 @@ test "vector division operators" {
const S = struct { const S = struct {
fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void { 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; const d0 = x / y;
for (@as([4]T, d0), 0..) |v, i| { for (@as([4]T, d0), 0..) |v, i| {
try expect(x[i] / y[i] == v); 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 { 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; const r0 = x % y;
for (@as([4]T, r0), 0..) |v, i| { for (@as([4]T, r0), 0..) |v, i| {
try expect(x[i] % y[i] == v); try expect(x[i] % y[i] == v);

View file

@ -5,10 +5,38 @@ pub export fn entry() void {
_ = sliceAsBytes(ohnoes); _ = sliceAsBytes(ohnoes);
_ = &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 // error
// backend=llvm // backend=llvm
// target=native // target=native
// //
// :8:63: error: expected type 'type', found 'bool' // :8:48: error: expected type 'type', found 'bool'