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