authorgravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2018-10-19 16:19:22-05:00
committergravatar for jhc@dismail.deJimmi Holst Christensen <jhc@dismail.de> 2018-10-19 17:19:22-04:00
log2a3fdd52ce7bd0cc577947e49bc8a3e69a34b6d0
tree4b48272c8d6fc1a03d996644e6739388460165fa
parentb9a53c261a4abe4cdeb74513ebef80907c6517c0

Add std.meta (#1662)

Implement std.meta

4 files changed, 1155 insertions(+), 0 deletions(-)

std/index.zig+2
......@@ -32,6 +32,7 @@ pub const io = @import("io.zig");
3232pub const json = @import("json.zig");
3333pub const macho = @import("macho.zig");
3434pub const math = @import("math/index.zig");
35pub const meta = @import("meta/index.zig");
3536pub const mem = @import("mem.zig");
3637pub const net = @import("net.zig");
3738pub const os = @import("os/index.zig");
......@@ -74,6 +75,7 @@ test "std" {
7475 _ = @import("json.zig");
7576 _ = @import("macho.zig");
7677 _ = @import("math/index.zig");
78 _ = @import("meta/index.zig");
7779 _ = @import("mem.zig");
7880 _ = @import("net.zig");
7981 _ = @import("heap.zig");
std/mem.zig+179
......@@ -4,6 +4,8 @@ const assert = debug.assert;
44const math = std.math;
55const builtin = @import("builtin");
66const mem = @This();
7const meta = std.meta;
8const trait = meta.trait;
79
810pub const Allocator = struct.{
911 pub const Error = error.{OutOfMemory};
......@@ -863,3 +865,180 @@ pub fn endianSwap(comptime T: type, x: T) T {
863865test "std.mem.endianSwap" {
864866 assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
865867}
868
869
870
871fn AsBytesReturnType(comptime P: type) type
872{
873 if(comptime !trait.isSingleItemPtr(P)) @compileError("expected single item "
874 ++ "pointer, passed " ++ @typeName(P));
875
876 const size = usize(@sizeOf(meta.Child(P)));
877 const alignment = comptime meta.alignment(P);
878 if(comptime trait.isConstPtr(P)) return *align(alignment) const [size]u8;
879 return *align(alignment) [size]u8;
880}
881
882///Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
883pub fn asBytes(ptr: var) AsBytesReturnType(@typeOf(ptr))
884{
885 const P = @typeOf(ptr);
886 return @ptrCast(AsBytesReturnType(P), ptr);
887}
888
889test "std.mem.asBytes"
890{
891 const deadbeef = u32(0xDEADBEEF);
892 const deadbeef_bytes = switch(builtin.endian)
893 {
894 builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
895 builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
896 };
897
898 debug.assert(std.mem.eql(u8, asBytes(&deadbeef), deadbeef_bytes));
899
900 var codeface = u32(0xC0DEFACE);
901 for(asBytes(&codeface).*) |*b| b.* = 0;
902 debug.assert(codeface == 0);
903
904 const S = packed struct.
905 {
906 a: u8,
907 b: u8,
908 c: u8,
909 d: u8,
910 };
911
912 const inst = S.{ .a = 0xBE, .b = 0xEF, .c = 0xDE, .d = 0xA1, };
913 debug.assert(std.mem.eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1"));
914}
915
916///Given any value, returns a copy of its bytes in an array.
917pub fn toBytes(value: var) [@sizeOf(@typeOf(value))]u8
918{
919 return asBytes(&value).*;
920}
921
922test "std.mem.toBytes"
923{
924 var my_bytes = toBytes(u32(0x12345678));
925 switch(builtin.endian)
926 {
927 builtin.Endian.Big => debug.assert(std.mem.eql(u8, my_bytes, "\x12\x34\x56\x78")),
928 builtin.Endian.Little => debug.assert(std.mem.eql(u8, my_bytes, "\x78\x56\x34\x12")),
929 }
930
931 my_bytes[0] = '\x99';
932 switch(builtin.endian)
933 {
934 builtin.Endian.Big => debug.assert(std.mem.eql(u8, my_bytes, "\x99\x34\x56\x78")),
935 builtin.Endian.Little => debug.assert(std.mem.eql(u8, my_bytes, "\x99\x56\x34\x12")),
936 }
937}
938
939
940fn BytesAsValueReturnType(comptime T: type, comptime B: type) type
941{
942 const size = usize(@sizeOf(T));
943
944 if(comptime !trait.is(builtin.TypeId.Pointer)(B) or meta.Child(B) != [size]u8)
945 {
946 @compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B));
947 }
948
949 const alignment = comptime meta.alignment(B);
950
951 return if(comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;
952}
953
954///Given a pointer to an array of bytes, returns a pointer to a value of the specified type
955/// backed by those bytes, preserving constness.
956pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @typeOf(bytes))
957{
958 return @ptrCast(BytesAsValueReturnType(T, @typeOf(bytes)), bytes);
959}
960
961test "std.mem.bytesAsValue"
962{
963 const deadbeef = u32(0xDEADBEEF);
964 const deadbeef_bytes = switch(builtin.endian)
965 {
966 builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
967 builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
968 };
969
970 debug.assert(deadbeef == bytesAsValue(u32, &deadbeef_bytes).*);
971
972 var codeface_bytes = switch(builtin.endian)
973 {
974 builtin.Endian.Big => "\xC0\xDE\xFA\xCE",
975 builtin.Endian.Little => "\xCE\xFA\xDE\xC0",
976 };
977 var codeface = bytesAsValue(u32, &codeface_bytes);
978 debug.assert(codeface.* == 0xC0DEFACE);
979 codeface.* = 0;
980 for(codeface_bytes) |b| debug.assert(b == 0);
981
982 const S = packed struct.
983 {
984 a: u8,
985 b: u8,
986 c: u8,
987 d: u8,
988 };
989
990 const inst = S.{ .a = 0xBE, .b = 0xEF, .c = 0xDE, .d = 0xA1, };
991 const inst_bytes = "\xBE\xEF\xDE\xA1";
992 const inst2 = bytesAsValue(S, &inst_bytes);
993 debug.assert(meta.eql(inst, inst2.*));
994}
995
996///Given a pointer to an array of bytes, returns a value of the specified type backed by a
997/// copy of those bytes.
998pub fn bytesToValue(comptime T: type, bytes: var) T
999{
1000 return bytesAsValue(T, &bytes).*;
1001}
1002 test "std.mem.bytesToValue"
1003{
1004 const deadbeef_bytes = switch(builtin.endian)
1005 {
1006 builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
1007 builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
1008 };
1009
1010 const deadbeef = bytesToValue(u32, deadbeef_bytes);
1011 debug.assert(deadbeef == u32(0xDEADBEEF));
1012}
1013
1014
1015fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type
1016{
1017 if(trait.isConstPtr(T)) return *const [length]meta.Child(meta.Child(T));
1018 return *[length]meta.Child(meta.Child(T));
1019}
1020
1021///Given a pointer to an array, returns a pointer to a portion of that array, preserving constness.
1022pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize)
1023 SubArrayPtrReturnType(@typeOf(ptr), length)
1024{
1025 debug.assert(start + length <= ptr.*.len);
1026
1027 const ReturnType = SubArrayPtrReturnType(@typeOf(ptr), length);
1028 const T = meta.Child(meta.Child(@typeOf(ptr)));
1029 return @ptrCast(ReturnType, &ptr[start]);
1030}
1031
1032test "std.mem.subArrayPtr"
1033{
1034 const a1 = "abcdef";
1035 const sub1 = subArrayPtr(&a1, 2, 3);
1036 debug.assert(std.mem.eql(u8, sub1.*, "cde"));
1037
1038 var a2 = "abcdef";
1039 var sub2 = subArrayPtr(&a2, 2, 3);
1040
1041 debug.assert(std.mem.eql(u8, sub2, "cde"));
1042 sub2[1] = 'X';
1043 debug.assert(std.mem.eql(u8, a2, "abcXef"));
1044}
\ No newline at end of file
std/meta/index.zig created+525
......@@ -0,0 +1,525 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const debug = std.debug;
4const mem = std.mem;
5const math = std.math;
6
7pub const trait = @import("trait.zig");
8
9const TypeId = builtin.TypeId;
10const TypeInfo = builtin.TypeInfo;
11
12pub fn tagName(v: var) []const u8 {
13 const T = @typeOf(v);
14 switch (@typeInfo(T)) {
15 TypeId.Enum => |info| {
16 const Tag = info.tag_type;
17 inline for (info.fields) |field| {
18 if (field.value == @enumToInt(v)) return field.name;
19 }
20
21 unreachable;
22 },
23 TypeId.Union => |info| {
24 const UnionTag = if(info.tag_type) |UT| UT else @compileError("union is untagged");
25 const Tag = @typeInfo(UnionTag).Enum.tag_type;
26 inline for (info.fields) |field| {
27 if (field.enum_field.?.value == @enumToInt(UnionTag(v)))
28 return field.name;
29 }
30
31 unreachable;
32 },
33 TypeId.ErrorSet => |info| {
34 inline for (info.errors) |err| {
35 if (err.value == @errorToInt(v)) return err.name;
36 }
37
38 unreachable;
39 },
40 else => @compileError("expected enum, error set or union type, found '"
41 ++ @typeName(T) ++ "'"),
42 }
43}
44
45test "std.meta.tagName" {
46 const E1 = enum.{
47 A,
48 B,
49 };
50 const E2 = enum(u8).{
51 C = 33,
52 D,
53 };
54 const U1 = union(enum).{
55 G: u8,
56 H: u16,
57 };
58 const U2 = union(E2).{
59 C: u8,
60 D: u16,
61 };
62
63 var u1g = U1.{ .G = 0 };
64 var u1h = U1.{ .H = 0 };
65 var u2a = U2.{ .C = 0 };
66 var u2b = U2.{ .D = 0 };
67
68 debug.assert(mem.eql(u8, tagName(E1.A), "A"));
69 debug.assert(mem.eql(u8, tagName(E1.B), "B"));
70 debug.assert(mem.eql(u8, tagName(E2.C), "C"));
71 debug.assert(mem.eql(u8, tagName(E2.D), "D"));
72 debug.assert(mem.eql(u8, tagName(error.E), "E"));
73 debug.assert(mem.eql(u8, tagName(error.F), "F"));
74 debug.assert(mem.eql(u8, tagName(u1g), "G"));
75 debug.assert(mem.eql(u8, tagName(u1h), "H"));
76 debug.assert(mem.eql(u8, tagName(u2a), "C"));
77 debug.assert(mem.eql(u8, tagName(u2b), "D"));
78}
79
80pub fn bitCount(comptime T: type) u32 {
81 return switch (@typeInfo(T)) {
82 TypeId.Int => |info| info.bits,
83 TypeId.Float => |info| info.bits,
84 else => @compileError("Expected int or float type, found '" ++ @typeName(T) ++ "'"),
85 };
86}
87
88test "std.meta.bitCount" {
89 debug.assert(bitCount(u8) == 8);
90 debug.assert(bitCount(f32) == 32);
91}
92
93pub fn alignment(comptime T: type) u29 {
94 //@alignOf works on non-pointer types
95 const P = if(comptime trait.is(TypeId.Pointer)(T)) T else *T;
96 return @typeInfo(P).Pointer.alignment;
97}
98
99test "std.meta.alignment" {
100 debug.assert(alignment(u8) == 1);
101 debug.assert(alignment(*align(1) u8) == 1);
102 debug.assert(alignment(*align(2) u8) == 2);
103 debug.assert(alignment([]align(1) u8) == 1);
104 debug.assert(alignment([]align(2) u8) == 2);
105}
106
107pub fn Child(comptime T: type) type {
108 return switch (@typeInfo(T)) {
109 TypeId.Array => |info| info.child,
110 TypeId.Pointer => |info| info.child,
111 TypeId.Optional => |info| info.child,
112 TypeId.Promise => |info| if(info.child) |child| child else null,
113 else => @compileError("Expected promise, pointer, optional, or array type, "
114 ++ "found '" ++ @typeName(T) ++ "'"),
115 };
116}
117
118test "std.meta.Child" {
119 debug.assert(Child([1]u8) == u8);
120 debug.assert(Child(*u8) == u8);
121 debug.assert(Child([]u8) == u8);
122 debug.assert(Child(?u8) == u8);
123 debug.assert(Child(promise->u8) == u8);
124}
125
126pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
127 return switch (@typeInfo(T)) {
128 TypeId.Struct => |info| info.layout,
129 TypeId.Enum => |info| info.layout,
130 TypeId.Union => |info| info.layout,
131 else => @compileError("Expected struct, enum or union type, found '"
132 ++ @typeName(T) ++ "'"),
133 };
134}
135
136test "std.meta.containerLayout" {
137 const E1 = enum.{
138 A,
139 };
140 const E2 = packed enum.{
141 A,
142 };
143 const E3 = extern enum.{
144 A,
145 };
146 const S1 = struct.{};
147 const S2 = packed struct.{};
148 const S3 = extern struct.{};
149 const U1 = union.{
150 a: u8,
151 };
152 const U2 = packed union.{
153 a: u8,
154 };
155 const U3 = extern union.{
156 a: u8,
157 };
158
159 debug.assert(containerLayout(E1) == TypeInfo.ContainerLayout.Auto);
160 debug.assert(containerLayout(E2) == TypeInfo.ContainerLayout.Packed);
161 debug.assert(containerLayout(E3) == TypeInfo.ContainerLayout.Extern);
162 debug.assert(containerLayout(S1) == TypeInfo.ContainerLayout.Auto);
163 debug.assert(containerLayout(S2) == TypeInfo.ContainerLayout.Packed);
164 debug.assert(containerLayout(S3) == TypeInfo.ContainerLayout.Extern);
165 debug.assert(containerLayout(U1) == TypeInfo.ContainerLayout.Auto);
166 debug.assert(containerLayout(U2) == TypeInfo.ContainerLayout.Packed);
167 debug.assert(containerLayout(U3) == TypeInfo.ContainerLayout.Extern);
168}
169
170pub fn definitions(comptime T: type) []TypeInfo.Definition {
171 return switch (@typeInfo(T)) {
172 TypeId.Struct => |info| info.defs,
173 TypeId.Enum => |info| info.defs,
174 TypeId.Union => |info| info.defs,
175 else => @compileError("Expected struct, enum or union type, found '"
176 ++ @typeName(T) ++ "'"),
177 };
178}
179
180test "std.meta.definitions" {
181 const E1 = enum.{
182 A,
183
184 fn a() void {}
185 };
186 const S1 = struct.{
187 fn a() void {}
188 };
189 const U1 = union.{
190 a: u8,
191
192 fn a() void {}
193 };
194
195 const defs = comptime [][]TypeInfo.Definition.{
196 definitions(E1),
197 definitions(S1),
198 definitions(U1),
199 };
200
201 inline for (defs) |def| {
202 debug.assert(def.len == 1);
203 debug.assert(comptime mem.eql(u8, def[0].name, "a"));
204 }
205}
206
207pub fn definitionInfo(comptime T: type, comptime def_name: []const u8) TypeInfo.Definition {
208 inline for (comptime definitions(T)) |def| {
209 if (comptime mem.eql(u8, def.name, def_name))
210 return def;
211 }
212
213 @compileError("'" ++ @typeName(T) ++ "' has no definition '" ++ def_name ++ "'");
214}
215
216test "std.meta.definitionInfo" {
217 const E1 = enum.{
218 A,
219
220 fn a() void {}
221 };
222 const S1 = struct.{
223 fn a() void {}
224 };
225 const U1 = union.{
226 a: u8,
227
228 fn a() void {}
229 };
230
231 const infos = comptime []TypeInfo.Definition.{
232 definitionInfo(E1, "a"),
233 definitionInfo(S1, "a"),
234 definitionInfo(U1, "a"),
235 };
236
237 inline for (infos) |info| {
238 debug.assert(comptime mem.eql(u8, info.name, "a"));
239 debug.assert(!info.is_pub);
240 }
241}
242
243pub fn fields(comptime T: type) switch (@typeInfo(T)) {
244 TypeId.Struct => []TypeInfo.StructField,
245 TypeId.Union => []TypeInfo.UnionField,
246 TypeId.ErrorSet => []TypeInfo.Error,
247 TypeId.Enum => []TypeInfo.EnumField,
248 else => @compileError("Expected struct, union, error set or enum type, found '"
249 ++ @typeName(T) ++ "'"),
250} {
251 return switch (@typeInfo(T)) {
252 TypeId.Struct => |info| info.fields,
253 TypeId.Union => |info| info.fields,
254 TypeId.Enum => |info| info.fields,
255 TypeId.ErrorSet => |info| info.errors,
256 else => @compileError("Expected struct, union, error set or enum type, found '"
257 ++ @typeName(T) ++ "'"),
258 };
259}
260
261test "std.meta.fields" {
262 const E1 = enum.{
263 A,
264 };
265 const E2 = error.{A};
266 const S1 = struct.{
267 a: u8,
268 };
269 const U1 = union.{
270 a: u8,
271 };
272
273 const e1f = comptime fields(E1);
274 const e2f = comptime fields(E2);
275 const sf = comptime fields(S1);
276 const uf = comptime fields(U1);
277
278 debug.assert(e1f.len == 1);
279 debug.assert(e2f.len == 1);
280 debug.assert(sf.len == 1);
281 debug.assert(uf.len == 1);
282 debug.assert(mem.eql(u8, e1f[0].name, "A"));
283 debug.assert(mem.eql(u8, e2f[0].name, "A"));
284 debug.assert(mem.eql(u8, sf[0].name, "a"));
285 debug.assert(mem.eql(u8, uf[0].name, "a"));
286 debug.assert(comptime sf[0].field_type == u8);
287 debug.assert(comptime uf[0].field_type == u8);
288}
289
290pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) {
291 TypeId.Struct => TypeInfo.StructField,
292 TypeId.Union => TypeInfo.UnionField,
293 TypeId.ErrorSet => TypeInfo.Error,
294 TypeId.Enum => TypeInfo.EnumField,
295 else => @compileError("Expected struct, union, error set or enum type, found '"
296 ++ @typeName(T) ++ "'"),
297} {
298 inline for (comptime fields(T)) |field| {
299 if (comptime mem.eql(u8, field.name, field_name))
300 return field;
301 }
302
303 @compileError("'" ++ @typeName(T) ++ "' has no field '" ++ field_name ++ "'");
304}
305
306test "std.meta.fieldInfo" {
307 const E1 = enum.{
308 A,
309 };
310 const E2 = error.{A};
311 const S1 = struct.{
312 a: u8,
313 };
314 const U1 = union.{
315 a: u8,
316 };
317
318 const e1f = comptime fieldInfo(E1, "A");
319 const e2f = comptime fieldInfo(E2, "A");
320 const sf = comptime fieldInfo(S1, "a");
321 const uf = comptime fieldInfo(U1, "a");
322
323 debug.assert(mem.eql(u8, e1f.name, "A"));
324 debug.assert(mem.eql(u8, e2f.name, "A"));
325 debug.assert(mem.eql(u8, sf.name, "a"));
326 debug.assert(mem.eql(u8, uf.name, "a"));
327 debug.assert(comptime sf.field_type == u8);
328 debug.assert(comptime uf.field_type == u8);
329}
330
331pub fn TagType(comptime T: type) type {
332 return switch (@typeInfo(T)) {
333 TypeId.Enum => |info| info.tag_type,
334 TypeId.Union => |info| if(info.tag_type) |Tag| Tag else null,
335 else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"),
336 };
337}
338
339test "std.meta.TagType" {
340 const E = enum(u8).{
341 C = 33,
342 D,
343 };
344 const U = union(E).{
345 C: u8,
346 D: u16,
347 };
348
349 debug.assert(TagType(E) == u8);
350 debug.assert(TagType(U) == E);
351}
352
353
354
355///Returns the active tag of a tagged union
356pub fn activeTag(u: var) @TagType(@typeOf(u))
357{
358 const T = @typeOf(u);
359 return @TagType(T)(u);
360}
361
362test "std.meta.activeTag"
363{
364 const UE = enum.
365 {
366 Int,
367 Float,
368 };
369
370 const U = union(UE).
371 {
372 Int: u32,
373 Float: f32,
374 };
375
376 var u = U.{ .Int = 32, };
377 debug.assert(activeTag(u) == UE.Int);
378
379 u = U.{ .Float = 112.9876, };
380 debug.assert(activeTag(u) == UE.Float);
381
382}
383
384///Compares two of any type for equality. Containers are compared on a field-by-field basis,
385/// where possible. Pointers are not followed.
386pub fn eql(a: var, b: @typeOf(a)) bool
387{
388 const T = @typeOf(a);
389
390 switch(@typeId(T))
391 {
392 builtin.TypeId.Struct =>
393 {
394 const info = @typeInfo(T).Struct;
395
396 inline for(info.fields) |field_info|
397 {
398 if(!eql(@field(a, field_info.name),
399 @field(b, field_info.name))) return false;
400 }
401 return true;
402 },
403 builtin.TypeId.ErrorUnion =>
404 {
405 if(a) |a_p|
406 {
407 if(b) |b_p| return eql(a_p, b_p) else |_| return false;
408 }
409 else |a_e|
410 {
411 if(b) |_| return false else |b_e| return a_e == b_e;
412 }
413 },
414 builtin.TypeId.Union =>
415 {
416 const info = @typeInfo(T).Union;
417
418 if(info.tag_type) |_|
419 {
420 const tag_a = activeTag(a);
421 const tag_b = activeTag(b);
422 if(tag_a != tag_b) return false;
423
424 inline for(info.fields) |field_info|
425 {
426 const enum_field = field_info.enum_field.?;
427 if(enum_field.value == @enumToInt(tag_a))
428 {
429 return eql(@field(a, enum_field.name),
430 @field(b, enum_field.name));
431 }
432 }
433 return false;
434 }
435
436 @compileError("cannot compare untagged union type " ++ @typeName(T));
437 },
438 builtin.TypeId.Array =>
439 {
440 if(a.len != b.len) return false;
441 for(a) |e, i| if(!eql(e, b[i])) return false;
442 return true;
443 },
444 builtin.TypeId.Pointer =>
445 {
446 const info = @typeInfo(T).Pointer;
447 switch(info.size)
448 {
449 builtin.TypeInfo.Pointer.Size.One,
450 builtin.TypeInfo.Pointer.Size.Many => return a == b,
451 builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
452 }
453 },
454 else => return a == b,
455 }
456}
457
458
459test "std.meta.eql"
460{
461 const S = struct.
462 {
463 a: u32,
464 b: f64,
465 c: [5]u8,
466 };
467
468 const U = union(enum).
469 {
470 s: S,
471 f: f32,
472 };
473
474 const s_1 = S.
475 {
476 .a = 134,
477 .b = 123.3,
478 .c = "12345",
479 };
480
481 const s_2 = S.
482 {
483 .a = 1,
484 .b = 123.3,
485 .c = "54321",
486 };
487
488 const s_3 = S.
489 {
490 .a = 134,
491 .b = 123.3,
492 .c = "12345",
493 };
494
495 const u_1 = U.{ .f = 24, };
496 const u_2 = U.{ .s = s_1, };
497 const u_3 = U.{ .f = 24, };
498
499 debug.assert(eql(s_1, s_3));
500 debug.assert(eql(&s_1, &s_1));
501 debug.assert(!eql(&s_1, &s_3));
502 debug.assert(eql(u_1, u_3));
503 debug.assert(!eql(u_1, u_2));
504
505 var a1 = "abcdef";
506 var a2 = "abcdef";
507 var a3 = "ghijkl";
508
509 debug.assert(eql(a1, a2));
510 debug.assert(!eql(a1, a3));
511 debug.assert(!eql(a1[0..], a2[0..]));
512
513 const EU = struct.
514 {
515 fn tst(err: bool) !u8
516 {
517 if(err) return error.Error;
518 return u8(5);
519 }
520 };
521
522 debug.assert(eql(EU.tst(true), EU.tst(true)));
523 debug.assert(eql(EU.tst(false), EU.tst(false)));
524 debug.assert(!eql(EU.tst(false), EU.tst(true)));
525}
\ No newline at end of file
std/meta/trait.zig created+449
......@@ -0,0 +1,449 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const mem = std.mem;
4const debug = std.debug;
5const warn = debug.warn;
6
7const meta = @import("index.zig");
8
9//This is necessary if we want to return generic functions directly because of how the
10// the type erasure works. see: #1375
11fn traitFnWorkaround(comptime T: type) bool
12{
13 return false;
14}
15
16pub const TraitFn = @typeOf(traitFnWorkaround);
17
18///
19
20//////Trait generators
21
22//Need TraitList because compiler can't do varargs at comptime yet
23pub const TraitList = []const TraitFn;
24pub fn multiTrait(comptime traits: TraitList) TraitFn
25{
26 const Closure = struct.
27 {
28 pub fn trait(comptime T: type) bool
29 {
30 inline for(traits) |t| if(!t(T)) return false;
31 return true;
32 }
33 };
34 return Closure.trait;
35}
36
37test "std.meta.trait.multiTrait"
38{
39 const Vector2 = struct.
40 {
41 const MyType = @This();
42
43 x: u8,
44 y: u8,
45
46 pub fn add(self: MyType, other: MyType) MyType
47 {
48 return MyType.
49 {
50 .x = self.x + other.x,
51 .y = self.y + other.y,
52 };
53 }
54 };
55
56 const isVector = multiTrait
57 (
58 TraitList.
59 {
60 hasFn("add"),
61 hasField("x"),
62 hasField("y"),
63 }
64 );
65 debug.assert(isVector(Vector2));
66 debug.assert(!isVector(u8));
67}
68
69///
70
71pub fn hasDef(comptime name: []const u8) TraitFn
72{
73 const Closure = struct.
74 {
75 pub fn trait(comptime T: type) bool
76 {
77 const info = @typeInfo(T);
78 const defs = switch(info)
79 {
80 builtin.TypeId.Struct => |s| s.defs,
81 builtin.TypeId.Union => |u| u.defs,
82 builtin.TypeId.Enum => |e| e.defs,
83 else => return false,
84 };
85
86 inline for(defs) |def|
87 {
88 if(mem.eql(u8, def.name, name)) return def.is_pub;
89 }
90
91 return false;
92 }
93 };
94 return Closure.trait;
95}
96
97test "std.meta.trait.hasDef"
98{
99 const TestStruct = struct.
100 {
101 pub const value = u8(16);
102 };
103
104 const TestStructFail = struct.
105 {
106 const value = u8(16);
107 };
108
109 debug.assert(hasDef("value")(TestStruct));
110 debug.assert(!hasDef("value")(TestStructFail));
111 debug.assert(!hasDef("value")(*TestStruct));
112 debug.assert(!hasDef("value")(**TestStructFail));
113 debug.assert(!hasDef("x")(TestStruct));
114 debug.assert(!hasDef("value")(u8));
115}
116
117///
118pub fn hasFn(comptime name: []const u8) TraitFn
119{
120 const Closure = struct.
121 {
122 pub fn trait(comptime T: type) bool
123 {
124 if(!comptime hasDef(name)(T)) return false;
125 const DefType = @typeOf(@field(T, name));
126 const def_type_id = @typeId(DefType);
127 return def_type_id == builtin.TypeId.Fn;
128 }
129 };
130 return Closure.trait;
131}
132
133test "std.meta.trait.hasFn"
134{
135 const TestStruct = struct.
136 {
137 pub fn useless() void {}
138 };
139
140 debug.assert(hasFn("useless")(TestStruct));
141 debug.assert(!hasFn("append")(TestStruct));
142 debug.assert(!hasFn("useless")(u8));
143}
144
145///
146pub fn hasField(comptime name: []const u8) TraitFn
147{
148 const Closure = struct.
149 {
150 pub fn trait(comptime T: type) bool
151 {
152 const info = @typeInfo(T);
153 const fields = switch(info)
154 {
155 builtin.TypeId.Struct => |s| s.fields,
156 builtin.TypeId.Union => |u| u.fields,
157 builtin.TypeId.Enum => |e| e.fields,
158 else => return false,
159 };
160
161 inline for(fields) |field|
162 {
163 if(mem.eql(u8, field.name, name)) return true;
164 }
165
166 return false;
167 }
168 };
169 return Closure.trait;
170}
171
172test "std.meta.trait.hasField"
173{
174 const TestStruct = struct.
175 {
176 value: u32,
177 };
178
179 debug.assert(hasField("value")(TestStruct));
180 debug.assert(!hasField("value")(*TestStruct));
181 debug.assert(!hasField("x")(TestStruct));
182 debug.assert(!hasField("x")(**TestStruct));
183 debug.assert(!hasField("value")(u8));
184}
185
186///
187
188pub fn is(comptime id: builtin.TypeId) TraitFn
189{
190 const Closure = struct.
191 {
192 pub fn trait(comptime T: type) bool
193 {
194 return id == @typeId(T);
195 }
196 };
197 return Closure.trait;
198}
199
200test "std.meta.trait.is"
201{
202 debug.assert(is(builtin.TypeId.Int)(u8));
203 debug.assert(!is(builtin.TypeId.Int)(f32));
204 debug.assert(is(builtin.TypeId.Pointer)(*u8));
205 debug.assert(is(builtin.TypeId.Void)(void));
206 debug.assert(!is(builtin.TypeId.Optional)(error));
207}
208
209///
210
211pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn
212{
213 const Closure = struct.
214 {
215 pub fn trait(comptime T: type) bool
216 {
217 if(!comptime isSingleItemPtr(T)) return false;
218 return id == @typeId(meta.Child(T));
219 }
220 };
221 return Closure.trait;
222}
223
224test "std.meta.trait.isPtrTo"
225{
226 debug.assert(!isPtrTo(builtin.TypeId.Struct)(struct.{}));
227 debug.assert(isPtrTo(builtin.TypeId.Struct)(*struct.{}));
228 debug.assert(!isPtrTo(builtin.TypeId.Struct)(**struct.{}));
229}
230
231
232///////////Strait trait Fns
233
234//@TODO:
235// Somewhat limited since we can't apply this logic to normal variables, fields, or
236// Fns yet. Should be isExternType?
237pub fn isExtern(comptime T: type) bool
238{
239 const Extern = builtin.TypeInfo.ContainerLayout.Extern;
240 const info = @typeInfo(T);
241 return switch(info)
242 {
243 builtin.TypeId.Struct => |s| s.layout == Extern,
244 builtin.TypeId.Union => |u| u.layout == Extern,
245 builtin.TypeId.Enum => |e| e.layout == Extern,
246 else => false,
247 };
248}
249
250test "std.meta.trait.isExtern"
251{
252 const TestExStruct = extern struct.{};
253 const TestStruct = struct.{};
254
255 debug.assert(isExtern(TestExStruct));
256 debug.assert(!isExtern(TestStruct));
257 debug.assert(!isExtern(u8));
258}
259
260///
261
262pub fn isPacked(comptime T: type) bool
263{
264 const Packed = builtin.TypeInfo.ContainerLayout.Packed;
265 const info = @typeInfo(T);
266 return switch(info)
267 {
268 builtin.TypeId.Struct => |s| s.layout == Packed,
269 builtin.TypeId.Union => |u| u.layout == Packed,
270 builtin.TypeId.Enum => |e| e.layout == Packed,
271 else => false,
272 };
273}
274
275test "std.meta.trait.isPacked"
276{
277 const TestPStruct = packed struct.{};
278 const TestStruct = struct.{};
279
280 debug.assert(isPacked(TestPStruct));
281 debug.assert(!isPacked(TestStruct));
282 debug.assert(!isPacked(u8));
283}
284
285///
286
287pub fn isSingleItemPtr(comptime T: type) bool
288{
289 if(comptime is(builtin.TypeId.Pointer)(T))
290 {
291 const info = @typeInfo(T);
292 return info.Pointer.size == builtin.TypeInfo.Pointer.Size.One;
293 }
294 return false;
295}
296
297test "std.meta.trait.isSingleItemPtr"
298{
299 const array = []u8.{0} ** 10;
300 debug.assert(isSingleItemPtr(@typeOf(&array[0])));
301 debug.assert(!isSingleItemPtr(@typeOf(array)));
302 debug.assert(!isSingleItemPtr(@typeOf(array[0..1])));
303}
304
305///
306
307pub fn isManyItemPtr(comptime T: type) bool
308{
309 if(comptime is(builtin.TypeId.Pointer)(T))
310 {
311 const info = @typeInfo(T);
312 return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Many;
313 }
314 return false;
315}
316
317test "std.meta.trait.isManyItemPtr"
318{
319 const array = []u8.{0} ** 10;
320 const mip = @ptrCast([*]const u8, &array[0]);
321 debug.assert(isManyItemPtr(@typeOf(mip)));
322 debug.assert(!isManyItemPtr(@typeOf(array)));
323 debug.assert(!isManyItemPtr(@typeOf(array[0..1])));
324}
325
326///
327
328pub fn isSlice(comptime T: type) bool
329{
330 if(comptime is(builtin.TypeId.Pointer)(T))
331 {
332 const info = @typeInfo(T);
333 return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Slice;
334 }
335 return false;
336}
337
338test "std.meta.trait.isSlice"
339{
340 const array = []u8.{0} ** 10;
341 debug.assert(isSlice(@typeOf(array[0..])));
342 debug.assert(!isSlice(@typeOf(array)));
343 debug.assert(!isSlice(@typeOf(&array[0])));
344}
345
346///
347
348pub fn isIndexable(comptime T: type) bool
349{
350 if(comptime is(builtin.TypeId.Pointer)(T))
351 {
352 const info = @typeInfo(T);
353 if(info.Pointer.size == builtin.TypeInfo.Pointer.Size.One)
354 {
355 if(comptime is(builtin.TypeId.Array)(meta.Child(T))) return true;
356 return false;
357 }
358 return true;
359 }
360 return comptime is(builtin.TypeId.Array)(T);
361}
362
363test "std.meta.trait.isIndexable"
364{
365 const array = []u8.{0} ** 10;
366 const slice = array[0..];
367
368 debug.assert(isIndexable(@typeOf(array)));
369 debug.assert(isIndexable(@typeOf(&array)));
370 debug.assert(isIndexable(@typeOf(slice)));
371 debug.assert(!isIndexable(meta.Child(@typeOf(slice))));
372}
373
374///
375
376pub fn isNumber(comptime T: type) bool
377{
378 return switch(@typeId(T))
379 {
380 builtin.TypeId.Int,
381 builtin.TypeId.Float,
382 builtin.TypeId.ComptimeInt,
383 builtin.TypeId.ComptimeFloat => true,
384 else => false,
385 };
386}
387
388test "std.meta.trait.isNumber"
389{
390 const NotANumber = struct.
391 {
392 number: u8,
393 };
394
395 debug.assert(isNumber(u32));
396 debug.assert(isNumber(f32));
397 debug.assert(isNumber(u64));
398 debug.assert(isNumber(@typeOf(102)));
399 debug.assert(isNumber(@typeOf(102.123)));
400 debug.assert(!isNumber([]u8));
401 debug.assert(!isNumber(NotANumber));
402}
403
404///
405
406pub fn isConstPtr(comptime T: type) bool
407{
408 if(!comptime is(builtin.TypeId.Pointer)(T)) return false;
409 const info = @typeInfo(T);
410 return info.Pointer.is_const;
411}
412
413test "std.meta.trait.isConstPtr"
414{
415 var t = u8(0);
416 const c = u8(0);
417 debug.assert(isConstPtr(*const @typeOf(t)));
418 debug.assert(isConstPtr(@typeOf(&c)));
419 debug.assert(!isConstPtr(*@typeOf(t)));
420 debug.assert(!isConstPtr(@typeOf(6)));
421}
422
423///
424
425pub fn isContainer(comptime T: type) bool
426{
427 const info = @typeInfo(T);
428 return switch(info)
429 {
430 builtin.TypeId.Struct => true,
431 builtin.TypeId.Union => true,
432 builtin.TypeId.Enum => true,
433 else => false,
434 };
435}
436
437test "std.meta.trait.isContainer"
438{
439 const TestStruct = struct.{};
440 const TestUnion = union.{ a: void, };
441 const TestEnum = enum.{ A, B, };
442
443 debug.assert(isContainer(TestStruct));
444 debug.assert(isContainer(TestUnion));
445 debug.assert(isContainer(TestEnum));
446 debug.assert(!isContainer(u8));
447}
448
449///
\ No newline at end of file