From be5130ec535456559497ac241ac9fa76c4bbb8ca Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Dec 2021 00:39:25 -0700 Subject: [PATCH] compiler_rt: move more functions to the stage2 section also move more already-passing behavior tests to the passing section. --- lib/std/special/compiler_rt.zig | 24 +- lib/std/special/compiler_rt/truncXfYf2.zig | 20 +- test/behavior.zig | 7 +- test/behavior/align.zig | 15 + test/behavior/align_stage1.zig | 34 -- test/behavior/bugs/1322.zig | 19 - test/behavior/cast_llvm.zig | 68 +++ test/behavior/cast_stage1.zig | 68 --- test/behavior/defer.zig | 59 +++ test/behavior/defer_stage1.zig | 59 --- test/behavior/eval.zig | 25 ++ test/behavior/eval_stage1.zig | 25 -- test/behavior/struct.zig | 27 ++ test/behavior/type.zig | 400 ------------------ test/behavior/type_info.zig | 454 -------------------- test/behavior/type_info_stage1.zig | 463 +++++++++++++++++++++ test/behavior/type_stage1.zig | 411 ++++++++++++++++++ test/behavior/union.zig | 25 ++ 18 files changed, 1119 insertions(+), 1084 deletions(-) delete mode 100644 test/behavior/bugs/1322.zig create mode 100644 test/behavior/type_info_stage1.zig create mode 100644 test/behavior/type_stage1.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 5445edc25a..297fd1cb39 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -159,6 +159,18 @@ comptime { @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); } + const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2; + @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); + const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2; + @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); + const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2; + @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); + const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2; + @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); + + const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; + @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); + if (!builtin.zig_is_stage2) { if (!long_double_is_f128) { // TODO implement these @@ -318,18 +330,6 @@ comptime { if (!is_test) { @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); } - const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2; - @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); - const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2; - @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); - const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2; - @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); - const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2; - @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); - - const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; - @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); - const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2; @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index 559ec0ec4f..4cded15abc 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -7,23 +7,23 @@ const native_arch = builtin.cpu.arch; pub const F16T = if (native_arch.isAARCH64()) f16 else u16; pub fn __truncsfhf2(a: f32) callconv(.C) F16T { - return @bitCast(F16T, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f32, a })); + return @bitCast(F16T, truncXfYf2(f16, f32, a)); } pub fn __truncdfhf2(a: f64) callconv(.C) F16T { - return @bitCast(F16T, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a })); + return @bitCast(F16T, truncXfYf2(f16, f64, a)); } pub fn __trunctfhf2(a: f128) callconv(.C) F16T { - return @bitCast(F16T, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f128, a })); + return @bitCast(F16T, truncXfYf2(f16, f128, a)); } pub fn __trunctfsf2(a: f128) callconv(.C) f32 { - return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a }); + return truncXfYf2(f32, f128, a); } pub fn __trunctfdf2(a: f128) callconv(.C) f64 { - return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f64, f128, a }); + return truncXfYf2(f64, f128, a); } pub fn __trunctfxf2(a: f128) callconv(.C) c_longdouble { @@ -32,25 +32,25 @@ pub fn __trunctfxf2(a: f128) callconv(.C) c_longdouble { } pub fn __truncdfsf2(a: f64) callconv(.C) f32 { - return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f64, a }); + return truncXfYf2(f32, f64, a); } pub fn __aeabi_d2f(a: f64) callconv(.AAPCS) f32 { @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __truncdfsf2, .{a}); + return truncXfYf2(f32, f64, a); } pub fn __aeabi_d2h(a: f64) callconv(.AAPCS) u16 { @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __truncdfhf2, .{a}); + return @bitCast(F16T, truncXfYf2(f16, f64, a)); } pub fn __aeabi_f2h(a: f32) callconv(.AAPCS) u16 { @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __truncsfhf2, .{a}); + return @bitCast(F16T, truncXfYf2(f16, f32, a)); } -fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { +inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits); const dst_rep_t = std.meta.Int(.unsigned, @typeInfo(dst_t).Float.bits); const srcSigBits = std.math.floatMantissaBits(src_t); diff --git a/test/behavior.zig b/test/behavior.zig index 7bc52ff518..74d7b1f73c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -18,6 +18,8 @@ test { _ = @import("behavior/pub_enum.zig"); _ = @import("behavior/slice_sentinel_comptime.zig"); _ = @import("behavior/truncate.zig"); + _ = @import("behavior/type.zig"); + _ = @import("behavior/type_info.zig"); _ = @import("behavior/usingnamespace.zig"); // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend @@ -124,7 +126,6 @@ test { _ = @import("behavior/bugs/1025.zig"); _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1120.zig"); - _ = @import("behavior/bugs/1322.zig"); _ = @import("behavior/bugs/1421.zig"); _ = @import("behavior/bugs/1442.zig"); _ = @import("behavior/bugs/1607.zig"); @@ -186,8 +187,8 @@ test { _ = @import("behavior/truncate_stage1.zig"); _ = @import("behavior/try.zig"); _ = @import("behavior/tuple.zig"); - _ = @import("behavior/type.zig"); - _ = @import("behavior/type_info.zig"); + _ = @import("behavior/type_stage1.zig"); + _ = @import("behavior/type_info_stage1.zig"); _ = @import("behavior/typename.zig"); _ = @import("behavior/union_stage1.zig"); _ = @import("behavior/union_with_members.zig"); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 76a024b88f..77d2eea6a8 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -120,3 +120,18 @@ test "size of extern struct with 128-bit field" { }) == 32); } } + +test "@ptrCast preserves alignment of bigger source" { + var x: u32 align(16) = 1234; + const ptr = @ptrCast(*u8, &x); + try expect(@TypeOf(ptr) == *align(16) u8); +} + +test "alignstack" { + try expect(fnWithAlignedStack() == 1234); +} + +fn fnWithAlignedStack() i32 { + @setAlignStack(256); + return 1234; +} diff --git a/test/behavior/align_stage1.zig b/test/behavior/align_stage1.zig index a4af92368e..30d08abb38 100644 --- a/test/behavior/align_stage1.zig +++ b/test/behavior/align_stage1.zig @@ -20,25 +20,6 @@ test "function alignment" { noop4(); } -var baz: packed struct { - a: u32, - b: u32, -} = undefined; - -test "packed struct alignment" { - try expect(@TypeOf(&baz.b) == *align(1) u32); -} - -const blah: packed struct { - a: u3, - b: u3, - c: u2, -} = undefined; - -test "bit field alignment" { - try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3); -} - test "implicitly decreasing fn alignment" { // function alignment is a compile error on wasm32/wasm64 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; @@ -90,12 +71,6 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { return 0x1; } -test "@ptrCast preserves alignment of bigger source" { - var x: u32 align(16) = 1234; - const ptr = @ptrCast(*u8, &x); - try expect(@TypeOf(ptr) == *align(16) u8); -} - test "runtime known array index has best alignment possible" { // take full advantage of over-alignment var array align(4) = [_]u8{ 1, 2, 3, 4 }; @@ -134,15 +109,6 @@ fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void { comptime try expect(@TypeOf(&ptr[index]) == T); } -test "alignstack" { - try expect(fnWithAlignedStack() == 1234); -} - -fn fnWithAlignedStack() i32 { - @setAlignStack(256); - return 1234; -} - test "alignment of function with c calling convention" { var runtime_nothing = nothing; const casted1 = @ptrCast(*const u8, runtime_nothing); diff --git a/test/behavior/bugs/1322.zig b/test/behavior/bugs/1322.zig deleted file mode 100644 index e0220a1719..0000000000 --- a/test/behavior/bugs/1322.zig +++ /dev/null @@ -1,19 +0,0 @@ -const std = @import("std"); - -const B = union(enum) { - c: C, - None, -}; - -const A = struct { - b: B, -}; - -const C = struct {}; - -test "tagged union with all void fields but a meaningful tag" { - var a: A = A{ .b = B{ .c = C{} } }; - try std.testing.expect(@as(std.meta.Tag(B), a.b) == std.meta.Tag(B).c); - a = A{ .b = B.None }; - try std.testing.expect(@as(std.meta.Tag(B), a.b) == std.meta.Tag(B).None); -} diff --git a/test/behavior/cast_llvm.zig b/test/behavior/cast_llvm.zig index 278bd2f7a6..57413df920 100644 --- a/test/behavior/cast_llvm.zig +++ b/test/behavior/cast_llvm.zig @@ -197,3 +197,71 @@ test "cast between *[N]void and []void" { var b: []void = &a; try expect(b.len == 4); } + +test "peer resolve arrays of different size to const slice" { + try expect(mem.eql(u8, boolToStr(true), "true")); + try expect(mem.eql(u8, boolToStr(false), "false")); + comptime try expect(mem.eql(u8, boolToStr(true), "true")); + comptime try expect(mem.eql(u8, boolToStr(false), "false")); +} +fn boolToStr(b: bool) []const u8 { + return if (b) "true" else "false"; +} + +test "cast f16 to wider types" { + const S = struct { + fn doTheTest() !void { + var x: f16 = 1234.0; + try expect(@as(f32, 1234.0) == x); + try expect(@as(f64, 1234.0) == x); + try expect(@as(f128, 1234.0) == x); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "cast f128 to narrower types" { + const S = struct { + fn doTheTest() !void { + var x: f128 = 1234.0; + try expect(@as(f16, 1234.0) == @floatCast(f16, x)); + try expect(@as(f32, 1234.0) == @floatCast(f32, x)); + try expect(@as(f64, 1234.0) == @floatCast(f64, x)); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "peer type resolution: unreachable, null, slice" { + const S = struct { + fn doTheTest(num: usize, word: []const u8) !void { + const result = switch (num) { + 0 => null, + 1 => word, + else => unreachable, + }; + try expect(mem.eql(u8, result.?, "hi")); + } + }; + try S.doTheTest(1, "hi"); +} + +test "cast i8 fn call peers to i32 result" { + const S = struct { + fn doTheTest() !void { + var cond = true; + const value: i32 = if (cond) smallBoi() else bigBoi(); + try expect(value == 123); + } + fn smallBoi() i8 { + return 123; + } + fn bigBoi() i16 { + return 1234; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/cast_stage1.zig b/test/behavior/cast_stage1.zig index 5564359f52..a4854b30b2 100644 --- a/test/behavior/cast_stage1.zig +++ b/test/behavior/cast_stage1.zig @@ -15,16 +15,6 @@ fn testCastIntToErr(err: anyerror) !void { try expect(error.ItBroke == y); } -test "peer resolve arrays of different size to const slice" { - try expect(mem.eql(u8, boolToStr(true), "true")); - try expect(mem.eql(u8, boolToStr(false), "false")); - comptime try expect(mem.eql(u8, boolToStr(true), "true")); - comptime try expect(mem.eql(u8, boolToStr(false), "false")); -} -fn boolToStr(b: bool) []const u8 { - return if (b) "true" else "false"; -} - test "peer resolve array and const slice" { try testPeerResolveArrayConstSlice(true); comptime try testPeerResolveArrayConstSlice(true); @@ -174,32 +164,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { try expect(mem.eql(u8, std.mem.sliceTo(@ptrCast([*:0]const u8, x[0].?), 0), "window name")); } -test "cast f16 to wider types" { - const S = struct { - fn doTheTest() !void { - var x: f16 = 1234.0; - try std.testing.expectEqual(@as(f32, 1234.0), x); - try std.testing.expectEqual(@as(f64, 1234.0), x); - try std.testing.expectEqual(@as(f128, 1234.0), x); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "cast f128 to narrower types" { - const S = struct { - fn doTheTest() !void { - var x: f128 = 1234.0; - try std.testing.expectEqual(@as(f16, 1234.0), @floatCast(f16, x)); - try std.testing.expectEqual(@as(f32, 1234.0), @floatCast(f32, x)); - try std.testing.expectEqual(@as(f64, 1234.0), @floatCast(f64, x)); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - test "vector casts" { const S = struct { fn doTheTest() !void { @@ -250,20 +214,6 @@ test "@floatCast cast down" { } } -test "peer type resolution: unreachable, null, slice" { - const S = struct { - fn doTheTest(num: usize, word: []const u8) !void { - const result = switch (num) { - 0 => null, - 1 => word, - else => unreachable, - }; - try expect(mem.eql(u8, result.?, "hi")); - } - }; - try S.doTheTest(1, "hi"); -} - test "peer type resolution: unreachable, error set, unreachable" { const Error = error{ FileDescriptorAlreadyPresentInSet, @@ -374,24 +324,6 @@ test "type coercion related to sentinel-termination" { comptime try S.doTheTest(); } -test "cast i8 fn call peers to i32 result" { - const S = struct { - fn doTheTest() !void { - var cond = true; - const value: i32 = if (cond) smallBoi() else bigBoi(); - try expect(value == 123); - } - fn smallBoi() i8 { - return 123; - } - fn bigBoi() i16 { - return 1234; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - test "peer type resolution implicit cast to return type" { const S = struct { fn doTheTest() !void { diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index f340fd1842..f485dadcd7 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -2,3 +2,62 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const expectError = std.testing.expectError; + +test "break and continue inside loop inside defer expression" { + testBreakContInDefer(10); + comptime testBreakContInDefer(10); +} + +fn testBreakContInDefer(x: usize) void { + defer { + var i: usize = 0; + while (i < x) : (i += 1) { + if (i < 5) continue; + if (i == 5) break; + } + expect(i == 5) catch @panic("test failure"); + } +} + +test "defer and labeled break" { + var i = @as(usize, 0); + + blk: { + defer i += 1; + break :blk; + } + + try expect(i == 1); +} + +test "errdefer does not apply to fn inside fn" { + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| try expect(e == error.Bad); +} + +fn testNestedFnErrDefer() anyerror!void { + var a: i32 = 0; + errdefer a += 1; + const S = struct { + fn baz() anyerror { + return error.Bad; + } + }; + return S.baz(); +} + +test "return variable while defer expression in scope to modify it" { + const S = struct { + fn doTheTest() !void { + try expect(notNull().? == 1); + } + + fn notNull() ?u8 { + var res: ?u8 = 1; + defer res = null; + return res; + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/defer_stage1.zig b/test/behavior/defer_stage1.zig index 53ce188928..bb8cd51755 100644 --- a/test/behavior/defer_stage1.zig +++ b/test/behavior/defer_stage1.zig @@ -38,65 +38,6 @@ test "mixing normal and error defers" { try expect(result[2] == 'a'); } -test "break and continue inside loop inside defer expression" { - testBreakContInDefer(10); - comptime testBreakContInDefer(10); -} - -fn testBreakContInDefer(x: usize) void { - defer { - var i: usize = 0; - while (i < x) : (i += 1) { - if (i < 5) continue; - if (i == 5) break; - } - expect(i == 5) catch @panic("test failure"); - } -} - -test "defer and labeled break" { - var i = @as(usize, 0); - - blk: { - defer i += 1; - break :blk; - } - - try expect(i == 1); -} - -test "errdefer does not apply to fn inside fn" { - if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| try expect(e == error.Bad); -} - -fn testNestedFnErrDefer() anyerror!void { - var a: i32 = 0; - errdefer a += 1; - const S = struct { - fn baz() anyerror { - return error.Bad; - } - }; - return S.baz(); -} - -test "return variable while defer expression in scope to modify it" { - const S = struct { - fn doTheTest() !void { - try expect(notNull().? == 1); - } - - fn notNull() ?u8 { - var res: ?u8 = 1; - defer res = null; - return res; - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - test "errdefer with payload" { const S = struct { fn foo() !i32 { diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 01d62f353f..9fb6e9a8ce 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -467,3 +467,28 @@ test "comptime shlWithOverflow" { try expect(ct_shifted == rt_shifted); } + +test "const ptr to variable data changes at runtime" { + try expect(foo_ref.name[0] == 'a'); + foo_ref.name = "b"; + try expect(foo_ref.name[0] == 'b'); +} + +const Foo = struct { + name: []const u8, +}; + +var foo_contents = Foo{ .name = "a" }; +const foo_ref = &foo_contents; + +test "runtime 128 bit integer division" { + var a: u128 = 152313999999999991610955792383; + var b: u128 = 10000000000000000000; + var c = a / b; + try expect(c == 15231399999); +} + +test "@tagName of @typeInfo" { + const str = @tagName(@typeInfo(u8)); + try expect(std.mem.eql(u8, str, "Int")); +} diff --git a/test/behavior/eval_stage1.zig b/test/behavior/eval_stage1.zig index 8f7e7dbe0d..5475de3aa0 100644 --- a/test/behavior/eval_stage1.zig +++ b/test/behavior/eval_stage1.zig @@ -86,19 +86,6 @@ fn testCompTimeUIntComparisons(x: u32) void { } } -test "const ptr to variable data changes at runtime" { - try expect(foo_ref.name[0] == 'a'); - foo_ref.name = "b"; - try expect(foo_ref.name[0] == 'b'); -} - -const Foo = struct { - name: []const u8, -}; - -var foo_contents = Foo{ .name = "a" }; -const foo_ref = &foo_contents; - const hi1 = "hi"; const hi2 = hi1; test "const global shares pointer with other same one" { @@ -162,13 +149,6 @@ test "const ptr to comptime mutable data is not memoized" { } } -test "runtime 128 bit integer division" { - var a: u128 = 152313999999999991610955792383; - var b: u128 = 10000000000000000000; - var c = a / b; - try expect(c == 15231399999); -} - const Wrapper = struct { T: type, }; @@ -196,11 +176,6 @@ test "call method with comptime pass-by-non-copying-value self parameter" { try expect(b == 2); } -test "@tagName of @typeInfo" { - const str = @tagName(@typeInfo(u8)); - try expect(std.mem.eql(u8, str, "Int")); -} - test "setting backward branch quota just before a generic fn call" { @setEvalBranchQuota(1001); loopNTimes(1001); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 1e47877fd6..b5841c58fb 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -199,3 +199,30 @@ test "struct field init with catch" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "packed struct field alignment" { + const Stage1 = struct { + var baz: packed struct { + a: u32, + b: u32, + } = undefined; + }; + const Stage2 = struct { + var baz: packed struct { + a: u32, + b: u32 align(1), + } = undefined; + }; + const S = if (builtin.zig_is_stage2) Stage2 else Stage1; + try expect(@TypeOf(&S.baz.b) == *align(1) u32); +} + +const blah: packed struct { + a: u3, + b: u3, + c: u2, +} = undefined; + +test "bit field alignment" { + try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3); +} diff --git a/test/behavior/type.zig b/test/behavior/type.zig index cd5d2c3e06..1430a153e9 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -39,84 +39,6 @@ test "Type.Int" { try testTypes(&[_]type{ u8, u32, i64 }); } -test "Type.Float" { - try testing.expect(f16 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 16 } })); - try testing.expect(f32 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 32 } })); - try testing.expect(f64 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 64 } })); - try testing.expect(f128 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 128 } })); - try testTypes(&[_]type{ f16, f32, f64, f128 }); -} - -test "Type.Pointer" { - try testTypes(&[_]type{ - // One Value Pointer Types - *u8, *const u8, - *volatile u8, *const volatile u8, - *align(4) u8, *align(4) const u8, - *align(4) volatile u8, *align(4) const volatile u8, - *align(8) u8, *align(8) const u8, - *align(8) volatile u8, *align(8) const volatile u8, - *allowzero u8, *allowzero const u8, - *allowzero volatile u8, *allowzero const volatile u8, - *allowzero align(4) u8, *allowzero align(4) const u8, - *allowzero align(4) volatile u8, *allowzero align(4) const volatile u8, - // Many Values Pointer Types - [*]u8, [*]const u8, - [*]volatile u8, [*]const volatile u8, - [*]align(4) u8, [*]align(4) const u8, - [*]align(4) volatile u8, [*]align(4) const volatile u8, - [*]align(8) u8, [*]align(8) const u8, - [*]align(8) volatile u8, [*]align(8) const volatile u8, - [*]allowzero u8, [*]allowzero const u8, - [*]allowzero volatile u8, [*]allowzero const volatile u8, - [*]allowzero align(4) u8, [*]allowzero align(4) const u8, - [*]allowzero align(4) volatile u8, [*]allowzero align(4) const volatile u8, - // Slice Types - []u8, []const u8, - []volatile u8, []const volatile u8, - []align(4) u8, []align(4) const u8, - []align(4) volatile u8, []align(4) const volatile u8, - []align(8) u8, []align(8) const u8, - []align(8) volatile u8, []align(8) const volatile u8, - []allowzero u8, []allowzero const u8, - []allowzero volatile u8, []allowzero const volatile u8, - []allowzero align(4) u8, []allowzero align(4) const u8, - []allowzero align(4) volatile u8, []allowzero align(4) const volatile u8, - // C Pointer Types - [*c]u8, [*c]const u8, - [*c]volatile u8, [*c]const volatile u8, - [*c]align(4) u8, [*c]align(4) const u8, - [*c]align(4) volatile u8, [*c]align(4) const volatile u8, - [*c]align(8) u8, [*c]align(8) const u8, - [*c]align(8) volatile u8, [*c]align(8) const volatile u8, - }); -} - -test "Type.Array" { - try testing.expect([123]u8 == @Type(TypeInfo{ - .Array = TypeInfo.Array{ - .len = 123, - .child = u8, - .sentinel = null, - }, - })); - try testing.expect([2]u32 == @Type(TypeInfo{ - .Array = TypeInfo.Array{ - .len = 2, - .child = u32, - .sentinel = null, - }, - })); - try testing.expect([2:0]u32 == @Type(TypeInfo{ - .Array = TypeInfo.Array{ - .len = 2, - .child = u32, - .sentinel = 0, - }, - })); - try testTypes(&[_]type{ [1]u8, [30]usize, [7]bool }); -} - test "Type.ComptimeFloat" { try testTypes(&[_]type{comptime_float}); } @@ -129,331 +51,9 @@ test "Type.Undefined" { test "Type.Null" { try testTypes(&[_]type{@TypeOf(null)}); } -test "@Type create slice with null sentinel" { - const Slice = @Type(TypeInfo{ - .Pointer = .{ - .size = .Slice, - .is_const = true, - .is_volatile = false, - .is_allowzero = false, - .alignment = 8, - .address_space = .generic, - .child = *i32, - .sentinel = null, - }, - }); - try testing.expect(Slice == []align(8) const *i32); -} -test "@Type picks up the sentinel value from TypeInfo" { - try testTypes(&[_]type{ - [11:0]u8, [4:10]u8, - [*:0]u8, [*:0]const u8, - [*:0]volatile u8, [*:0]const volatile u8, - [*:0]align(4) u8, [*:0]align(4) const u8, - [*:0]align(4) volatile u8, [*:0]align(4) const volatile u8, - [*:0]align(8) u8, [*:0]align(8) const u8, - [*:0]align(8) volatile u8, [*:0]align(8) const volatile u8, - [*:0]allowzero u8, [*:0]allowzero const u8, - [*:0]allowzero volatile u8, [*:0]allowzero const volatile u8, - [*:0]allowzero align(4) u8, [*:0]allowzero align(4) const u8, - [*:0]allowzero align(4) volatile u8, [*:0]allowzero align(4) const volatile u8, - [*:5]allowzero align(4) volatile u8, [*:5]allowzero align(4) const volatile u8, - [:0]u8, [:0]const u8, - [:0]volatile u8, [:0]const volatile u8, - [:0]align(4) u8, [:0]align(4) const u8, - [:0]align(4) volatile u8, [:0]align(4) const volatile u8, - [:0]align(8) u8, [:0]align(8) const u8, - [:0]align(8) volatile u8, [:0]align(8) const volatile u8, - [:0]allowzero u8, [:0]allowzero const u8, - [:0]allowzero volatile u8, [:0]allowzero const volatile u8, - [:0]allowzero align(4) u8, [:0]allowzero align(4) const u8, - [:0]allowzero align(4) volatile u8, [:0]allowzero align(4) const volatile u8, - [:4]allowzero align(4) volatile u8, [:4]allowzero align(4) const volatile u8, - }); -} - -test "Type.Optional" { - try testTypes(&[_]type{ - ?u8, - ?*u8, - ?[]u8, - ?[*]u8, - ?[*c]u8, - }); -} - -test "Type.ErrorUnion" { - try testTypes(&[_]type{ - error{}!void, - error{Error}!void, - }); -} - -test "Type.Opaque" { - const Opaque = @Type(.{ - .Opaque = .{ - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - try testing.expect(Opaque != opaque {}); - try testing.expectEqualSlices( - TypeInfo.Declaration, - &[_]TypeInfo.Declaration{}, - @typeInfo(Opaque).Opaque.decls, - ); -} - -test "Type.Vector" { - try testTypes(&[_]type{ - @Vector(0, u8), - @Vector(4, u8), - @Vector(8, *u8), - std.meta.Vector(0, u8), - std.meta.Vector(4, u8), - std.meta.Vector(8, *u8), - }); -} - -test "Type.AnyFrame" { - try testTypes(&[_]type{ - anyframe, - anyframe->u8, - anyframe->anyframe->u8, - }); -} test "Type.EnumLiteral" { try testTypes(&[_]type{ @TypeOf(.Dummy), }); } - -fn add(a: i32, b: i32) i32 { - return a + b; -} - -test "Type.Frame" { - try testTypes(&[_]type{ - @Frame(add), - }); -} - -test "Type.ErrorSet" { - // error sets don't compare equal so just check if they compile - _ = @Type(@typeInfo(error{})); - _ = @Type(@typeInfo(error{A})); - _ = @Type(@typeInfo(error{ A, B, C })); -} - -test "Type.Struct" { - const A = @Type(@typeInfo(struct { x: u8, y: u32 })); - const infoA = @typeInfo(A).Struct; - try testing.expectEqual(TypeInfo.ContainerLayout.Auto, infoA.layout); - try testing.expectEqualSlices(u8, "x", infoA.fields[0].name); - try testing.expectEqual(u8, infoA.fields[0].field_type); - try testing.expectEqual(@as(?u8, null), infoA.fields[0].default_value); - try testing.expectEqualSlices(u8, "y", infoA.fields[1].name); - try testing.expectEqual(u32, infoA.fields[1].field_type); - try testing.expectEqual(@as(?u32, null), infoA.fields[1].default_value); - try testing.expectEqualSlices(TypeInfo.Declaration, &[_]TypeInfo.Declaration{}, infoA.decls); - try testing.expectEqual(@as(bool, false), infoA.is_tuple); - - var a = A{ .x = 0, .y = 1 }; - try testing.expectEqual(@as(u8, 0), a.x); - try testing.expectEqual(@as(u32, 1), a.y); - a.y += 1; - try testing.expectEqual(@as(u32, 2), a.y); - - const B = @Type(@typeInfo(extern struct { x: u8, y: u32 = 5 })); - const infoB = @typeInfo(B).Struct; - try testing.expectEqual(TypeInfo.ContainerLayout.Extern, infoB.layout); - try testing.expectEqualSlices(u8, "x", infoB.fields[0].name); - try testing.expectEqual(u8, infoB.fields[0].field_type); - try testing.expectEqual(@as(?u8, null), infoB.fields[0].default_value); - try testing.expectEqualSlices(u8, "y", infoB.fields[1].name); - try testing.expectEqual(u32, infoB.fields[1].field_type); - try testing.expectEqual(@as(?u32, 5), infoB.fields[1].default_value); - try testing.expectEqual(@as(usize, 0), infoB.decls.len); - try testing.expectEqual(@as(bool, false), infoB.is_tuple); - - const C = @Type(@typeInfo(packed struct { x: u8 = 3, y: u32 = 5 })); - const infoC = @typeInfo(C).Struct; - try testing.expectEqual(TypeInfo.ContainerLayout.Packed, infoC.layout); - try testing.expectEqualSlices(u8, "x", infoC.fields[0].name); - try testing.expectEqual(u8, infoC.fields[0].field_type); - try testing.expectEqual(@as(?u8, 3), infoC.fields[0].default_value); - try testing.expectEqualSlices(u8, "y", infoC.fields[1].name); - try testing.expectEqual(u32, infoC.fields[1].field_type); - try testing.expectEqual(@as(?u32, 5), infoC.fields[1].default_value); - try testing.expectEqual(@as(usize, 0), infoC.decls.len); - try testing.expectEqual(@as(bool, false), infoC.is_tuple); -} - -test "Type.Enum" { - const Foo = @Type(.{ - .Enum = .{ - .layout = .Auto, - .tag_type = u8, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "a", .value = 1 }, - .{ .name = "b", .value = 5 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = true, - }, - }); - try testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive); - try testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a)); - try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b)); - const Bar = @Type(.{ - .Enum = .{ - .layout = .Extern, - .tag_type = u32, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "a", .value = 1 }, - .{ .name = "b", .value = 5 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = false, - }, - }); - try testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive); - try testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a)); - try testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b)); - try testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6))); -} - -test "Type.Union" { - const Untagged = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = null, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) }, - .{ .name = "float", .field_type = f32, .alignment = @alignOf(f32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - var untagged = Untagged{ .int = 1 }; - untagged.float = 2.0; - untagged.int = 3; - try testing.expectEqual(@as(i32, 3), untagged.int); - - const PackedUntagged = @Type(.{ - .Union = .{ - .layout = .Packed, - .tag_type = null, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, - .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - var packed_untagged = PackedUntagged{ .signed = -1 }; - try testing.expectEqual(@as(i32, -1), packed_untagged.signed); - try testing.expectEqual(~@as(u32, 0), packed_untagged.unsigned); - - const Tag = @Type(.{ - .Enum = .{ - .layout = .Auto, - .tag_type = u1, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "signed", .value = 0 }, - .{ .name = "unsigned", .value = 1 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = true, - }, - }); - const Tagged = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = Tag, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, - .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - var tagged = Tagged{ .signed = -1 }; - try testing.expectEqual(Tag.signed, tagged); - tagged = .{ .unsigned = 1 }; - try testing.expectEqual(Tag.unsigned, tagged); -} - -test "Type.Union from Type.Enum" { - const Tag = @Type(.{ - .Enum = .{ - .layout = .Auto, - .tag_type = u0, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "working_as_expected", .value = 0 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = true, - }, - }); - const T = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = Tag, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - _ = T; - _ = @typeInfo(T).Union; -} - -test "Type.Union from regular enum" { - const E = enum { working_as_expected }; - const T = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = E, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - _ = T; - _ = @typeInfo(T).Union; -} - -test "Type.Fn" { - // wasm doesn't support align attributes on functions - if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; - - const foo = struct { - fn func(a: usize, b: bool) align(4) callconv(.C) usize { - _ = a; - _ = b; - return 0; - } - }.func; - const Foo = @Type(@typeInfo(@TypeOf(foo))); - const foo_2: Foo = foo; - _ = foo_2; -} - -test "Type.BoundFn" { - // wasm doesn't support align attributes on functions - if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; - - const TestStruct = packed struct { - pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void { - _ = self; - } - }; - const test_instance: TestStruct = undefined; - try testing.expect(std.meta.eql( - @typeName(@TypeOf(test_instance.foo)), - @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))), - )); -} diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index dd9a5b7839..1c67edf34f 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -8,18 +8,6 @@ const TypeId = std.builtin.TypeId; const expect = std.testing.expect; const expectEqualStrings = std.testing.expectEqualStrings; -test "type info: tag type, void info" { - try testBasic(); - comptime try testBasic(); -} - -fn testBasic() !void { - try expect(@typeInfo(TypeInfo).Union.tag_type == TypeId); - const void_info = @typeInfo(void); - try expect(void_info == TypeId.Void); - try expect(void_info.Void == {}); -} - test "type info: integer, floating point type info" { try testIntFloat(); comptime try testIntFloat(); @@ -36,107 +24,6 @@ fn testIntFloat() !void { try expect(f64_info.Float.bits == 64); } -test "type info: pointer type info" { - try testPointer(); - comptime try testPointer(); -} - -fn testPointer() !void { - const u32_ptr_info = @typeInfo(*u32); - try expect(u32_ptr_info == .Pointer); - try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); - try expect(u32_ptr_info.Pointer.is_const == false); - try expect(u32_ptr_info.Pointer.is_volatile == false); - try expect(u32_ptr_info.Pointer.alignment == @alignOf(u32)); - try expect(u32_ptr_info.Pointer.child == u32); - try expect(u32_ptr_info.Pointer.sentinel == null); -} - -test "type info: unknown length pointer type info" { - try testUnknownLenPtr(); - comptime try testUnknownLenPtr(); -} - -fn testUnknownLenPtr() !void { - const u32_ptr_info = @typeInfo([*]const volatile f64); - try expect(u32_ptr_info == .Pointer); - try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); - try expect(u32_ptr_info.Pointer.is_const == true); - try expect(u32_ptr_info.Pointer.is_volatile == true); - try expect(u32_ptr_info.Pointer.sentinel == null); - try expect(u32_ptr_info.Pointer.alignment == @alignOf(f64)); - try expect(u32_ptr_info.Pointer.child == f64); -} - -test "type info: null terminated pointer type info" { - try testNullTerminatedPtr(); - comptime try testNullTerminatedPtr(); -} - -fn testNullTerminatedPtr() !void { - const ptr_info = @typeInfo([*:0]u8); - try expect(ptr_info == .Pointer); - try expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); - try expect(ptr_info.Pointer.is_const == false); - try expect(ptr_info.Pointer.is_volatile == false); - try expect(ptr_info.Pointer.sentinel.? == 0); - - try expect(@typeInfo([:0]u8).Pointer.sentinel != null); -} - -test "type info: C pointer type info" { - try testCPtr(); - comptime try testCPtr(); -} - -fn testCPtr() !void { - const ptr_info = @typeInfo([*c]align(4) const i8); - try expect(ptr_info == .Pointer); - try expect(ptr_info.Pointer.size == .C); - try expect(ptr_info.Pointer.is_const); - try expect(!ptr_info.Pointer.is_volatile); - try expect(ptr_info.Pointer.alignment == 4); - try expect(ptr_info.Pointer.child == i8); -} - -test "type info: slice type info" { - try testSlice(); - comptime try testSlice(); -} - -fn testSlice() !void { - const u32_slice_info = @typeInfo([]u32); - try expect(u32_slice_info == .Pointer); - try expect(u32_slice_info.Pointer.size == .Slice); - try expect(u32_slice_info.Pointer.is_const == false); - try expect(u32_slice_info.Pointer.is_volatile == false); - try expect(u32_slice_info.Pointer.alignment == 4); - try expect(u32_slice_info.Pointer.child == u32); -} - -test "type info: array type info" { - try testArray(); - comptime try testArray(); -} - -fn testArray() !void { - { - const info = @typeInfo([42]u8); - try expect(info == .Array); - try expect(info.Array.len == 42); - try expect(info.Array.child == u8); - try expect(info.Array.sentinel == null); - } - - { - const info = @typeInfo([10:0]u8); - try expect(info.Array.len == 10); - try expect(info.Array.child == u8); - try expect(info.Array.sentinel.? == @as(u8, 0)); - try expect(@sizeOf([10:0]u8) == info.Array.len + 1); - } -} - test "type info: optional type info" { try testOptional(); comptime try testOptional(); @@ -147,344 +34,3 @@ fn testOptional() !void { try expect(null_info == .Optional); try expect(null_info.Optional.child == void); } - -test "type info: error set, error union info" { - try testErrorSet(); - comptime try testErrorSet(); -} - -fn testErrorSet() !void { - const TestErrorSet = error{ - First, - Second, - Third, - }; - - const error_set_info = @typeInfo(TestErrorSet); - try expect(error_set_info == .ErrorSet); - try expect(error_set_info.ErrorSet.?.len == 3); - try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First")); - - const error_union_info = @typeInfo(TestErrorSet!usize); - try expect(error_union_info == .ErrorUnion); - try expect(error_union_info.ErrorUnion.error_set == TestErrorSet); - try expect(error_union_info.ErrorUnion.payload == usize); - - const global_info = @typeInfo(anyerror); - try expect(global_info == .ErrorSet); - try expect(global_info.ErrorSet == null); -} - -test "type info: enum info" { - try testEnum(); - comptime try testEnum(); -} - -fn testEnum() !void { - const Os = enum { - Windows, - Macos, - Linux, - FreeBSD, - }; - - const os_info = @typeInfo(Os); - try expect(os_info == .Enum); - try expect(os_info.Enum.layout == .Auto); - try expect(os_info.Enum.fields.len == 4); - try expect(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); - try expect(os_info.Enum.fields[3].value == 3); - try expect(os_info.Enum.tag_type == u2); - try expect(os_info.Enum.decls.len == 0); -} - -test "type info: union info" { - try testUnion(); - comptime try testUnion(); -} - -fn testUnion() !void { - const typeinfo_info = @typeInfo(TypeInfo); - try expect(typeinfo_info == .Union); - try expect(typeinfo_info.Union.layout == .Auto); - try expect(typeinfo_info.Union.tag_type.? == TypeId); - try expect(typeinfo_info.Union.fields.len == 25); - try expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int)); - try expect(typeinfo_info.Union.decls.len == 22); - - const TestNoTagUnion = union { - Foo: void, - Bar: u32, - }; - - const notag_union_info = @typeInfo(TestNoTagUnion); - try expect(notag_union_info == .Union); - try expect(notag_union_info.Union.tag_type == null); - try expect(notag_union_info.Union.layout == .Auto); - try expect(notag_union_info.Union.fields.len == 2); - try expect(notag_union_info.Union.fields[0].alignment == @alignOf(void)); - try expect(notag_union_info.Union.fields[1].field_type == u32); - try expect(notag_union_info.Union.fields[1].alignment == @alignOf(u32)); - - const TestExternUnion = extern union { - foo: *anyopaque, - }; - - const extern_union_info = @typeInfo(TestExternUnion); - try expect(extern_union_info.Union.layout == .Extern); - try expect(extern_union_info.Union.tag_type == null); - try expect(extern_union_info.Union.fields[0].field_type == *anyopaque); -} - -test "type info: struct info" { - try testStruct(); - comptime try testStruct(); -} - -fn testStruct() !void { - const unpacked_struct_info = @typeInfo(TestUnpackedStruct); - try expect(unpacked_struct_info.Struct.is_tuple == false); - try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32)); - try expect(unpacked_struct_info.Struct.fields[0].default_value.? == 4); - try expectEqualStrings("foobar", unpacked_struct_info.Struct.fields[1].default_value.?); - - const struct_info = @typeInfo(TestStruct); - try expect(struct_info == .Struct); - try expect(struct_info.Struct.is_tuple == false); - try expect(struct_info.Struct.layout == .Packed); - try expect(struct_info.Struct.fields.len == 4); - try expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize)); - try expect(struct_info.Struct.fields[2].field_type == *TestStruct); - try expect(struct_info.Struct.fields[2].default_value == null); - try expect(struct_info.Struct.fields[3].default_value.? == 4); - try expect(struct_info.Struct.fields[3].alignment == 1); - try expect(struct_info.Struct.decls.len == 2); - try expect(struct_info.Struct.decls[0].is_pub); - try expect(!struct_info.Struct.decls[0].data.Fn.is_extern); - try expect(struct_info.Struct.decls[0].data.Fn.lib_name == null); - try expect(struct_info.Struct.decls[0].data.Fn.return_type == void); - try expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void); -} - -const TestUnpackedStruct = struct { - fieldA: u32 = 4, - fieldB: *const [6:0]u8 = "foobar", -}; - -const TestStruct = packed struct { - fieldA: usize align(2 * @alignOf(usize)), - fieldB: void, - fieldC: *Self, - fieldD: u32 = 4, - - pub fn foo(self: *const Self) void { - _ = self; - } - const Self = @This(); -}; - -test "type info: opaque info" { - try testOpaque(); - comptime try testOpaque(); -} - -fn testOpaque() !void { - const Foo = opaque { - const A = 1; - fn b() void {} - }; - - const foo_info = @typeInfo(Foo); - try expect(foo_info.Opaque.decls.len == 2); -} - -test "type info: function type info" { - // wasm doesn't support align attributes on functions - if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; - try testFunction(); - comptime try testFunction(); -} - -fn testFunction() !void { - const fn_info = @typeInfo(@TypeOf(foo)); - try expect(fn_info == .Fn); - try expect(fn_info.Fn.alignment > 0); - try expect(fn_info.Fn.calling_convention == .C); - try expect(!fn_info.Fn.is_generic); - try expect(fn_info.Fn.args.len == 2); - try expect(fn_info.Fn.is_var_args); - try expect(fn_info.Fn.return_type.? == usize); - const fn_aligned_info = @typeInfo(@TypeOf(fooAligned)); - try expect(fn_aligned_info.Fn.alignment == 4); - - const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo)); - try expect(bound_fn_info == .BoundFn); - try expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); -} - -extern fn foo(a: usize, b: bool, ...) callconv(.C) usize; -extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; - -test "typeInfo with comptime parameter in struct fn def" { - const S = struct { - pub fn func(comptime x: f32) void { - _ = x; - } - }; - comptime var info = @typeInfo(S); - _ = info; -} - -test "type info: vectors" { - try testVector(); - comptime try testVector(); -} - -fn testVector() !void { - const vec_info = @typeInfo(std.meta.Vector(4, i32)); - try expect(vec_info == .Vector); - try expect(vec_info.Vector.len == 4); - try expect(vec_info.Vector.child == i32); -} - -test "type info: anyframe and anyframe->T" { - try testAnyFrame(); - comptime try testAnyFrame(); -} - -fn testAnyFrame() !void { - { - const anyframe_info = @typeInfo(anyframe->i32); - try expect(anyframe_info == .AnyFrame); - try expect(anyframe_info.AnyFrame.child.? == i32); - } - - { - const anyframe_info = @typeInfo(anyframe); - try expect(anyframe_info == .AnyFrame); - try expect(anyframe_info.AnyFrame.child == null); - } -} - -test "type info: pass to function" { - _ = passTypeInfo(@typeInfo(void)); - _ = comptime passTypeInfo(@typeInfo(void)); -} - -fn passTypeInfo(comptime info: TypeInfo) type { - _ = info; - return void; -} - -test "type info: TypeId -> TypeInfo impl cast" { - _ = passTypeInfo(TypeId.Void); - _ = comptime passTypeInfo(TypeId.Void); -} - -test "type info: extern fns with and without lib names" { - const S = struct { - extern fn bar1() void; - extern "cool" fn bar2() void; - }; - const info = @typeInfo(S); - comptime { - for (info.Struct.decls) |decl| { - if (std.mem.eql(u8, decl.name, "bar1")) { - try expect(decl.data.Fn.lib_name == null); - } else { - try expectEqualStrings("cool", decl.data.Fn.lib_name.?); - } - } - } -} - -test "data field is a compile-time value" { - const S = struct { - const Bar = @as(isize, -1); - }; - comptime try expect(@typeInfo(S).Struct.decls[0].data.Var == isize); -} - -test "sentinel of opaque pointer type" { - const c_void_info = @typeInfo(*anyopaque); - try expect(c_void_info.Pointer.sentinel == null); -} - -test "@typeInfo does not force declarations into existence" { - const S = struct { - x: i32, - - fn doNotReferenceMe() void { - @compileError("test failed"); - } - }; - comptime try expect(@typeInfo(S).Struct.fields.len == 1); -} - -test "defaut value for a var-typed field" { - const S = struct { x: anytype }; - try expect(@typeInfo(S).Struct.fields[0].default_value == null); -} - -fn add(a: i32, b: i32) i32 { - return a + b; -} - -test "type info for async frames" { - switch (@typeInfo(@Frame(add))) { - .Frame => |frame| { - try expect(frame.function == add); - }, - else => unreachable, - } -} - -test "type info: value is correctly copied" { - comptime { - var ptrInfo = @typeInfo([]u32); - ptrInfo.Pointer.size = .One; - try expect(@typeInfo([]u32).Pointer.size == .Slice); - } -} - -test "Declarations are returned in declaration order" { - const S = struct { - const a = 1; - const b = 2; - const c = 3; - const d = 4; - const e = 5; - }; - const d = @typeInfo(S).Struct.decls; - try expect(std.mem.eql(u8, d[0].name, "a")); - try expect(std.mem.eql(u8, d[1].name, "b")); - try expect(std.mem.eql(u8, d[2].name, "c")); - try expect(std.mem.eql(u8, d[3].name, "d")); - try expect(std.mem.eql(u8, d[4].name, "e")); -} - -test "Struct.is_tuple" { - try expect(@typeInfo(@TypeOf(.{0})).Struct.is_tuple); - try expect(!@typeInfo(@TypeOf(.{ .a = 0 })).Struct.is_tuple); -} - -test "StructField.is_comptime" { - const info = @typeInfo(struct { x: u8 = 3, comptime y: u32 = 5 }).Struct; - try expect(!info.fields[0].is_comptime); - try expect(info.fields[1].is_comptime); -} - -test "typeInfo resolves usingnamespace declarations" { - const A = struct { - pub const f1 = 42; - }; - - const B = struct { - const f0 = 42; - usingnamespace A; - }; - - try expect(@typeInfo(B).Struct.decls.len == 2); - //a -} diff --git a/test/behavior/type_info_stage1.zig b/test/behavior/type_info_stage1.zig new file mode 100644 index 0000000000..625536f1fa --- /dev/null +++ b/test/behavior/type_info_stage1.zig @@ -0,0 +1,463 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const mem = std.mem; + +const TypeInfo = std.builtin.TypeInfo; +const TypeId = std.builtin.TypeId; + +const expect = std.testing.expect; +const expectEqualStrings = std.testing.expectEqualStrings; + +test "type info: tag type, void info" { + try testBasic(); + comptime try testBasic(); +} + +fn testBasic() !void { + try expect(@typeInfo(TypeInfo).Union.tag_type == TypeId); + const void_info = @typeInfo(void); + try expect(void_info == TypeId.Void); + try expect(void_info.Void == {}); +} + +test "type info: pointer type info" { + try testPointer(); + comptime try testPointer(); +} + +fn testPointer() !void { + const u32_ptr_info = @typeInfo(*u32); + try expect(u32_ptr_info == .Pointer); + try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); + try expect(u32_ptr_info.Pointer.is_const == false); + try expect(u32_ptr_info.Pointer.is_volatile == false); + try expect(u32_ptr_info.Pointer.alignment == @alignOf(u32)); + try expect(u32_ptr_info.Pointer.child == u32); + try expect(u32_ptr_info.Pointer.sentinel == null); +} + +test "type info: unknown length pointer type info" { + try testUnknownLenPtr(); + comptime try testUnknownLenPtr(); +} + +fn testUnknownLenPtr() !void { + const u32_ptr_info = @typeInfo([*]const volatile f64); + try expect(u32_ptr_info == .Pointer); + try expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + try expect(u32_ptr_info.Pointer.is_const == true); + try expect(u32_ptr_info.Pointer.is_volatile == true); + try expect(u32_ptr_info.Pointer.sentinel == null); + try expect(u32_ptr_info.Pointer.alignment == @alignOf(f64)); + try expect(u32_ptr_info.Pointer.child == f64); +} + +test "type info: null terminated pointer type info" { + try testNullTerminatedPtr(); + comptime try testNullTerminatedPtr(); +} + +fn testNullTerminatedPtr() !void { + const ptr_info = @typeInfo([*:0]u8); + try expect(ptr_info == .Pointer); + try expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + try expect(ptr_info.Pointer.is_const == false); + try expect(ptr_info.Pointer.is_volatile == false); + try expect(ptr_info.Pointer.sentinel.? == 0); + + try expect(@typeInfo([:0]u8).Pointer.sentinel != null); +} + +test "type info: C pointer type info" { + try testCPtr(); + comptime try testCPtr(); +} + +fn testCPtr() !void { + const ptr_info = @typeInfo([*c]align(4) const i8); + try expect(ptr_info == .Pointer); + try expect(ptr_info.Pointer.size == .C); + try expect(ptr_info.Pointer.is_const); + try expect(!ptr_info.Pointer.is_volatile); + try expect(ptr_info.Pointer.alignment == 4); + try expect(ptr_info.Pointer.child == i8); +} + +test "type info: slice type info" { + try testSlice(); + comptime try testSlice(); +} + +fn testSlice() !void { + const u32_slice_info = @typeInfo([]u32); + try expect(u32_slice_info == .Pointer); + try expect(u32_slice_info.Pointer.size == .Slice); + try expect(u32_slice_info.Pointer.is_const == false); + try expect(u32_slice_info.Pointer.is_volatile == false); + try expect(u32_slice_info.Pointer.alignment == 4); + try expect(u32_slice_info.Pointer.child == u32); +} + +test "type info: array type info" { + try testArray(); + comptime try testArray(); +} + +fn testArray() !void { + { + const info = @typeInfo([42]u8); + try expect(info == .Array); + try expect(info.Array.len == 42); + try expect(info.Array.child == u8); + try expect(info.Array.sentinel == null); + } + + { + const info = @typeInfo([10:0]u8); + try expect(info.Array.len == 10); + try expect(info.Array.child == u8); + try expect(info.Array.sentinel.? == @as(u8, 0)); + try expect(@sizeOf([10:0]u8) == info.Array.len + 1); + } +} + +test "type info: error set, error union info" { + try testErrorSet(); + comptime try testErrorSet(); +} + +fn testErrorSet() !void { + const TestErrorSet = error{ + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + try expect(error_set_info == .ErrorSet); + try expect(error_set_info.ErrorSet.?.len == 3); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First")); + + const error_union_info = @typeInfo(TestErrorSet!usize); + try expect(error_union_info == .ErrorUnion); + try expect(error_union_info.ErrorUnion.error_set == TestErrorSet); + try expect(error_union_info.ErrorUnion.payload == usize); + + const global_info = @typeInfo(anyerror); + try expect(global_info == .ErrorSet); + try expect(global_info.ErrorSet == null); +} + +test "type info: enum info" { + try testEnum(); + comptime try testEnum(); +} + +fn testEnum() !void { + const Os = enum { + Windows, + Macos, + Linux, + FreeBSD, + }; + + const os_info = @typeInfo(Os); + try expect(os_info == .Enum); + try expect(os_info.Enum.layout == .Auto); + try expect(os_info.Enum.fields.len == 4); + try expect(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); + try expect(os_info.Enum.fields[3].value == 3); + try expect(os_info.Enum.tag_type == u2); + try expect(os_info.Enum.decls.len == 0); +} + +test "type info: union info" { + try testUnion(); + comptime try testUnion(); +} + +fn testUnion() !void { + const typeinfo_info = @typeInfo(TypeInfo); + try expect(typeinfo_info == .Union); + try expect(typeinfo_info.Union.layout == .Auto); + try expect(typeinfo_info.Union.tag_type.? == TypeId); + try expect(typeinfo_info.Union.fields.len == 25); + try expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int)); + try expect(typeinfo_info.Union.decls.len == 22); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + try expect(notag_union_info == .Union); + try expect(notag_union_info.Union.tag_type == null); + try expect(notag_union_info.Union.layout == .Auto); + try expect(notag_union_info.Union.fields.len == 2); + try expect(notag_union_info.Union.fields[0].alignment == @alignOf(void)); + try expect(notag_union_info.Union.fields[1].field_type == u32); + try expect(notag_union_info.Union.fields[1].alignment == @alignOf(u32)); + + const TestExternUnion = extern union { + foo: *anyopaque, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + try expect(extern_union_info.Union.layout == .Extern); + try expect(extern_union_info.Union.tag_type == null); + try expect(extern_union_info.Union.fields[0].field_type == *anyopaque); +} + +test "type info: struct info" { + try testStruct(); + comptime try testStruct(); +} + +fn testStruct() !void { + const unpacked_struct_info = @typeInfo(TestUnpackedStruct); + try expect(unpacked_struct_info.Struct.is_tuple == false); + try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32)); + try expect(unpacked_struct_info.Struct.fields[0].default_value.? == 4); + try expectEqualStrings("foobar", unpacked_struct_info.Struct.fields[1].default_value.?); + + const struct_info = @typeInfo(TestStruct); + try expect(struct_info == .Struct); + try expect(struct_info.Struct.is_tuple == false); + try expect(struct_info.Struct.layout == .Packed); + try expect(struct_info.Struct.fields.len == 4); + try expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize)); + try expect(struct_info.Struct.fields[2].field_type == *TestStruct); + try expect(struct_info.Struct.fields[2].default_value == null); + try expect(struct_info.Struct.fields[3].default_value.? == 4); + try expect(struct_info.Struct.fields[3].alignment == 1); + try expect(struct_info.Struct.decls.len == 2); + try expect(struct_info.Struct.decls[0].is_pub); + try expect(!struct_info.Struct.decls[0].data.Fn.is_extern); + try expect(struct_info.Struct.decls[0].data.Fn.lib_name == null); + try expect(struct_info.Struct.decls[0].data.Fn.return_type == void); + try expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void); +} + +const TestUnpackedStruct = struct { + fieldA: u32 = 4, + fieldB: *const [6:0]u8 = "foobar", +}; + +const TestStruct = packed struct { + fieldA: usize align(2 * @alignOf(usize)), + fieldB: void, + fieldC: *Self, + fieldD: u32 = 4, + + pub fn foo(self: *const Self) void { + _ = self; + } + const Self = @This(); +}; + +test "type info: opaque info" { + try testOpaque(); + comptime try testOpaque(); +} + +fn testOpaque() !void { + const Foo = opaque { + const A = 1; + fn b() void {} + }; + + const foo_info = @typeInfo(Foo); + try expect(foo_info.Opaque.decls.len == 2); +} + +test "type info: function type info" { + // wasm doesn't support align attributes on functions + if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; + try testFunction(); + comptime try testFunction(); +} + +fn testFunction() !void { + const fn_info = @typeInfo(@TypeOf(foo)); + try expect(fn_info == .Fn); + try expect(fn_info.Fn.alignment > 0); + try expect(fn_info.Fn.calling_convention == .C); + try expect(!fn_info.Fn.is_generic); + try expect(fn_info.Fn.args.len == 2); + try expect(fn_info.Fn.is_var_args); + try expect(fn_info.Fn.return_type.? == usize); + const fn_aligned_info = @typeInfo(@TypeOf(fooAligned)); + try expect(fn_aligned_info.Fn.alignment == 4); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo)); + try expect(bound_fn_info == .BoundFn); + try expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); +} + +extern fn foo(a: usize, b: bool, ...) callconv(.C) usize; +extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; + +test "typeInfo with comptime parameter in struct fn def" { + const S = struct { + pub fn func(comptime x: f32) void { + _ = x; + } + }; + comptime var info = @typeInfo(S); + _ = info; +} + +test "type info: vectors" { + try testVector(); + comptime try testVector(); +} + +fn testVector() !void { + const vec_info = @typeInfo(std.meta.Vector(4, i32)); + try expect(vec_info == .Vector); + try expect(vec_info.Vector.len == 4); + try expect(vec_info.Vector.child == i32); +} + +test "type info: anyframe and anyframe->T" { + try testAnyFrame(); + comptime try testAnyFrame(); +} + +fn testAnyFrame() !void { + { + const anyframe_info = @typeInfo(anyframe->i32); + try expect(anyframe_info == .AnyFrame); + try expect(anyframe_info.AnyFrame.child.? == i32); + } + + { + const anyframe_info = @typeInfo(anyframe); + try expect(anyframe_info == .AnyFrame); + try expect(anyframe_info.AnyFrame.child == null); + } +} + +test "type info: pass to function" { + _ = passTypeInfo(@typeInfo(void)); + _ = comptime passTypeInfo(@typeInfo(void)); +} + +fn passTypeInfo(comptime info: TypeInfo) type { + _ = info; + return void; +} + +test "type info: TypeId -> TypeInfo impl cast" { + _ = passTypeInfo(TypeId.Void); + _ = comptime passTypeInfo(TypeId.Void); +} + +test "type info: extern fns with and without lib names" { + const S = struct { + extern fn bar1() void; + extern "cool" fn bar2() void; + }; + const info = @typeInfo(S); + comptime { + for (info.Struct.decls) |decl| { + if (std.mem.eql(u8, decl.name, "bar1")) { + try expect(decl.data.Fn.lib_name == null); + } else { + try expectEqualStrings("cool", decl.data.Fn.lib_name.?); + } + } + } +} + +test "data field is a compile-time value" { + const S = struct { + const Bar = @as(isize, -1); + }; + comptime try expect(@typeInfo(S).Struct.decls[0].data.Var == isize); +} + +test "sentinel of opaque pointer type" { + const c_void_info = @typeInfo(*anyopaque); + try expect(c_void_info.Pointer.sentinel == null); +} + +test "@typeInfo does not force declarations into existence" { + const S = struct { + x: i32, + + fn doNotReferenceMe() void { + @compileError("test failed"); + } + }; + comptime try expect(@typeInfo(S).Struct.fields.len == 1); +} + +test "defaut value for a var-typed field" { + const S = struct { x: anytype }; + try expect(@typeInfo(S).Struct.fields[0].default_value == null); +} + +fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "type info for async frames" { + switch (@typeInfo(@Frame(add))) { + .Frame => |frame| { + try expect(frame.function == add); + }, + else => unreachable, + } +} + +test "type info: value is correctly copied" { + comptime { + var ptrInfo = @typeInfo([]u32); + ptrInfo.Pointer.size = .One; + try expect(@typeInfo([]u32).Pointer.size == .Slice); + } +} + +test "Declarations are returned in declaration order" { + const S = struct { + const a = 1; + const b = 2; + const c = 3; + const d = 4; + const e = 5; + }; + const d = @typeInfo(S).Struct.decls; + try expect(std.mem.eql(u8, d[0].name, "a")); + try expect(std.mem.eql(u8, d[1].name, "b")); + try expect(std.mem.eql(u8, d[2].name, "c")); + try expect(std.mem.eql(u8, d[3].name, "d")); + try expect(std.mem.eql(u8, d[4].name, "e")); +} + +test "Struct.is_tuple" { + try expect(@typeInfo(@TypeOf(.{0})).Struct.is_tuple); + try expect(!@typeInfo(@TypeOf(.{ .a = 0 })).Struct.is_tuple); +} + +test "StructField.is_comptime" { + const info = @typeInfo(struct { x: u8 = 3, comptime y: u32 = 5 }).Struct; + try expect(!info.fields[0].is_comptime); + try expect(info.fields[1].is_comptime); +} + +test "typeInfo resolves usingnamespace declarations" { + const A = struct { + pub const f1 = 42; + }; + + const B = struct { + const f0 = 42; + usingnamespace A; + }; + + try expect(@typeInfo(B).Struct.decls.len == 2); + //a +} diff --git a/test/behavior/type_stage1.zig b/test/behavior/type_stage1.zig new file mode 100644 index 0000000000..914bf166d6 --- /dev/null +++ b/test/behavior/type_stage1.zig @@ -0,0 +1,411 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const TypeInfo = std.builtin.TypeInfo; +const testing = std.testing; + +fn testTypes(comptime types: []const type) !void { + inline for (types) |testType| { + try testing.expect(testType == @Type(@typeInfo(testType))); + } +} + +test "Type.Float" { + try testing.expect(f16 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 16 } })); + try testing.expect(f32 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 32 } })); + try testing.expect(f64 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 64 } })); + try testing.expect(f128 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 128 } })); + try testTypes(&[_]type{ f16, f32, f64, f128 }); +} + +test "Type.Pointer" { + try testTypes(&[_]type{ + // One Value Pointer Types + *u8, *const u8, + *volatile u8, *const volatile u8, + *align(4) u8, *align(4) const u8, + *align(4) volatile u8, *align(4) const volatile u8, + *align(8) u8, *align(8) const u8, + *align(8) volatile u8, *align(8) const volatile u8, + *allowzero u8, *allowzero const u8, + *allowzero volatile u8, *allowzero const volatile u8, + *allowzero align(4) u8, *allowzero align(4) const u8, + *allowzero align(4) volatile u8, *allowzero align(4) const volatile u8, + // Many Values Pointer Types + [*]u8, [*]const u8, + [*]volatile u8, [*]const volatile u8, + [*]align(4) u8, [*]align(4) const u8, + [*]align(4) volatile u8, [*]align(4) const volatile u8, + [*]align(8) u8, [*]align(8) const u8, + [*]align(8) volatile u8, [*]align(8) const volatile u8, + [*]allowzero u8, [*]allowzero const u8, + [*]allowzero volatile u8, [*]allowzero const volatile u8, + [*]allowzero align(4) u8, [*]allowzero align(4) const u8, + [*]allowzero align(4) volatile u8, [*]allowzero align(4) const volatile u8, + // Slice Types + []u8, []const u8, + []volatile u8, []const volatile u8, + []align(4) u8, []align(4) const u8, + []align(4) volatile u8, []align(4) const volatile u8, + []align(8) u8, []align(8) const u8, + []align(8) volatile u8, []align(8) const volatile u8, + []allowzero u8, []allowzero const u8, + []allowzero volatile u8, []allowzero const volatile u8, + []allowzero align(4) u8, []allowzero align(4) const u8, + []allowzero align(4) volatile u8, []allowzero align(4) const volatile u8, + // C Pointer Types + [*c]u8, [*c]const u8, + [*c]volatile u8, [*c]const volatile u8, + [*c]align(4) u8, [*c]align(4) const u8, + [*c]align(4) volatile u8, [*c]align(4) const volatile u8, + [*c]align(8) u8, [*c]align(8) const u8, + [*c]align(8) volatile u8, [*c]align(8) const volatile u8, + }); +} + +test "Type.Array" { + try testing.expect([123]u8 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 123, + .child = u8, + .sentinel = null, + }, + })); + try testing.expect([2]u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .sentinel = null, + }, + })); + try testing.expect([2:0]u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .sentinel = 0, + }, + })); + try testTypes(&[_]type{ [1]u8, [30]usize, [7]bool }); +} + +test "@Type create slice with null sentinel" { + const Slice = @Type(TypeInfo{ + .Pointer = .{ + .size = .Slice, + .is_const = true, + .is_volatile = false, + .is_allowzero = false, + .alignment = 8, + .address_space = .generic, + .child = *i32, + .sentinel = null, + }, + }); + try testing.expect(Slice == []align(8) const *i32); +} +test "@Type picks up the sentinel value from TypeInfo" { + try testTypes(&[_]type{ + [11:0]u8, [4:10]u8, + [*:0]u8, [*:0]const u8, + [*:0]volatile u8, [*:0]const volatile u8, + [*:0]align(4) u8, [*:0]align(4) const u8, + [*:0]align(4) volatile u8, [*:0]align(4) const volatile u8, + [*:0]align(8) u8, [*:0]align(8) const u8, + [*:0]align(8) volatile u8, [*:0]align(8) const volatile u8, + [*:0]allowzero u8, [*:0]allowzero const u8, + [*:0]allowzero volatile u8, [*:0]allowzero const volatile u8, + [*:0]allowzero align(4) u8, [*:0]allowzero align(4) const u8, + [*:0]allowzero align(4) volatile u8, [*:0]allowzero align(4) const volatile u8, + [*:5]allowzero align(4) volatile u8, [*:5]allowzero align(4) const volatile u8, + [:0]u8, [:0]const u8, + [:0]volatile u8, [:0]const volatile u8, + [:0]align(4) u8, [:0]align(4) const u8, + [:0]align(4) volatile u8, [:0]align(4) const volatile u8, + [:0]align(8) u8, [:0]align(8) const u8, + [:0]align(8) volatile u8, [:0]align(8) const volatile u8, + [:0]allowzero u8, [:0]allowzero const u8, + [:0]allowzero volatile u8, [:0]allowzero const volatile u8, + [:0]allowzero align(4) u8, [:0]allowzero align(4) const u8, + [:0]allowzero align(4) volatile u8, [:0]allowzero align(4) const volatile u8, + [:4]allowzero align(4) volatile u8, [:4]allowzero align(4) const volatile u8, + }); +} + +test "Type.Optional" { + try testTypes(&[_]type{ + ?u8, + ?*u8, + ?[]u8, + ?[*]u8, + ?[*c]u8, + }); +} + +test "Type.ErrorUnion" { + try testTypes(&[_]type{ + error{}!void, + error{Error}!void, + }); +} + +test "Type.Opaque" { + const Opaque = @Type(.{ + .Opaque = .{ + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + try testing.expect(Opaque != opaque {}); + try testing.expectEqualSlices( + TypeInfo.Declaration, + &[_]TypeInfo.Declaration{}, + @typeInfo(Opaque).Opaque.decls, + ); +} + +test "Type.Vector" { + try testTypes(&[_]type{ + @Vector(0, u8), + @Vector(4, u8), + @Vector(8, *u8), + std.meta.Vector(0, u8), + std.meta.Vector(4, u8), + std.meta.Vector(8, *u8), + }); +} + +test "Type.AnyFrame" { + try testTypes(&[_]type{ + anyframe, + anyframe->u8, + anyframe->anyframe->u8, + }); +} + +fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "Type.Frame" { + try testTypes(&[_]type{ + @Frame(add), + }); +} + +test "Type.ErrorSet" { + // error sets don't compare equal so just check if they compile + _ = @Type(@typeInfo(error{})); + _ = @Type(@typeInfo(error{A})); + _ = @Type(@typeInfo(error{ A, B, C })); +} + +test "Type.Struct" { + const A = @Type(@typeInfo(struct { x: u8, y: u32 })); + const infoA = @typeInfo(A).Struct; + try testing.expectEqual(TypeInfo.ContainerLayout.Auto, infoA.layout); + try testing.expectEqualSlices(u8, "x", infoA.fields[0].name); + try testing.expectEqual(u8, infoA.fields[0].field_type); + try testing.expectEqual(@as(?u8, null), infoA.fields[0].default_value); + try testing.expectEqualSlices(u8, "y", infoA.fields[1].name); + try testing.expectEqual(u32, infoA.fields[1].field_type); + try testing.expectEqual(@as(?u32, null), infoA.fields[1].default_value); + try testing.expectEqualSlices(TypeInfo.Declaration, &[_]TypeInfo.Declaration{}, infoA.decls); + try testing.expectEqual(@as(bool, false), infoA.is_tuple); + + var a = A{ .x = 0, .y = 1 }; + try testing.expectEqual(@as(u8, 0), a.x); + try testing.expectEqual(@as(u32, 1), a.y); + a.y += 1; + try testing.expectEqual(@as(u32, 2), a.y); + + const B = @Type(@typeInfo(extern struct { x: u8, y: u32 = 5 })); + const infoB = @typeInfo(B).Struct; + try testing.expectEqual(TypeInfo.ContainerLayout.Extern, infoB.layout); + try testing.expectEqualSlices(u8, "x", infoB.fields[0].name); + try testing.expectEqual(u8, infoB.fields[0].field_type); + try testing.expectEqual(@as(?u8, null), infoB.fields[0].default_value); + try testing.expectEqualSlices(u8, "y", infoB.fields[1].name); + try testing.expectEqual(u32, infoB.fields[1].field_type); + try testing.expectEqual(@as(?u32, 5), infoB.fields[1].default_value); + try testing.expectEqual(@as(usize, 0), infoB.decls.len); + try testing.expectEqual(@as(bool, false), infoB.is_tuple); + + const C = @Type(@typeInfo(packed struct { x: u8 = 3, y: u32 = 5 })); + const infoC = @typeInfo(C).Struct; + try testing.expectEqual(TypeInfo.ContainerLayout.Packed, infoC.layout); + try testing.expectEqualSlices(u8, "x", infoC.fields[0].name); + try testing.expectEqual(u8, infoC.fields[0].field_type); + try testing.expectEqual(@as(?u8, 3), infoC.fields[0].default_value); + try testing.expectEqualSlices(u8, "y", infoC.fields[1].name); + try testing.expectEqual(u32, infoC.fields[1].field_type); + try testing.expectEqual(@as(?u32, 5), infoC.fields[1].default_value); + try testing.expectEqual(@as(usize, 0), infoC.decls.len); + try testing.expectEqual(@as(bool, false), infoC.is_tuple); +} + +test "Type.Enum" { + const Foo = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u8, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "a", .value = 1 }, + .{ .name = "b", .value = 5 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = true, + }, + }); + try testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive); + try testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a)); + try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b)); + const Bar = @Type(.{ + .Enum = .{ + .layout = .Extern, + .tag_type = u32, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "a", .value = 1 }, + .{ .name = "b", .value = 5 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = false, + }, + }); + try testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive); + try testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a)); + try testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b)); + try testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6))); +} + +test "Type.Union" { + const Untagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = null, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) }, + .{ .name = "float", .field_type = f32, .alignment = @alignOf(f32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + var untagged = Untagged{ .int = 1 }; + untagged.float = 2.0; + untagged.int = 3; + try testing.expectEqual(@as(i32, 3), untagged.int); + + const PackedUntagged = @Type(.{ + .Union = .{ + .layout = .Packed, + .tag_type = null, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, + .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + var packed_untagged = PackedUntagged{ .signed = -1 }; + try testing.expectEqual(@as(i32, -1), packed_untagged.signed); + try testing.expectEqual(~@as(u32, 0), packed_untagged.unsigned); + + const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u1, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "signed", .value = 0 }, + .{ .name = "unsigned", .value = 1 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = true, + }, + }); + const Tagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = Tag, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, + .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + var tagged = Tagged{ .signed = -1 }; + try testing.expectEqual(Tag.signed, tagged); + tagged = .{ .unsigned = 1 }; + try testing.expectEqual(Tag.unsigned, tagged); +} + +test "Type.Union from Type.Enum" { + const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u0, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "working_as_expected", .value = 0 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = true, + }, + }); + const T = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = Tag, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + _ = T; + _ = @typeInfo(T).Union; +} + +test "Type.Union from regular enum" { + const E = enum { working_as_expected }; + const T = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = E, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + _ = T; + _ = @typeInfo(T).Union; +} + +test "Type.Fn" { + // wasm doesn't support align attributes on functions + if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; + + const foo = struct { + fn func(a: usize, b: bool) align(4) callconv(.C) usize { + _ = a; + _ = b; + return 0; + } + }.func; + const Foo = @Type(@typeInfo(@TypeOf(foo))); + const foo_2: Foo = foo; + _ = foo_2; +} + +test "Type.BoundFn" { + // wasm doesn't support align attributes on functions + if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; + + const TestStruct = packed struct { + pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void { + _ = self; + } + }; + const test_instance: TestStruct = undefined; + try testing.expect(std.meta.eql( + @typeName(@TypeOf(test_instance.foo)), + @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))), + )); +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 325586d35c..12190e418c 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -460,3 +460,28 @@ test "tagged union as return value" { fn returnAnInt(x: i32) TaggedFoo { return TaggedFoo{ .One = x }; } + +test "tagged union with all void fields but a meaningful tag" { + const S = struct { + const B = union(enum) { + c: C, + None, + }; + + const A = struct { + b: B, + }; + + const C = struct {}; + + fn doTheTest() !void { + var a: A = A{ .b = B{ .c = C{} } }; + try expect(@as(Tag(B), a.b) == Tag(B).c); + a = A{ .b = B.None }; + try expect(@as(Tag(B), a.b) == Tag(B).None); + } + }; + try S.doTheTest(); + // TODO enable the test at comptime too + //comptime try S.doTheTest(); +}