| 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); |
| 3 | const Type = std.builtin.Type; |
| 4 | const testing = std.testing; |
| 5 | const assert = std.debug.assert; |
| 6 | |
| 7 | test "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 | |
| 16 | test "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 | |
| 64 | test "@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 | |
| 69 | test "@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 | |
| 74 | test "@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 | |
| 104 | test "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 | |
| 119 | fn add(a: i32, b: i32) i32 { |
| 120 | return a + b; |
| 121 | } |
| 122 | |
| 123 | test "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 | |
| 194 | test "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 | |
| 230 | test "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 | |
| 254 | test "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 | |
| 260 | test "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 | |
| 266 | test "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 | |
| 276 | test "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 | |
| 286 | test "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 | |
| 303 | test "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 | |
| 314 | test "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 | |
| 323 | test "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 | |
| 342 | test "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 | |
| 361 | test "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 | |
| 386 | test "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 | |
| 408 | test "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 | |
| 421 | test "undefined type value" { |
| 422 | const S = struct { |
| 423 | const undef_type: type = undefined; |
| 424 | }; |
| 425 | comptime assert(@TypeOf(S.undef_type) == type); |
| 426 | } |
| 427 | |
| 428 | test "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 | } |