master
1const builtin = @import("builtin");
2const std = @import("std.zig");
3const debug = std.debug;
4const mem = std.mem;
5const math = std.math;
6const testing = std.testing;
7const root = @import("root");
8
9pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
10
11const Type = std.builtin.Type;
12
13test {
14 _ = TrailerFlags;
15}
16
17/// Returns the variant of an enum type, `T`, which is named `str`, or `null` if no such variant exists.
18pub fn stringToEnum(comptime T: type, str: []const u8) ?T {
19 // Using StaticStringMap here is more performant, but it will start to take too
20 // long to compile if the enum is large enough, due to the current limits of comptime
21 // performance when doing things like constructing lookup maps at comptime.
22 // TODO The '100' here is arbitrary and should be increased when possible:
23 // - https://github.com/ziglang/zig/issues/4055
24 // - https://github.com/ziglang/zig/issues/3863
25 if (@typeInfo(T).@"enum".fields.len <= 100) {
26 const kvs = comptime build_kvs: {
27 const EnumKV = struct { []const u8, T };
28 var kvs_array: [@typeInfo(T).@"enum".fields.len]EnumKV = undefined;
29 for (@typeInfo(T).@"enum".fields, 0..) |enumField, i| {
30 kvs_array[i] = .{ enumField.name, @field(T, enumField.name) };
31 }
32 break :build_kvs kvs_array[0..];
33 };
34 const map = std.StaticStringMap(T).initComptime(kvs);
35 return map.get(str);
36 } else {
37 inline for (@typeInfo(T).@"enum".fields) |enumField| {
38 if (mem.eql(u8, str, enumField.name)) {
39 return @field(T, enumField.name);
40 }
41 }
42 return null;
43 }
44}
45
46test stringToEnum {
47 const E1 = enum {
48 A,
49 B,
50 };
51 try testing.expect(E1.A == stringToEnum(E1, "A").?);
52 try testing.expect(E1.B == stringToEnum(E1, "B").?);
53 try testing.expect(null == stringToEnum(E1, "C"));
54}
55
56/// Returns the alignment of type T.
57/// Note that if T is a pointer type the result is different than the one
58/// returned by @alignOf(T).
59/// If T is a pointer type the alignment of the type it points to is returned.
60pub fn alignment(comptime T: type) comptime_int {
61 return switch (@typeInfo(T)) {
62 .optional => |info| switch (@typeInfo(info.child)) {
63 .pointer, .@"fn" => alignment(info.child),
64 else => @alignOf(T),
65 },
66 .pointer => |info| info.alignment,
67 else => @alignOf(T),
68 };
69}
70
71test alignment {
72 try testing.expect(alignment(u8) == 1);
73 try testing.expect(alignment(*align(1) u8) == 1);
74 try testing.expect(alignment(*align(2) u8) == 2);
75 try testing.expect(alignment([]align(1) u8) == 1);
76 try testing.expect(alignment([]align(2) u8) == 2);
77 try testing.expect(alignment(fn () void) > 0);
78 try testing.expect(alignment(*const fn () void) > 0);
79 try testing.expect(alignment(*align(128) const fn () void) == 128);
80}
81
82/// Given a parameterized type (array, vector, pointer, optional), returns the "child type".
83pub fn Child(comptime T: type) type {
84 return switch (@typeInfo(T)) {
85 .array => |info| info.child,
86 .vector => |info| info.child,
87 .pointer => |info| info.child,
88 .optional => |info| info.child,
89 else => @compileError("Expected pointer, optional, array or vector type, found '" ++ @typeName(T) ++ "'"),
90 };
91}
92
93test Child {
94 try testing.expect(Child([1]u8) == u8);
95 try testing.expect(Child(*u8) == u8);
96 try testing.expect(Child([]u8) == u8);
97 try testing.expect(Child(?u8) == u8);
98 try testing.expect(Child(@Vector(2, u8)) == u8);
99}
100
101/// Given a "memory span" type (array, slice, vector, or pointer to such), returns the "element type".
102pub fn Elem(comptime T: type) type {
103 switch (@typeInfo(T)) {
104 .array => |info| return info.child,
105 .vector => |info| return info.child,
106 .pointer => |info| switch (info.size) {
107 .one => switch (@typeInfo(info.child)) {
108 .array => |array_info| return array_info.child,
109 .vector => |vector_info| return vector_info.child,
110 else => {},
111 },
112 .many, .c, .slice => return info.child,
113 },
114 .optional => |info| return Elem(info.child),
115 else => {},
116 }
117 @compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'");
118}
119
120test Elem {
121 try testing.expect(Elem([1]u8) == u8);
122 try testing.expect(Elem([*]u8) == u8);
123 try testing.expect(Elem([]u8) == u8);
124 try testing.expect(Elem(*[10]u8) == u8);
125 try testing.expect(Elem(@Vector(2, u8)) == u8);
126 try testing.expect(Elem(*@Vector(2, u8)) == u8);
127 try testing.expect(Elem(?[*]u8) == u8);
128}
129
130/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value,
131/// or `null` if there is not one.
132/// Types which cannot possibly have a sentinel will be a compile error.
133/// Result is always comptime-known.
134pub inline fn sentinel(comptime T: type) ?Elem(T) {
135 switch (@typeInfo(T)) {
136 .array => |info| return info.sentinel(),
137 .pointer => |info| {
138 switch (info.size) {
139 .many, .slice => return info.sentinel(),
140 .one => switch (@typeInfo(info.child)) {
141 .array => |array_info| return array_info.sentinel(),
142 else => {},
143 },
144 else => {},
145 }
146 },
147 else => {},
148 }
149 @compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel");
150}
151
152test sentinel {
153 try testSentinel();
154 try comptime testSentinel();
155}
156
157fn testSentinel() !void {
158 try testing.expectEqual(@as(u8, 0), sentinel([:0]u8).?);
159 try testing.expectEqual(@as(u8, 0), sentinel([*:0]u8).?);
160 try testing.expectEqual(@as(u8, 0), sentinel([5:0]u8).?);
161 try testing.expectEqual(@as(u8, 0), sentinel(*const [5:0]u8).?);
162
163 try testing.expect(sentinel([]u8) == null);
164 try testing.expect(sentinel([*]u8) == null);
165 try testing.expect(sentinel([5]u8) == null);
166 try testing.expect(sentinel(*const [5]u8) == null);
167}
168
169/// Given a "memory span" type, returns the same type except with the given sentinel value.
170pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
171 switch (@typeInfo(T)) {
172 .pointer => |info| switch (info.size) {
173 .one => switch (@typeInfo(info.child)) {
174 .array => |array_info| return @Pointer(.one, .{
175 .@"const" = info.is_const,
176 .@"volatile" = info.is_volatile,
177 .@"allowzero" = info.is_allowzero,
178 .@"align" = info.alignment,
179 .@"addrspace" = info.address_space,
180 }, [array_info.len:sentinel_val]array_info.child, null),
181 else => {},
182 },
183 .many, .slice => |size| return @Pointer(size, .{
184 .@"const" = info.is_const,
185 .@"volatile" = info.is_volatile,
186 .@"allowzero" = info.is_allowzero,
187 .@"align" = info.alignment,
188 .@"addrspace" = info.address_space,
189 }, info.child, sentinel_val),
190 else => {},
191 },
192 .optional => |info| switch (@typeInfo(info.child)) {
193 .pointer => |ptr_info| switch (ptr_info.size) {
194 .many => return ?@Pointer(.many, .{
195 .@"const" = ptr_info.is_const,
196 .@"volatile" = ptr_info.is_volatile,
197 .@"allowzero" = ptr_info.is_allowzero,
198 .@"align" = ptr_info.alignment,
199 .@"addrspace" = ptr_info.address_space,
200 .child = ptr_info.child,
201 }, ptr_info.child, sentinel_val),
202 else => {},
203 },
204 else => {},
205 },
206 else => {},
207 }
208 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
209}
210
211pub fn containerLayout(comptime T: type) Type.ContainerLayout {
212 return switch (@typeInfo(T)) {
213 .@"struct" => |info| info.layout,
214 .@"union" => |info| info.layout,
215 else => @compileError("expected struct or union type, found '" ++ @typeName(T) ++ "'"),
216 };
217}
218
219test containerLayout {
220 const S1 = struct {};
221 const S2 = packed struct {};
222 const S3 = extern struct {};
223 const U1 = union {
224 a: u8,
225 };
226 const U2 = packed union {
227 a: u8,
228 };
229 const U3 = extern union {
230 a: u8,
231 };
232
233 try testing.expect(containerLayout(S1) == .auto);
234 try testing.expect(containerLayout(S2) == .@"packed");
235 try testing.expect(containerLayout(S3) == .@"extern");
236 try testing.expect(containerLayout(U1) == .auto);
237 try testing.expect(containerLayout(U2) == .@"packed");
238 try testing.expect(containerLayout(U3) == .@"extern");
239}
240
241/// Instead of this function, prefer to use e.g. `@typeInfo(foo).@"struct".decls`
242/// directly when you know what kind of type it is.
243pub fn declarations(comptime T: type) []const Type.Declaration {
244 return switch (@typeInfo(T)) {
245 .@"struct" => |info| info.decls,
246 .@"enum" => |info| info.decls,
247 .@"union" => |info| info.decls,
248 .@"opaque" => |info| info.decls,
249 else => @compileError("Expected struct, enum, union, or opaque type, found '" ++ @typeName(T) ++ "'"),
250 };
251}
252
253test declarations {
254 const E1 = enum {
255 A,
256
257 pub fn a() void {}
258 };
259 const S1 = struct {
260 pub fn a() void {}
261 };
262 const U1 = union {
263 b: u8,
264
265 pub fn a() void {}
266 };
267 const O1 = opaque {
268 pub fn a() void {}
269 };
270
271 const decls = comptime [_][]const Type.Declaration{
272 declarations(E1),
273 declarations(S1),
274 declarations(U1),
275 declarations(O1),
276 };
277
278 inline for (decls) |decl| {
279 try testing.expect(decl.len == 1);
280 try testing.expect(comptime mem.eql(u8, decl[0].name, "a"));
281 }
282}
283
284pub fn declarationInfo(comptime T: type, comptime decl_name: []const u8) Type.Declaration {
285 inline for (comptime declarations(T)) |decl| {
286 if (comptime mem.eql(u8, decl.name, decl_name))
287 return decl;
288 }
289
290 @compileError("'" ++ @typeName(T) ++ "' has no declaration '" ++ decl_name ++ "'");
291}
292
293test declarationInfo {
294 const E1 = enum {
295 A,
296
297 pub fn a() void {}
298 };
299 const S1 = struct {
300 pub fn a() void {}
301 };
302 const U1 = union {
303 b: u8,
304
305 pub fn a() void {}
306 };
307
308 const infos = comptime [_]Type.Declaration{
309 declarationInfo(E1, "a"),
310 declarationInfo(S1, "a"),
311 declarationInfo(U1, "a"),
312 };
313
314 inline for (infos) |info| {
315 try testing.expect(comptime mem.eql(u8, info.name, "a"));
316 }
317}
318pub fn fields(comptime T: type) switch (@typeInfo(T)) {
319 .@"struct" => []const Type.StructField,
320 .@"union" => []const Type.UnionField,
321 .@"enum" => []const Type.EnumField,
322 .error_set => []const Type.Error,
323 else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
324} {
325 return switch (@typeInfo(T)) {
326 .@"struct" => |info| info.fields,
327 .@"union" => |info| info.fields,
328 .@"enum" => |info| info.fields,
329 .error_set => |errors| errors.?, // must be non global error set
330 else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
331 };
332}
333
334test fields {
335 const E1 = enum {
336 A,
337 };
338 const E2 = error{A};
339 const S1 = struct {
340 a: u8,
341 };
342 const U1 = union {
343 a: u8,
344 };
345
346 const e1f = comptime fields(E1);
347 const e2f = comptime fields(E2);
348 const sf = comptime fields(S1);
349 const uf = comptime fields(U1);
350
351 try testing.expect(e1f.len == 1);
352 try testing.expect(e2f.len == 1);
353 try testing.expect(sf.len == 1);
354 try testing.expect(uf.len == 1);
355 try testing.expect(mem.eql(u8, e1f[0].name, "A"));
356 try testing.expect(mem.eql(u8, e2f[0].name, "A"));
357 try testing.expect(mem.eql(u8, sf[0].name, "a"));
358 try testing.expect(mem.eql(u8, uf[0].name, "a"));
359 try testing.expect(comptime sf[0].type == u8);
360 try testing.expect(comptime uf[0].type == u8);
361}
362
363pub fn fieldInfo(comptime T: type, comptime field: FieldEnum(T)) switch (@typeInfo(T)) {
364 .@"struct" => Type.StructField,
365 .@"union" => Type.UnionField,
366 .@"enum" => Type.EnumField,
367 .error_set => Type.Error,
368 else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
369} {
370 return fields(T)[@intFromEnum(field)];
371}
372
373test fieldInfo {
374 const E1 = enum {
375 A,
376 };
377 const E2 = error{A};
378 const S1 = struct {
379 a: u8,
380 };
381 const U1 = union {
382 a: u8,
383 };
384
385 const e1f = fieldInfo(E1, .A);
386 const e2f = fieldInfo(E2, .A);
387 const sf = fieldInfo(S1, .a);
388 const uf = fieldInfo(U1, .a);
389
390 try testing.expect(mem.eql(u8, e1f.name, "A"));
391 try testing.expect(mem.eql(u8, e2f.name, "A"));
392 try testing.expect(mem.eql(u8, sf.name, "a"));
393 try testing.expect(mem.eql(u8, uf.name, "a"));
394 try testing.expect(comptime sf.type == u8);
395 try testing.expect(comptime uf.type == u8);
396}
397
398pub fn fieldNames(comptime T: type) *const [fields(T).len][:0]const u8 {
399 return comptime blk: {
400 const fieldInfos = fields(T);
401 var names: [fieldInfos.len][:0]const u8 = undefined;
402 for (&names, fieldInfos) |*name, field| name.* = field.name;
403 const final = names;
404 break :blk &final;
405 };
406}
407
408test fieldNames {
409 const E1 = enum { A, B };
410 const E2 = error{A};
411 const S1 = struct {
412 a: u8,
413 };
414 const U1 = union {
415 a: u8,
416 b: void,
417 };
418
419 const e1names = fieldNames(E1);
420 const e2names = fieldNames(E2);
421 const s1names = fieldNames(S1);
422 const u1names = fieldNames(U1);
423
424 try testing.expect(e1names.len == 2);
425 try testing.expectEqualSlices(u8, e1names[0], "A");
426 try testing.expectEqualSlices(u8, e1names[1], "B");
427 try testing.expect(e2names.len == 1);
428 try testing.expectEqualSlices(u8, e2names[0], "A");
429 try testing.expect(s1names.len == 1);
430 try testing.expectEqualSlices(u8, s1names[0], "a");
431 try testing.expect(u1names.len == 2);
432 try testing.expectEqualSlices(u8, u1names[0], "a");
433 try testing.expectEqualSlices(u8, u1names[1], "b");
434}
435
436/// Given an enum or error set type, returns a pointer to an array containing all tags for that
437/// enum or error set.
438pub fn tags(comptime T: type) *const [fields(T).len]T {
439 return comptime blk: {
440 const fieldInfos = fields(T);
441 var res: [fieldInfos.len]T = undefined;
442 for (fieldInfos, 0..) |field, i| {
443 res[i] = @field(T, field.name);
444 }
445 const final = res;
446 break :blk &final;
447 };
448}
449
450test tags {
451 const E1 = enum { A, B };
452 const E2 = error{A};
453
454 const e1_tags = tags(E1);
455 const e2_tags = tags(E2);
456
457 try testing.expect(e1_tags.len == 2);
458 try testing.expectEqual(E1.A, e1_tags[0]);
459 try testing.expectEqual(E1.B, e1_tags[1]);
460 try testing.expect(e2_tags.len == 1);
461 try testing.expectEqual(E2.A, e2_tags[0]);
462}
463
464/// Returns an enum with a variant named after each field of `T`.
465pub fn FieldEnum(comptime T: type) type {
466 const field_names = fieldNames(T);
467
468 switch (@typeInfo(T)) {
469 .@"union" => |@"union"| if (@"union".tag_type) |EnumTag| {
470 for (std.enums.values(EnumTag), 0..) |v, i| {
471 if (@intFromEnum(v) != i) break; // enum values not consecutive
472 if (!std.mem.eql(u8, @tagName(v), field_names[i])) break; // fields out of order
473 } else {
474 return EnumTag;
475 }
476 },
477 else => {},
478 }
479
480 const IntTag = std.math.IntFittingRange(0, field_names.len -| 1);
481 return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len));
482}
483
484fn expectEqualEnum(expected: anytype, actual: @TypeOf(expected)) !void {
485 // TODO: https://github.com/ziglang/zig/issues/7419
486 // testing.expectEqual(@typeInfo(expected).@"enum", @typeInfo(actual).@"enum");
487 try testing.expectEqual(
488 @typeInfo(expected).@"enum".tag_type,
489 @typeInfo(actual).@"enum".tag_type,
490 );
491 // For comparing decls and fields, we cannot use the meta eql function here
492 // because the language does not guarantee that the slice pointers for field names
493 // and decl names will be the same.
494 comptime {
495 const expected_fields = @typeInfo(expected).@"enum".fields;
496 const actual_fields = @typeInfo(actual).@"enum".fields;
497 if (expected_fields.len != actual_fields.len) return error.FailedTest;
498 for (expected_fields, 0..) |expected_field, i| {
499 const actual_field = actual_fields[i];
500 try testing.expectEqual(expected_field.value, actual_field.value);
501 try testing.expectEqualStrings(expected_field.name, actual_field.name);
502 }
503 }
504 comptime {
505 const expected_decls = @typeInfo(expected).@"enum".decls;
506 const actual_decls = @typeInfo(actual).@"enum".decls;
507 if (expected_decls.len != actual_decls.len) return error.FailedTest;
508 for (expected_decls, 0..) |expected_decl, i| {
509 const actual_decl = actual_decls[i];
510 try testing.expectEqualStrings(expected_decl.name, actual_decl.name);
511 }
512 }
513 try testing.expectEqual(
514 @typeInfo(expected).@"enum".is_exhaustive,
515 @typeInfo(actual).@"enum".is_exhaustive,
516 );
517}
518
519test FieldEnum {
520 try expectEqualEnum(enum {}, FieldEnum(struct {}));
521 try expectEqualEnum(enum { a }, FieldEnum(struct { a: u8 }));
522 try expectEqualEnum(enum { a, b, c }, FieldEnum(struct { a: u8, b: void, c: f32 }));
523 try expectEqualEnum(enum { a, b, c }, FieldEnum(union { a: u8, b: void, c: f32 }));
524
525 const Tagged = union(enum) { a: u8, b: void, c: f32 };
526 try testing.expectEqual(Tag(Tagged), FieldEnum(Tagged));
527
528 const Tag2 = enum { a, b, c };
529 const Tagged2 = union(Tag2) { a: u8, b: void, c: f32 };
530 try testing.expect(Tag(Tagged2) == FieldEnum(Tagged2));
531
532 const Tag3 = enum(u8) { a, b, c = 7 };
533 const Tagged3 = union(Tag3) { a: u8, b: void, c: f32 };
534 try testing.expect(Tag(Tagged3) != FieldEnum(Tagged3));
535}
536
537pub fn DeclEnum(comptime T: type) type {
538 const decls = declarations(T);
539 var names: [decls.len][]const u8 = undefined;
540 for (&names, decls) |*name, decl| name.* = decl.name;
541 const IntTag = std.math.IntFittingRange(0, decls.len -| 1);
542 return @Enum(IntTag, .exhaustive, &names, &std.simd.iota(IntTag, decls.len));
543}
544
545test DeclEnum {
546 const A = struct {
547 pub const a: u8 = 0;
548 };
549 const B = union {
550 foo: void,
551
552 pub const a: u8 = 0;
553 pub const b: void = {};
554 pub const c: f32 = 0;
555 };
556 const C = enum {
557 bar,
558
559 pub const a: u8 = 0;
560 pub const b: void = {};
561 pub const c: f32 = 0;
562 };
563 const D = struct {};
564
565 try expectEqualEnum(enum { a }, DeclEnum(A));
566 try expectEqualEnum(enum { a, b, c }, DeclEnum(B));
567 try expectEqualEnum(enum { a, b, c }, DeclEnum(C));
568 try expectEqualEnum(enum {}, DeclEnum(D));
569}
570
571pub fn Tag(comptime T: type) type {
572 return switch (@typeInfo(T)) {
573 .@"enum" => |info| info.tag_type,
574 .@"union" => |info| info.tag_type orelse @compileError(@typeName(T) ++ " has no tag type"),
575 else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"),
576 };
577}
578
579test Tag {
580 const E = enum(u8) {
581 C = 33,
582 D,
583 };
584 const U = union(E) {
585 C: u8,
586 D: u16,
587 };
588
589 try testing.expect(Tag(E) == u8);
590 try testing.expect(Tag(U) == E);
591}
592
593/// Returns the active tag of a tagged union
594pub fn activeTag(u: anytype) Tag(@TypeOf(u)) {
595 const T = @TypeOf(u);
596 return @as(Tag(T), u);
597}
598
599test activeTag {
600 const UE = enum {
601 Int,
602 Float,
603 };
604
605 const U = union(UE) {
606 Int: u32,
607 Float: f32,
608 };
609
610 var u = U{ .Int = 32 };
611 try testing.expect(activeTag(u) == UE.Int);
612
613 u = U{ .Float = 112.9876 };
614 try testing.expect(activeTag(u) == UE.Float);
615}
616
617/// Deprecated: Use @FieldType(U, tag_name)
618const TagPayloadType = TagPayload;
619
620/// Deprecated: Use @FieldType(U, tag_name)
621pub fn TagPayloadByName(comptime U: type, comptime tag_name: []const u8) type {
622 const info = @typeInfo(U).@"union";
623
624 inline for (info.fields) |field_info| {
625 if (comptime mem.eql(u8, field_info.name, tag_name))
626 return field_info.type;
627 }
628
629 @compileError("no field '" ++ tag_name ++ "' in union '" ++ @typeName(U) ++ "'");
630}
631
632/// Deprecated: Use @FieldType(U, @tagName(tag))
633pub fn TagPayload(comptime U: type, comptime tag: Tag(U)) type {
634 return TagPayloadByName(U, @tagName(tag));
635}
636
637test TagPayload {
638 const Event = union(enum) {
639 Moved: struct {
640 from: i32,
641 to: i32,
642 },
643 };
644 const MovedEvent = TagPayload(Event, Event.Moved);
645 const e: Event = .{ .Moved = undefined };
646 try testing.expect(MovedEvent == @TypeOf(e.Moved));
647}
648
649/// Compares two of any type for equality. Containers that do not support comparison
650/// on their own are compared on a field-by-field basis. Pointers are not followed.
651pub fn eql(a: anytype, b: @TypeOf(a)) bool {
652 const T = @TypeOf(a);
653
654 switch (@typeInfo(T)) {
655 .@"struct" => |info| {
656 if (info.layout == .@"packed") return a == b;
657
658 inline for (info.fields) |field_info| {
659 if (!eql(@field(a, field_info.name), @field(b, field_info.name))) return false;
660 }
661 return true;
662 },
663 .error_union => {
664 if (a) |a_p| {
665 if (b) |b_p| return eql(a_p, b_p) else |_| return false;
666 } else |a_e| {
667 if (b) |_| return false else |b_e| return a_e == b_e;
668 }
669 },
670 .@"union" => |info| {
671 if (info.tag_type) |UnionTag| {
672 const tag_a: UnionTag = a;
673 const tag_b: UnionTag = b;
674 if (tag_a != tag_b) return false;
675
676 return switch (a) {
677 inline else => |val, tag| return eql(val, @field(b, @tagName(tag))),
678 };
679 }
680
681 @compileError("cannot compare untagged union type " ++ @typeName(T));
682 },
683 .array => {
684 if (a.len != b.len) return false;
685 for (a, 0..) |e, i|
686 if (!eql(e, b[i])) return false;
687 return true;
688 },
689 .vector => return @reduce(.And, a == b),
690 .pointer => |info| {
691 return switch (info.size) {
692 .one, .many, .c => a == b,
693 .slice => a.ptr == b.ptr and a.len == b.len,
694 };
695 },
696 .optional => {
697 if (a == null and b == null) return true;
698 if (a == null or b == null) return false;
699 return eql(a.?, b.?);
700 },
701 else => return a == b,
702 }
703}
704
705test eql {
706 const S = struct {
707 a: u32,
708 b: f64,
709 c: [5]u8,
710 };
711
712 const U = union(enum) {
713 s: S,
714 f: ?f32,
715 };
716
717 const s_1 = S{
718 .a = 134,
719 .b = 123.3,
720 .c = "12345".*,
721 };
722
723 var s_3 = S{
724 .a = 134,
725 .b = 123.3,
726 .c = "12345".*,
727 };
728
729 const u_1 = U{ .f = 24 };
730 const u_2 = U{ .s = s_1 };
731 const u_3 = U{ .f = 24 };
732
733 try testing.expect(eql(s_1, s_3));
734 try testing.expect(eql(&s_1, &s_1));
735 try testing.expect(!eql(&s_1, &s_3));
736 try testing.expect(eql(u_1, u_3));
737 try testing.expect(!eql(u_1, u_2));
738
739 const a1 = "abcdef".*;
740 const a2 = "abcdef".*;
741 const a3 = "ghijkl".*;
742
743 try testing.expect(eql(a1, a2));
744 try testing.expect(!eql(a1, a3));
745
746 const EU = struct {
747 fn tst(err: bool) !u8 {
748 if (err) return error.Error;
749 return @as(u8, 5);
750 }
751 };
752
753 try testing.expect(eql(EU.tst(true), EU.tst(true)));
754 try testing.expect(eql(EU.tst(false), EU.tst(false)));
755 try testing.expect(!eql(EU.tst(false), EU.tst(true)));
756
757 const CU = union(enum) {
758 a: void,
759 b: void,
760 c: comptime_int,
761 };
762
763 try testing.expect(eql(CU{ .a = {} }, .a));
764 try testing.expect(!eql(CU{ .a = {} }, .b));
765
766 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
767
768 const V = @Vector(4, u32);
769 const v1: V = @splat(1);
770 const v2: V = @splat(1);
771 const v3: V = @splat(2);
772
773 try testing.expect(eql(v1, v2));
774 try testing.expect(!eql(v1, v3));
775}
776
777/// Deprecated: use `std.enums.fromInt` instead and handle null.
778pub const IntToEnumError = error{InvalidEnumTag};
779
780/// Deprecated: use `std.enums.fromInt` instead and handle null instead of an error.
781pub fn intToEnum(comptime EnumTag: type, tag_int: anytype) IntToEnumError!EnumTag {
782 return std.enums.fromInt(EnumTag, tag_int) orelse return error.InvalidEnumTag;
783}
784
785/// Given a type and a name, return the field index according to source order.
786/// Returns `null` if the field is not found.
787pub fn fieldIndex(comptime T: type, comptime name: []const u8) ?comptime_int {
788 inline for (fields(T), 0..) |field, i| {
789 if (mem.eql(u8, field.name, name))
790 return i;
791 }
792 return null;
793}
794
795/// Returns a slice of pointers to public declarations of a namespace.
796pub fn declList(comptime Namespace: type, comptime Decl: type) []const *const Decl {
797 const S = struct {
798 fn declNameLessThan(context: void, lhs: *const Decl, rhs: *const Decl) bool {
799 _ = context;
800 return mem.lessThan(u8, lhs.name, rhs.name);
801 }
802 };
803 comptime {
804 const decls = declarations(Namespace);
805 var array: [decls.len]*const Decl = undefined;
806 for (decls, 0..) |decl, i| {
807 array[i] = &@field(Namespace, decl.name);
808 }
809 mem.sort(*const Decl, &array, {}, S.declNameLessThan);
810 return &array;
811 }
812}
813
814/// Deprecated: use @Int
815pub fn Int(comptime signedness: std.builtin.Signedness, comptime bit_count: u16) type {
816 return @Int(signedness, bit_count);
817}
818
819pub fn Float(comptime bit_count: u8) type {
820 return switch (bit_count) {
821 16 => f16,
822 32 => f32,
823 64 => f64,
824 80 => f80,
825 128 => f128,
826 else => @compileError("invalid float bit count"),
827 };
828}
829test Float {
830 try testing.expectEqual(f16, Float(16));
831 try testing.expectEqual(f32, Float(32));
832 try testing.expectEqual(f64, Float(64));
833 try testing.expectEqual(f80, Float(80));
834 try testing.expectEqual(f128, Float(128));
835}
836
837/// For a given function type, returns a tuple type which fields will
838/// correspond to the argument types.
839///
840/// Examples:
841/// - `ArgsTuple(fn () void)` ⇒ `tuple { }`
842/// - `ArgsTuple(fn (a: u32) u32)` ⇒ `tuple { u32 }`
843/// - `ArgsTuple(fn (a: u32, b: f16) noreturn)` ⇒ `tuple { u32, f16 }`
844pub fn ArgsTuple(comptime Function: type) type {
845 const info = @typeInfo(Function);
846 if (info != .@"fn")
847 @compileError("ArgsTuple expects a function type");
848
849 const function_info = info.@"fn";
850 if (function_info.is_var_args)
851 @compileError("Cannot create ArgsTuple for variadic function");
852
853 var argument_field_list: [function_info.params.len]type = undefined;
854 inline for (function_info.params, 0..) |arg, i| {
855 const T = arg.type orelse @compileError("cannot create ArgsTuple for function with an 'anytype' parameter");
856 argument_field_list[i] = T;
857 }
858
859 return Tuple(&argument_field_list);
860}
861
862/// Deprecated; use `@Tuple` instead.
863///
864/// To be removed after Zig 0.16.0 releases.
865pub fn Tuple(comptime types: []const type) type {
866 return @Tuple(types);
867}
868
869const TupleTester = struct {
870 fn assertTypeEqual(comptime Expected: type, comptime Actual: type) void {
871 if (Expected != Actual)
872 @compileError("Expected type " ++ @typeName(Expected) ++ ", but got type " ++ @typeName(Actual));
873 }
874
875 fn assertTuple(comptime expected: anytype, comptime Actual: type) void {
876 const info = @typeInfo(Actual);
877 if (info != .@"struct")
878 @compileError("Expected struct type");
879 if (!info.@"struct".is_tuple)
880 @compileError("Struct type must be a tuple type");
881
882 const fields_list = std.meta.fields(Actual);
883 if (expected.len != fields_list.len)
884 @compileError("Argument count mismatch");
885
886 inline for (fields_list, 0..) |fld, i| {
887 if (expected[i] != fld.type) {
888 @compileError("Field " ++ fld.name ++ " expected to be type " ++ @typeName(expected[i]) ++ ", but was type " ++ @typeName(fld.type));
889 }
890 }
891 }
892};
893
894test ArgsTuple {
895 TupleTester.assertTuple(.{}, ArgsTuple(fn () void));
896 TupleTester.assertTuple(.{u32}, ArgsTuple(fn (a: u32) []const u8));
897 TupleTester.assertTuple(.{ u32, f16 }, ArgsTuple(fn (a: u32, b: f16) noreturn));
898 TupleTester.assertTuple(.{ u32, f16, []const u8, void }, ArgsTuple(fn (a: u32, b: f16, c: []const u8, void) noreturn));
899 TupleTester.assertTuple(.{u32}, ArgsTuple(fn (comptime a: u32) []const u8));
900}
901
902test Tuple {
903 TupleTester.assertTuple(.{}, Tuple(&[_]type{}));
904 TupleTester.assertTuple(.{u32}, Tuple(&[_]type{u32}));
905 TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
906 TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void }));
907}
908
909test "Tuple deduplication" {
910 const T1 = std.meta.Tuple(&.{ u32, f32, i8 });
911 const T2 = std.meta.Tuple(&.{ u32, f32, i8 });
912 const T3 = std.meta.Tuple(&.{ u32, f32, i7 });
913
914 if (T1 != T2) {
915 @compileError("std.meta.Tuple doesn't deduplicate tuple types.");
916 }
917 if (T1 == T3) {
918 @compileError("std.meta.Tuple fails to generate different types.");
919 }
920}
921
922test "ArgsTuple forwarding" {
923 const T1 = std.meta.Tuple(&.{ u32, f32, i8 });
924 const T2 = std.meta.ArgsTuple(fn (u32, f32, i8) void);
925 const T3 = std.meta.ArgsTuple(fn (u32, f32, i8) callconv(.c) noreturn);
926
927 if (T1 != T2) {
928 @compileError("std.meta.ArgsTuple produces different types than std.meta.Tuple");
929 }
930 if (T1 != T3) {
931 @compileError("std.meta.ArgsTuple produces different types for the same argument lists.");
932 }
933}
934
935/// Returns whether `error_union` contains an error.
936pub fn isError(error_union: anytype) bool {
937 return if (error_union) |_| false else |_| true;
938}
939
940test isError {
941 try std.testing.expect(isError(math.divTrunc(u8, 5, 0)));
942 try std.testing.expect(!isError(math.divTrunc(u8, 5, 5)));
943}
944
945/// Returns true if a type has a namespace and the namespace contains `name`;
946/// `false` otherwise. Result is always comptime-known.
947pub inline fn hasFn(comptime T: type, comptime name: []const u8) bool {
948 switch (@typeInfo(T)) {
949 .@"struct", .@"union", .@"enum", .@"opaque" => {},
950 else => return false,
951 }
952 if (!@hasDecl(T, name))
953 return false;
954
955 return @typeInfo(@TypeOf(@field(T, name))) == .@"fn";
956}
957
958test hasFn {
959 const S1 = struct {
960 pub fn foo() void {}
961 };
962
963 try std.testing.expect(hasFn(S1, "foo"));
964 try std.testing.expect(!hasFn(S1, "bar"));
965 try std.testing.expect(!hasFn(*S1, "foo"));
966
967 const S2 = struct {
968 foo: fn () void,
969 };
970
971 try std.testing.expect(!hasFn(S2, "foo"));
972}
973
974/// Returns true if a type has a `name` method; `false` otherwise.
975/// Result is always comptime-known.
976pub inline fn hasMethod(comptime T: type, comptime name: []const u8) bool {
977 return switch (@typeInfo(T)) {
978 .pointer => |P| switch (P.size) {
979 .one => hasFn(P.child, name),
980 .many, .slice, .c => false,
981 },
982 else => hasFn(T, name),
983 };
984}
985
986test hasMethod {
987 try std.testing.expect(!hasMethod(u32, "foo"));
988 try std.testing.expect(!hasMethod([]u32, "len"));
989 try std.testing.expect(!hasMethod(struct { u32, u64 }, "len"));
990
991 const S1 = struct {
992 pub fn foo() void {}
993 };
994
995 try std.testing.expect(hasMethod(S1, "foo"));
996 try std.testing.expect(hasMethod(*S1, "foo"));
997
998 try std.testing.expect(!hasMethod(S1, "bar"));
999 try std.testing.expect(!hasMethod(*[1]S1, "foo"));
1000 try std.testing.expect(!hasMethod(*[10]S1, "foo"));
1001 try std.testing.expect(!hasMethod([]S1, "foo"));
1002
1003 const S2 = struct {
1004 foo: fn () void,
1005 };
1006
1007 try std.testing.expect(!hasMethod(S2, "foo"));
1008
1009 const U = union {
1010 pub fn foo() void {}
1011 };
1012
1013 try std.testing.expect(hasMethod(U, "foo"));
1014 try std.testing.expect(hasMethod(*U, "foo"));
1015 try std.testing.expect(!hasMethod(U, "bar"));
1016}
1017
1018/// True if every value of the type `T` has a unique bit pattern representing it.
1019/// In other words, `T` has no unused bits and no padding.
1020/// Result is always comptime-known.
1021pub inline fn hasUniqueRepresentation(comptime T: type) bool {
1022 return switch (@typeInfo(T)) {
1023 else => false, // TODO can we know if it's true for some of these types ?
1024
1025 .@"anyframe",
1026 .@"enum",
1027 .error_set,
1028 .@"fn",
1029 => true,
1030
1031 .bool => false,
1032
1033 .int => |info| @sizeOf(T) * 8 == info.bits,
1034
1035 .pointer => |info| info.size != .slice,
1036
1037 .optional => |info| switch (@typeInfo(info.child)) {
1038 .pointer => |ptr| !ptr.is_allowzero and switch (ptr.size) {
1039 .slice, .c => false,
1040 .one, .many => true,
1041 },
1042 else => false,
1043 },
1044
1045 .array => |info| hasUniqueRepresentation(info.child),
1046
1047 .@"struct" => |info| {
1048 if (info.layout == .@"packed") return @sizeOf(T) * 8 == @bitSizeOf(T);
1049
1050 var sum_size = @as(usize, 0);
1051
1052 inline for (info.fields) |field| {
1053 if (field.is_comptime) continue;
1054 if (!hasUniqueRepresentation(field.type)) return false;
1055 sum_size += @sizeOf(field.type);
1056 }
1057
1058 return @sizeOf(T) == sum_size;
1059 },
1060
1061 .vector => |info| hasUniqueRepresentation(info.child) and
1062 @sizeOf(T) == @sizeOf(info.child) * info.len,
1063 };
1064}
1065
1066test hasUniqueRepresentation {
1067 const TestStruct1 = struct {
1068 a: u32,
1069 b: u32,
1070 };
1071
1072 try testing.expect(hasUniqueRepresentation(TestStruct1));
1073
1074 const TestStruct2 = struct {
1075 a: u32,
1076 b: u16,
1077 };
1078
1079 try testing.expect(!hasUniqueRepresentation(TestStruct2));
1080
1081 const TestStruct3 = struct {
1082 a: u32,
1083 b: u32,
1084 };
1085
1086 try testing.expect(hasUniqueRepresentation(TestStruct3));
1087
1088 const TestStruct4 = struct { a: []const u8 };
1089
1090 try testing.expect(!hasUniqueRepresentation(TestStruct4));
1091
1092 const TestStruct5 = struct { a: TestStruct4 };
1093
1094 try testing.expect(!hasUniqueRepresentation(TestStruct5));
1095
1096 const TestStruct6 = packed struct(u8) {
1097 @"0": bool,
1098 @"1": bool,
1099 @"2": bool,
1100 @"3": bool,
1101 @"4": bool,
1102 @"5": bool,
1103 @"6": bool,
1104 @"7": bool,
1105 };
1106
1107 try testing.expect(hasUniqueRepresentation(TestStruct6));
1108
1109 const TestUnion2 = extern union {
1110 a: u32,
1111 b: u16,
1112 };
1113
1114 try testing.expect(!hasUniqueRepresentation(TestUnion2));
1115
1116 const TestUnion3 = union {
1117 a: u32,
1118 b: u16,
1119 };
1120
1121 try testing.expect(!hasUniqueRepresentation(TestUnion3));
1122
1123 const TestUnion4 = union(enum) {
1124 a: u32,
1125 b: u16,
1126 };
1127
1128 try testing.expect(!hasUniqueRepresentation(TestUnion4));
1129
1130 inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| {
1131 try testing.expect(hasUniqueRepresentation(T));
1132 }
1133 inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| {
1134 try testing.expect(!hasUniqueRepresentation(T));
1135 }
1136
1137 try testing.expect(hasUniqueRepresentation(*u8));
1138 try testing.expect(hasUniqueRepresentation(*const u8));
1139 try testing.expect(hasUniqueRepresentation(?*u8));
1140 try testing.expect(hasUniqueRepresentation(?*const u8));
1141
1142 try testing.expect(!hasUniqueRepresentation([]u8));
1143 try testing.expect(!hasUniqueRepresentation([]const u8));
1144 try testing.expect(!hasUniqueRepresentation(?[]u8));
1145 try testing.expect(!hasUniqueRepresentation(?[]const u8));
1146
1147 try testing.expect(hasUniqueRepresentation(@Vector(std.simd.suggestVectorLength(u8) orelse 1, u8)));
1148 try testing.expect(@sizeOf(@Vector(3, u8)) == 3 or !hasUniqueRepresentation(@Vector(3, u8)));
1149
1150 const StructWithComptimeFields = struct {
1151 comptime should_be_ignored: u64 = 42,
1152 comptime should_also_be_ignored: [*:0]const u8 = "hope you're having a good day :)",
1153 field: u32,
1154 };
1155
1156 try testing.expect(hasUniqueRepresentation(StructWithComptimeFields));
1157}