1const std = @import("std");
2const builtin = @import("builtin");
3const Type = std.builtin.Type;
4const testing = std.testing;
5const assert = std.debug.assert;
6
7test "Type.Int" {
8 try testing.expect(u1 == @Int(.unsigned, 1));
9 try testing.expect(i1 == @Int(.signed, 1));
10 try testing.expect(u8 == @Int(.unsigned, 8));
11 try testing.expect(i8 == @Int(.signed, 8));
12 try testing.expect(u64 == @Int(.unsigned, 64));
13 try testing.expect(i64 == @Int(.signed, 64));
14}
15
16test "Type.Pointer" {
17 inline for (&[_]type{
18 // One Value Pointer Types
19 *u8, *const u8,
20 *volatile u8, *const volatile u8,
21 *align(4) u8, *align(4) const u8,
22 *align(4) volatile u8, *align(4) const volatile u8,
23 *align(8) u8, *align(8) const u8,
24 *align(8) volatile u8, *align(8) const volatile u8,
25 *allowzero u8, *allowzero const u8,
26 *allowzero volatile u8, *allowzero const volatile u8,
27 *allowzero align(4) u8, *allowzero align(4) const u8,
28 *allowzero align(4) volatile u8, *allowzero align(4) const volatile u8,
29 // Many Values Pointer Types
30 [*]u8, [*]const u8,
31 [*]volatile u8, [*]const volatile u8,
32 [*]align(4) u8, [*]align(4) const u8,
33 [*]align(4) volatile u8, [*]align(4) const volatile u8,
34 [*]align(8) u8, [*]align(8) const u8,
35 [*]align(8) volatile u8, [*]align(8) const volatile u8,
36 [*]allowzero u8, [*]allowzero const u8,
37 [*]allowzero volatile u8, [*]allowzero const volatile u8,
38 [*]allowzero align(4) u8, [*]allowzero align(4) const u8,
39 [*]allowzero align(4) volatile u8, [*]allowzero align(4) const volatile u8,
40 // Slice Types
41 []u8, []const u8,
42 []volatile u8, []const volatile u8,
43 []align(4) u8, []align(4) const u8,
44 []align(4) volatile u8, []align(4) const volatile u8,
45 []align(8) u8, []align(8) const u8,
46 []align(8) volatile u8, []align(8) const volatile u8,
47 []allowzero u8, []allowzero const u8,
48 []allowzero volatile u8, []allowzero const volatile u8,
49 []allowzero align(4) u8, []allowzero align(4) const u8,
50 []allowzero align(4) volatile u8, []allowzero align(4) const volatile u8,
51 // C Pointer Types
52 [*c]u8, [*c]const u8,
53 [*c]volatile u8, [*c]const volatile u8,
54 [*c]align(4) u8, [*c]align(4) const u8,
55 [*c]align(4) volatile u8, [*c]align(4) const volatile u8,
56 [*c]align(8) u8, [*c]align(8) const u8,
57 [*c]align(8) volatile u8, [*c]align(8) const volatile u8,
58 }) |testType| {
59 const ptr = @typeInfo(testType).pointer;
60 try testing.expect(testType == @Pointer(ptr.size, ptr.attrs, ptr.child, ptr.sentinel()));
61 }
62}
63
64test "@Pointer create slice without sentinel" {
65 const Slice = @Pointer(.slice, .{ .@"const" = true, .@"align" = 8 }, ?*i32, null);
66 try testing.expect(Slice == []align(8) const ?*i32);
67}
68
69test "@Pointer create slice with null sentinel" {
70 const Slice = @Pointer(.slice, .{ .@"const" = true, .@"align" = 8 }, ?*i32, @as(?*i32, null));
71 try testing.expect(Slice == [:null]align(8) const ?*i32);
72}
73
74test "@Pointer on @typeInfo round-trips sentinels" {
75 inline for (&[_]type{
76 [*:0]u8, [*:0]const u8,
77 [*:0]volatile u8, [*:0]const volatile u8,
78 [*:0]align(4) u8, [*:0]align(4) const u8,
79 [*:0]align(4) volatile u8, [*:0]align(4) const volatile u8,
80 [*:0]align(8) u8, [*:0]align(8) const u8,
81 [*:0]align(8) volatile u8, [*:0]align(8) const volatile u8,
82 [*:0]allowzero u8, [*:0]allowzero const u8,
83 [*:0]allowzero volatile u8, [*:0]allowzero const volatile u8,
84 [*:0]allowzero align(4) u8, [*:0]allowzero align(4) const u8,
85 [*:0]allowzero align(4) volatile u8, [*:0]allowzero align(4) const volatile u8,
86 [*:5]allowzero align(4) volatile u8, [*:5]allowzero align(4) const volatile u8,
87 [:0]u8, [:0]const u8,
88 [:0]volatile u8, [:0]const volatile u8,
89 [:0]align(4) u8, [:0]align(4) const u8,
90 [:0]align(4) volatile u8, [:0]align(4) const volatile u8,
91 [:0]align(8) u8, [:0]align(8) const u8,
92 [:0]align(8) volatile u8, [:0]align(8) const volatile u8,
93 [:0]allowzero u8, [:0]allowzero const u8,
94 [:0]allowzero volatile u8, [:0]allowzero const volatile u8,
95 [:0]allowzero align(4) u8, [:0]allowzero align(4) const u8,
96 [:0]allowzero align(4) volatile u8, [:0]allowzero align(4) const volatile u8,
97 [:4]allowzero align(4) volatile u8, [:4]allowzero align(4) const volatile u8,
98 }) |TestType| {
99 const ptr = @typeInfo(TestType).pointer;
100 try testing.expect(TestType == @Pointer(ptr.size, ptr.attrs, ptr.child, ptr.sentinel()));
101 }
102}
103
104test "Type.Opaque" {
105 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
106 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
107 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
108 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
109
110 const Opaque = opaque {};
111 try testing.expect(Opaque != opaque {});
112 try testing.expectEqualSlices(
113 [:0]const u8,
114 &.{},
115 @typeInfo(Opaque).@"opaque".decl_names,
116 );
117}
118
119fn add(a: i32, b: i32) i32 {
120 return a + b;
121}
122
123test "Type.Struct" {
124 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
125 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
126 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
127 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
128
129 const A = @Struct(.auto, null, &.{ "x", "y" }, &.{ u8, u32 }, &@splat(.{}));
130 const infoA = @typeInfo(A).@"struct";
131 try testing.expectEqual(Type.ContainerLayout.auto, infoA.layout);
132 try testing.expectEqualSlices(u8, "x", infoA.field_names[0]);
133 try testing.expectEqual(u8, infoA.field_types[0]);
134 try testing.expectEqual(@as(?*const anyopaque, null), infoA.field_attrs[0].default_value_ptr);
135 try testing.expectEqualSlices(u8, "y", infoA.field_names[1]);
136 try testing.expectEqual(u32, infoA.field_types[1]);
137 try testing.expectEqual(@as(?*const anyopaque, null), infoA.field_attrs[1].default_value_ptr);
138 try testing.expectEqualSlices([:0]const u8, &.{}, infoA.decl_names);
139 try testing.expectEqual(@as(bool, false), infoA.is_tuple);
140
141 var a = A{ .x = 0, .y = 1 };
142 try testing.expectEqual(@as(u8, 0), a.x);
143 try testing.expectEqual(@as(u32, 1), a.y);
144 a.y += 1;
145 try testing.expectEqual(@as(u32, 2), a.y);
146
147 const B = @Struct(
148 .@"extern",
149 null,
150 &.{ "x", "y" },
151 &.{ u8, u32 },
152 &.{ .{}, .{ .default_value_ptr = &@as(u32, 5) } },
153 );
154 const infoB = @typeInfo(B).@"struct";
155 try testing.expectEqual(Type.ContainerLayout.@"extern", infoB.layout);
156 try testing.expectEqualSlices(u8, "x", infoB.field_names[0]);
157 try testing.expectEqual(u8, infoB.field_types[0]);
158 try testing.expectEqual(@as(?*const anyopaque, null), infoB.field_attrs[0].default_value_ptr);
159 try testing.expectEqualSlices(u8, "y", infoB.field_names[1]);
160 try testing.expectEqual(u32, infoB.field_types[1]);
161 try testing.expectEqual(@as(u32, 5), infoB.field_attrs[1].defaultValue(infoB.field_types[1]).?);
162 try testing.expectEqual(@as(usize, 0), infoB.decl_names.len);
163 try testing.expectEqual(@as(bool, false), infoB.is_tuple);
164
165 const C = @Struct(
166 .@"packed",
167 null,
168 &.{ "x", "y" },
169 &.{ u8, u32 },
170 &.{
171 .{ .default_value_ptr = &@as(u8, 3) },
172 .{ .default_value_ptr = &@as(u32, 5) },
173 },
174 );
175 const infoC = @typeInfo(C).@"struct";
176 try testing.expectEqual(Type.ContainerLayout.@"packed", infoC.layout);
177 try testing.expectEqualSlices(u8, "x", infoC.field_names[0]);
178 try testing.expectEqual(u8, infoC.field_types[0]);
179 try testing.expectEqual(@as(u8, 3), infoC.field_attrs[0].defaultValue(infoC.field_types[0]).?);
180 try testing.expectEqualSlices(u8, "y", infoC.field_names[1]);
181 try testing.expectEqual(u32, infoC.field_types[1]);
182 try testing.expectEqual(@as(u32, 5), infoC.field_attrs[1].defaultValue(infoC.field_types[1]).?);
183 try testing.expectEqual(@as(usize, 0), infoC.decl_names.len);
184 try testing.expectEqual(@as(bool, false), infoC.is_tuple);
185
186 // empty struct
187 const F = @Struct(.auto, null, &.{}, &.{}, &.{});
188 const infoF = @typeInfo(F).@"struct";
189 try testing.expectEqual(Type.ContainerLayout.auto, infoF.layout);
190 try testing.expect(infoF.field_names.len == 0);
191 try testing.expectEqual(@as(bool, false), infoF.is_tuple);
192}
193
194test "Type.Enum" {
195 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
196 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
197
198 const Foo = @Enum(u8, .exhaustive, &.{ "a", "b" }, &.{ 1, 5 });
199 try testing.expectEqual(std.builtin.Type.Enum.Mode.exhaustive, @typeInfo(Foo).@"enum".mode);
200 try testing.expectEqual(@as(u8, 1), @backingInt(Foo.a));
201 try testing.expectEqual(@as(u8, 5), @backingInt(Foo.b));
202 const Bar = @Enum(u32, .nonexhaustive, &.{ "a", "b" }, &.{ 1, 5 });
203 try testing.expectEqual(std.builtin.Type.Enum.Mode.nonexhaustive, @typeInfo(Bar).@"enum".mode);
204 try testing.expectEqual(@as(u32, 1), @backingInt(Bar.a));
205 try testing.expectEqual(@as(u32, 5), @backingInt(Bar.b));
206 try testing.expectEqual(@as(u32, 6), @backingInt(@as(Bar, @fromBackingInt(@intCast(6)))));
207
208 { // from https://github.com/ziglang/zig/issues/19985
209 { // enum with single field can be initialized.
210 const E = @Enum(u0, .exhaustive, &.{"foo"}, &.{0});
211 const s: struct { E } = .{.foo};
212 try testing.expectEqual(.foo, s[0]);
213 }
214
215 { // meta.FieldEnum() with single field
216 const S = struct { foo: u8 };
217 const Fe = std.meta.FieldEnum(S);
218 var s: S = undefined;
219 const fe = std.meta.stringToEnum(Fe, "foo") orelse return error.InvalidField;
220 switch (fe) {
221 inline else => |tag| {
222 @field(s, @tagName(tag)) = 42;
223 },
224 }
225 try testing.expectEqual(42, s.foo);
226 }
227 }
228}
229
230test "Type.Union" {
231 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
232 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
233
234 const Untagged = @Union(.@"extern", null, &.{ "int", "float" }, &.{ i32, f32 }, &.{ .{}, .{} });
235 var untagged = Untagged{ .int = 1 };
236 untagged.float = 2.0;
237 untagged.int = 3;
238 try testing.expectEqual(@as(i32, 3), untagged.int);
239
240 const PackedUntagged = @Union(.@"packed", null, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &.{ .{}, .{} });
241 var packed_untagged: PackedUntagged = .{ .signed = -1 };
242 _ = &packed_untagged;
243 try testing.expectEqual(@as(i32, -1), packed_untagged.signed);
244 try testing.expectEqual(~@as(u32, 0), packed_untagged.unsigned);
245
246 const Tag = @Enum(u1, .exhaustive, &.{ "signed", "unsigned" }, &.{ 0, 1 });
247 const Tagged = @Union(.auto, Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &.{ .{}, .{} });
248 var tagged = Tagged{ .signed = -1 };
249 try testing.expectEqual(Tag.signed, @as(Tag, tagged));
250 tagged = .{ .unsigned = 1 };
251 try testing.expectEqual(Tag.unsigned, @as(Tag, tagged));
252}
253
254test "Type.Union from Type.Enum" {
255 const Tag = @Enum(u0, .exhaustive, &.{"working_as_expected"}, &.{0});
256 const T = @Union(.auto, Tag, &.{"working_as_expected"}, &.{u32}, &.{.{}});
257 _ = @typeInfo(T).@"union";
258}
259
260test "Type.Union from regular enum" {
261 const E = enum { working_as_expected };
262 const T = @Union(.auto, E, &.{"working_as_expected"}, &.{u32}, &.{.{}});
263 _ = @typeInfo(T).@"union";
264}
265
266test "Type.Union from empty regular enum" {
267 const E = enum {};
268 const U = @Union(.auto, E, &.{}, &.{}, &.{});
269
270 const info = @typeInfo(U).@"union";
271 try testing.expect(info.field_names.len == 0);
272 try testing.expect(info.tag_type != null);
273 try testing.expect(@typeInfo(info.tag_type.?).@"enum".tag_type == noreturn);
274}
275
276test "Type.Union from empty Type.Enum" {
277 const E = @Enum(noreturn, .exhaustive, &.{}, &.{});
278 const U = @Union(.auto, E, &.{}, &.{}, &.{});
279
280 const info = @typeInfo(U).@"union";
281 try testing.expect(info.field_names.len == 0);
282 try testing.expect(info.tag_type != null);
283 try testing.expect(@typeInfo(info.tag_type.?).@"enum".tag_type == noreturn);
284}
285
286test "Type.Fn" {
287 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
288 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
289
290 const some_opaque = opaque {};
291 const some_ptr = *some_opaque;
292
293 const A = @Fn(&.{ c_int, some_ptr }, &@splat(.{}), void, .{ .@"callconv" = .c });
294 comptime assert(A == fn (c_int, some_ptr) callconv(.c) void);
295
296 const B = @Fn(&.{ c_int, some_ptr, u32 }, &.{ .{}, .{ .@"noalias" = true }, .{} }, u64, .{});
297 comptime assert(B == fn (c_int, noalias some_ptr, u32) u64);
298
299 const C = @Fn(&.{?[*]u8}, &.{.{}}, *const anyopaque, .{ .@"callconv" = .c, .varargs = true });
300 comptime assert(C == fn (?[*]u8, ...) callconv(.c) *const anyopaque);
301}
302
303test "reified struct field name from optional payload" {
304 comptime {
305 const m_name: ?[1:0]u8 = "a".*;
306 if (m_name) |*name| {
307 const T = @Struct(.auto, null, &.{name}, &.{u8}, &.{.{}});
308 const t: T = .{ .a = 123 };
309 try std.testing.expect(t.a == 123);
310 }
311 }
312}
313
314test "reified union uses @alignOf" {
315 const S = struct {
316 fn CreateUnion(comptime T: type) type {
317 return @Union(.auto, null, &.{"field"}, &.{T}, &.{.{}});
318 }
319 };
320 _ = S.CreateUnion(struct {});
321}
322
323test "reified struct uses @alignOf" {
324 const S = struct {
325 fn NamespacedGlobals(comptime modules: anytype) type {
326 return @Struct(
327 .auto,
328 null,
329 &.{"globals"},
330 &.{modules.mach.globals},
331 &.{.{ .@"align" = @alignOf(modules.mach.globals) }},
332 );
333 }
334 };
335 _ = S.NamespacedGlobals(.{
336 .mach = .{
337 .globals = struct {},
338 },
339 });
340}
341
342test "empty struct assigned to reified struct field" {
343 const S = struct {
344 fn NamespacedComponents(comptime modules: anytype) type {
345 return @Struct(.auto, null, &.{"components"}, &.{@TypeOf(modules.components)}, &.{.{}});
346 }
347
348 fn namespacedComponents(comptime modules: anytype) NamespacedComponents(modules) {
349 var x: NamespacedComponents(modules) = undefined;
350 x.components = modules.components;
351 return x;
352 }
353 };
354 _ = S.namespacedComponents(.{
355 .components = .{
356 .location = struct {},
357 },
358 });
359}
360
361test "struct field names sliced at comptime from larger string" {
362 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
363
364 const text =
365 \\f1
366 \\f2
367 \\f3
368 ;
369 comptime {
370 var field_names: []const []const u8 = &.{};
371
372 var it = std.mem.tokenizeScalar(u8, text, '\n');
373 while (it.next()) |name| {
374 field_names = field_names ++ @as([]const []const u8, &.{name});
375 }
376
377 const T = @Struct(.auto, null, field_names, &@splat(usize), &@splat(.{}));
378 const gen_field_names = @typeInfo(T).@"struct".field_names;
379 try testing.expectEqual(3, gen_field_names.len);
380 try testing.expectEqualStrings("f1", gen_field_names[0]);
381 try testing.expectEqualStrings("f2", gen_field_names[1]);
382 try testing.expectEqualStrings("f3", gen_field_names[2]);
383 }
384}
385
386test "matching captures causes opaque equivalence" {
387 const S = struct {
388 fn UnsignedId(comptime I: type) type {
389 const U = @Int(.unsigned, @typeInfo(I).int.bits);
390 return opaque {
391 fn id(x: U) U {
392 return x;
393 }
394 };
395 }
396 };
397
398 comptime assert(S.UnsignedId(u8) == S.UnsignedId(i8));
399 comptime assert(S.UnsignedId(u16) == S.UnsignedId(i16));
400 comptime assert(S.UnsignedId(u8) != S.UnsignedId(u16));
401
402 const a = S.UnsignedId(u8).id(123);
403 const b = S.UnsignedId(i8).id(123);
404 comptime assert(@TypeOf(a) == @TypeOf(b));
405 try testing.expect(a == b);
406}
407
408test "reify enum where fields refers to part of array" {
409 const field_names: [3][]const u8 = .{ "foo", "bar", undefined };
410 const field_values: [3]u8 = .{ undefined, 0, 1 };
411 const E = @Enum(u8, .exhaustive, field_names[0..2], field_values[1..3]);
412 var a: E = undefined;
413 var b: E = undefined;
414 a = .foo;
415 b = .bar;
416 try testing.expect(a == .foo);
417 try testing.expect(b == .bar);
418 try testing.expect(a != b);
419}
420
421test "undefined type value" {
422 const S = struct {
423 const undef_type: type = undefined;
424 };
425 comptime assert(@TypeOf(S.undef_type) == type);
426}
427
428test "reify struct with zero fields through const arrays" {
429 const names: [0][]const u8 = .{};
430 const types: [0]type = .{};
431 const attrs: [0]std.builtin.Type.Struct.FieldAttributes = .{};
432 const S = @Struct(.auto, null, &names, &types, &attrs);
433 comptime assert(@typeInfo(S) == .@"struct");
434 comptime assert(@typeInfo(S).@"struct".field_names.len == 0);
435}