authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-08 16:18:18+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 11:28:36+00:00
loga3d2f2999f9708cbcc6f3082547b5f68a3b593cd
tree27455924e27051dab92e0c44f63e9b90c985fe3e
parentf92b998f9fe985d554d6078bb2bb11d094d94b91
signaturelock-open Commit is signed but in an unrecognized format.

tests: add new tests

Some for bugs which have been fixed, some for language changes.

20 files changed, 297 insertions(+), 51 deletions(-)

test/behavior/alignof.zig+5
......@@ -39,3 +39,8 @@ test "correct alignment for elements and slices of aligned array" {
3939 try expect(@alignOf(@TypeOf(&buf[start..end])) == @alignOf(*u8));
4040 try expect(@alignOf(@TypeOf(&buf[start])) == @alignOf(*u8));
4141}
42
43test "@alignOf(anyerror!noreturn)" {
44 try expect(@alignOf(anyerror!noreturn) == @alignOf(anyerror));
45 try expect(@alignOf(anyerror!anyerror!noreturn) == @alignOf(anyerror));
46}
test/behavior/enum.zig+15
......@@ -1354,3 +1354,18 @@ test "empty enum passed as argument" {
13541354 };
13551355 E.f(@as(E, undefined));
13561356}
1357
1358test "enum int tag type uses declaration inside the enum" {
1359 const static = struct {
1360 const E = enum(E.IntTag) {
1361 const IntTag = u8;
1362 a,
1363 b,
1364 c,
1365 };
1366 };
1367 try expect(@sizeOf(static.E) == @sizeOf(u8));
1368 const val: static.E = .b;
1369 try expect(val == .b);
1370 try expect(@intFromEnum(val) == 1);
1371}
test/behavior/error.zig+33
......@@ -1109,3 +1109,36 @@ test "'if' ignores error via local while 'else' ignores error directly" {
11091109 try S.testOne(false);
11101110 try S.testOne(true);
11111111}
1112
1113test "@errorCast into own inferred error set" {
1114 const static = struct {
1115 fn foo(b: bool) !void {
1116 if (b) {
1117 return @errorCast(error.Bad);
1118 }
1119 }
1120 };
1121 try static.foo(false);
1122 if (static.foo(true)) {
1123 return error.ExpectedError;
1124 } else |err| {
1125 try expect(err == error.Bad);
1126 }
1127
1128 const errors = @typeInfo(@typeInfo(@TypeOf(static.foo(false))).error_union.error_set).error_set.?;
1129 comptime assert(errors.len == 1);
1130 comptime assert(std.mem.eql(u8, errors[0].name, "Bad"));
1131}
1132
1133test "@errorCast into other inferred error set" {
1134 const static = struct {
1135 fn foo() !void {
1136 return error.Bad;
1137 }
1138 };
1139 const Ies = @typeInfo(@TypeOf(static.foo())).error_union.error_set;
1140 const err: Ies = @errorCast(error.Bad);
1141 try expect(err == error.Bad);
1142 const non_err: Ies!u32 = @errorCast(@as(error{}!u32, 123));
1143 try expect(try non_err == 123);
1144}
test/behavior/packed-union.zig+22
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const assert = std.debug.assert;
4const expect = std.testing.expect;
45const expectEqual = std.testing.expectEqual;
56
67test "flags in packed union" {
......@@ -177,3 +178,24 @@ test "assigning to non-active field at comptime" {
177178 test_bits.bits = .{};
178179 }
179180}
181
182test "packed union with explicit backing integer" {
183 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
184 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
185 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
186
187 const U = packed union(i32) {
188 raw: i32,
189 unsigned_halves: packed struct { low: u16, high: u16 },
190
191 fn check(val: @This()) !void {
192 try expect(@as(i32, @bitCast(val)) == -2);
193 try expect(@as(u32, @bitCast(val)) == 0xFFFFFFFE);
194 try expect(val.raw == -2);
195 try expect(val.unsigned_halves.low == 0xFFFE);
196 try expect(val.unsigned_halves.high == 0xFFFF);
197 }
198 };
199 try U.check(.{ .raw = -2 });
200 try comptime U.check(.{ .raw = -2 });
201}
test/behavior/struct.zig+51
......@@ -2254,3 +2254,54 @@ test "runtime-known slice of comptime-only struct" {
22542254 .{ .index = 15, .T = Mixed },
22552255 });
22562256}
2257
2258test "struct contains aligned pointer to itself through type decl" {
2259 const Slab = struct {
2260 const Ptr = *align(64) const @This();
2261 next: Ptr,
2262 };
2263 // We intentionally use `Slab.Ptr` before `Slab`.
2264 var ptr: Slab.Ptr = undefined;
2265 var slab: Slab align(64) = undefined;
2266 ptr = &slab;
2267 slab.next = ptr;
2268
2269 try expect(ptr == &slab);
2270 try expect(slab.next == &slab);
2271 try expect(slab.next.next == &slab);
2272 try expect(slab.next.next.next == &slab);
2273}
2274
2275test "struct contains underaligned field with overaligned pointer to itself" {
2276 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
2277 const S = struct {
2278 ptr: *align(8) @This() align(1),
2279 };
2280 var val: S align(8) = undefined;
2281 val.ptr = &val;
2282 try expect(val.ptr == &val);
2283 try expect(val.ptr.ptr == &val);
2284 try expect(val.ptr.ptr.ptr == &val);
2285}
2286
2287test "struct contains pointer to function accepting that struct" {
2288 const S = struct {
2289 const FnPtr = ?*const fn (@This()) void;
2290 fn_ptr: FnPtr,
2291 };
2292 const dummy_fn_ptr: S.FnPtr = @ptrFromInt(0x100000);
2293 const dummy_s: S = .{ .fn_ptr = dummy_fn_ptr };
2294 try expect(dummy_s.fn_ptr == dummy_fn_ptr);
2295 try expect(@TypeOf(dummy_s.fn_ptr.?) == *const fn (S) void);
2296}
2297
2298test "struct queries typeinfo of struct containing pointer back to first struct" {
2299 const static = struct {
2300 const A = struct { b: *B };
2301 const B = struct { a: T: {
2302 _ = @typeInfo(A);
2303 break :T u32;
2304 } };
2305 };
2306 _ = @as(static.A, undefined);
2307}
test/behavior/tuple.zig+11
......@@ -592,3 +592,14 @@ test "array of tuples that end with a zero-bit field followed by padding" {
592592 try expect(S.foo[1][1] == 4);
593593 try expect(S.foo[1][2] == {});
594594}
595
596test "call function at comptime through container-level const tuple" {
597 const static = struct {
598 const MyTuple = struct { (fn () u32) };
599 const val: MyTuple = .{foo};
600 fn foo() u32 {
601 return 1234;
602 }
603 };
604 comptime assert(static.val[0]() == 1234);
605}
test/cases/compile_errors/enum_uses_own_typeinfo.zig created+15
......@@ -0,0 +1,15 @@
1const E = enum(u9) {
2 const a_val: @typeInfo(E).@"enum".tag_type = 0;
3 a = a_val,
4};
5comptime {
6 _ = E.a;
7}
8
9// error
10//
11// error: dependency loop with length 3
12// :3:9: note: type 'tmp.E' uses value of declaration 'tmp.E.a_val' here
13// :2:50: note: value of declaration 'tmp.E.a_val' uses type of declaration 'tmp.E.a_val' here
14// :2:18: note: type of declaration 'tmp.E.a_val' depends on type 'tmp.E' for type information query here
15// note: eliminate any one of these dependencies to break the loop
test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig+2-2
......@@ -11,6 +11,6 @@ comptime {
1111//
1212// :3:5: error: unable to export type 'type'
1313// :7:5: error: unable to export type 'tmp.E'
14// :7:5: note: enum tag type 'u1' is not extern compatible
15// :7:5: note: only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible
14// :1:11: note: integer tag type of enum is inferred
15// :1:11: note: consider explicitly specifying the integer tag type
1616// :1:11: note: enum declared here
test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig deleted-45
......@@ -1,45 +0,0 @@
1// zig fmt: off
2pub const E = enum {
3@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",
4@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23",
5@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34",
6@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45",
7@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56",
8@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67",
9@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78",
10@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89",
11@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100",
12@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109",
13@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118",
14@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127",
15@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136",
16@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145",
17@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154",
18@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163",
19@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172",
20@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181",
21@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190",
22@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199",
23@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208",
24@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217",
25@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226",
26@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235",
27@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244",
28@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253",
29@"254",@"255", @"256"
30};
31// zig fmt: on
32pub const S = extern struct {
33 e: E,
34};
35export fn entry() void {
36 const s: S = undefined;
37 _ = s;
38}
39
40// error
41//
42// :33:8: error: extern structs cannot contain fields of type 'tmp.E'
43// :33:8: note: enum tag type 'u9' is not extern compatible
44// :33:8: note: only integers with 0 or power of two bits are extern compatible
45// :2:15: note: enum declared here
test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig+2-2
......@@ -10,6 +10,6 @@ export fn entry() void {
1010// error
1111//
1212// :3:8: error: extern structs cannot contain fields of type 'tmp.E'
13// :3:8: note: enum tag type 'u31' is not extern compatible
14// :3:8: note: only integers with 0 or power of two bits are extern compatible
13// :1:15: note: enum tag type 'u31' is not extern compatible
14// :1:15: note: only integers with 0 or power of two bits are extern compatible
1515// :1:15: note: enum declared here
test/cases/compile_errors/fn_type_returning_pointer_to_itself.zig created+8
......@@ -0,0 +1,8 @@
1const MyFn = fn () ?*const MyFn;
2comptime {
3 _ = MyFn;
4}
5
6// error
7//
8// :1:28: error: value of declaration 'tmp.MyFn' depends on itself here
test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig+2-2
......@@ -7,6 +7,6 @@ export fn entry(foo: Foo) void {
77// target=x86_64-linux
88//
99// :2:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'x86_64_sysv'
10// :2:17: note: enum tag type 'u2' is not extern compatible
11// :2:17: note: only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible
10// :1:13: note: integer tag type of enum is inferred
11// :1:13: note: consider explicitly specifying the integer tag type
1212// :1:13: note: enum declared here
test/cases/compile_errors/implicit_backing_type_in_extern_context.zig created+51
......@@ -0,0 +1,51 @@
1const PackedStruct = packed struct { x: u32 };
2const PackedUnion = packed union { x: u32 };
3
4/// This enum has 256 fields, so `u8` will be its inferred tag type.
5const Enum = enum {
6 // zig fmt: off
7 _00, _01, _02, _03, _04, _05, _06, _07, _08, _09, _0a, _0b, _0c, _0d, _0e, _0f,
8 _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _1a, _1b, _1c, _1d, _1e, _1f,
9 _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _2a, _2b, _2c, _2d, _2e, _2f,
10 _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _3a, _3b, _3c, _3d, _3e, _3f,
11 _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _4a, _4b, _4c, _4d, _4e, _4f,
12 _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _5a, _5b, _5c, _5d, _5e, _5f,
13 _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _6a, _6b, _6c, _6d, _6e, _6f,
14 _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _7a, _7b, _7c, _7d, _7e, _7f,
15 _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _8a, _8b, _8c, _8d, _8e, _8f,
16 _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _9a, _9b, _9c, _9d, _9e, _9f,
17 _a0, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8, _a9, _aa, _ab, _ac, _ad, _ae, _af,
18 _b0, _b1, _b2, _b3, _b4, _b5, _b6, _b7, _b8, _b9, _ba, _bb, _bc, _bd, _be, _bf,
19 _c0, _c1, _c2, _c3, _c4, _c5, _c6, _c7, _c8, _c9, _ca, _cb, _cc, _cd, _ce, _cf,
20 _d0, _d1, _d2, _d3, _d4, _d5, _d6, _d7, _d8, _d9, _da, _db, _dc, _dd, _de, _df,
21 _e0, _e1, _e2, _e3, _e4, _e5, _e6, _e7, _e8, _e9, _ea, _eb, _ec, _ed, _ee, _ef,
22 _f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _fa, _fb, _fc, _fd, _fe, _ff,
23 // zig fmt: on
24};
25
26const Extern0 = extern struct { val: PackedStruct };
27const Extern1 = extern struct { val: PackedUnion };
28const Extern2 = extern struct { val: Enum };
29
30comptime {
31 _ = @as(Extern0, undefined);
32}
33comptime {
34 _ = @as(Extern1, undefined);
35}
36comptime {
37 _ = @as(Extern2, undefined);
38}
39
40// error
41//
42// :26:38: error: extern structs cannot contain fields of type 'tmp.PackedStruct'
43// :26:38: note: inferred backing integer of packed struct has unspecified signedness
44// :1:29: note: struct declared here
45// :27:38: error: extern structs cannot contain fields of type 'tmp.PackedUnion'
46// :27:38: note: inferred backing integer of packed union has unspecified signedness
47// :2:28: note: union declared here
48// :28:38: error: extern structs cannot contain fields of type 'tmp.Enum'
49// :5:14: note: integer tag type of enum is inferred
50// :5:14: note: consider explicitly specifying the integer tag type
51// :5:14: note: enum declared here
test/cases/compile_errors/packed_struct_uses_own_size.zig created+10
......@@ -0,0 +1,10 @@
1const S = packed struct {
2 x: @Int(.unsigned, @sizeOf(S)),
3};
4comptime {
5 _ = @as(S, undefined);
6}
7
8// error
9//
10// :2:32: error: type 'tmp.S' depends on itself for size query here
test/cases/compile_errors/packed_struct_uses_own_typeinfo.zig created+13
......@@ -0,0 +1,13 @@
1const S = packed struct(u16) {
2 a: bool,
3 b: bool,
4 _padding: @Int(.unsigned, 17 - @typeInfo(S).Struct.fields.len) = 0,
5};
6
7comptime {
8 _ = @as(S, .{ .a = true, .b = true });
9}
10
11// error
12//
13// :4:36: error: type 'tmp.S' depends on itself for type information query here
test/cases/compile_errors/simple_struct_loop.zig created+16
......@@ -0,0 +1,16 @@
1const A = struct {
2 b: B,
3};
4const B = struct {
5 a: A,
6};
7comptime {
8 _ = @as(A, undefined);
9}
10
11// error
12//
13// error: dependency loop with length 2
14// :2:8: note: type 'tmp.A' depends on type 'tmp.B' for field declared here
15// :5:8: note: type 'tmp.B' depends on type 'tmp.A' for field declared here
16// note: eliminate any one of these dependencies to break the loop
test/cases/compile_errors/sizeOf_bad_type.zig+4
......@@ -15,6 +15,9 @@ const S4 = struct { a: u32, b: noreturn };
1515export fn entry4() usize {
1616 return @sizeOf(S4);
1717}
18export fn entry5() usize {
19 return @sizeOf([1]fn () void);
20}
1821
1922// error
2023//
......@@ -25,3 +28,4 @@ export fn entry4() usize {
2528// :10:12: note: struct declared here
2629// :16:20: error: no size available for uninstantiable type 'tmp.S4'
2730// :14:12: note: struct declared here
31// :19:20: error: no size available for comptime-only type '[1]fn () void'
test/cases/compile_errors/struct_field_queries_hasfield_of_itself.zig created+14
......@@ -0,0 +1,14 @@
1const Foo = packed struct {
2 bar: (T: {
3 _ = @hasField(Foo, "bar");
4 break :T void;
5 }),
6};
7
8comptime {
9 _ = @as(Foo, undefined);
10}
11
12// error
13//
14// :3:23: error: type 'tmp.Foo' depends on itself for field query here
test/cases/compile_errors/struct_uses_reified_type_which_queries_struct_alignment.zig created+13
......@@ -0,0 +1,13 @@
1const A = struct { b: *B };
2const B = @Struct(.auto, null, &.{"x"}, &.{A}, &.{.{ .@"align" = @alignOf(A) }});
3comptime {
4 _ = @as(A, undefined);
5 _ = @as(B, undefined);
6}
7
8// error
9//
10// error: dependency loop with length 2
11// :1:24: note: type 'tmp.A' uses value of declaration 'tmp.B' here
12// :2:75: note: value of declaration 'tmp.B' depends on type 'tmp.A' for alignment query here
13// note: eliminate any one of these dependencies to break the loop
test/cases/compile_errors/struct_uses_sizeof_self_as_array_len.zig created+10
......@@ -0,0 +1,10 @@
1const S = struct {
2 a: *[@sizeOf(S)]u8,
3};
4comptime {
5 _ = @as(S, undefined);
6}
7
8// error
9//
10// :2:18: error: type 'tmp.S' depends on itself for size query here