master
1const builtin = @import("builtin");
2const std = @import("std");
3const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;
5const expectError = std.testing.expectError;
6
7test "break and continue inside loop inside defer expression" {
8 testBreakContInDefer(10);
9 comptime testBreakContInDefer(10);
10}
11
12fn testBreakContInDefer(x: usize) void {
13 defer {
14 var i: usize = 0;
15 while (i < x) : (i += 1) {
16 if (i < 5) continue;
17 if (i == 5) break;
18 }
19 expect(i == 5) catch @panic("test failure");
20 }
21}
22
23test "defer and labeled break" {
24 var i = @as(usize, 0);
25
26 blk: {
27 defer i += 1;
28 break :blk;
29 }
30
31 try expect(i == 1);
32}
33
34test "errdefer does not apply to fn inside fn" {
35 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
36
37 if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| try expect(e == error.Bad);
38}
39
40fn testNestedFnErrDefer() anyerror!void {
41 var a: i32 = 0;
42 errdefer a += 1;
43 const S = struct {
44 fn baz() anyerror {
45 return error.Bad;
46 }
47 };
48 return S.baz();
49}
50
51test "return variable while defer expression in scope to modify it" {
52 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
53 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
54
55 const S = struct {
56 fn doTheTest() !void {
57 try expect(notNull().? == 1);
58 }
59
60 fn notNull() ?u8 {
61 var res: ?u8 = 1;
62 defer res = null;
63 return res;
64 }
65 };
66
67 try S.doTheTest();
68 try comptime S.doTheTest();
69}
70
71var result: [3]u8 = undefined;
72var index: usize = undefined;
73
74fn runSomeErrorDefers(x: bool) !bool {
75 index = 0;
76 defer {
77 result[index] = 'a';
78 index += 1;
79 }
80 errdefer {
81 result[index] = 'b';
82 index += 1;
83 }
84 defer {
85 result[index] = 'c';
86 index += 1;
87 }
88 return if (x) x else error.FalseNotAllowed;
89}
90
91test "mixing normal and error defers" {
92 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
93 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
94
95 try expect(runSomeErrorDefers(true) catch unreachable);
96 try expect(result[0] == 'c');
97 try expect(result[1] == 'a');
98
99 const ok = runSomeErrorDefers(false) catch |err| x: {
100 try expect(err == error.FalseNotAllowed);
101 break :x true;
102 };
103 try expect(ok);
104 try expect(result[0] == 'c');
105 try expect(result[1] == 'b');
106 try expect(result[2] == 'a');
107}
108
109test "errdefer with payload" {
110 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
111 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
112 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
113 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
114
115 const S = struct {
116 fn foo() !i32 {
117 errdefer |a| {
118 expectEqual(error.One, a) catch @panic("test failure");
119 }
120 return error.One;
121 }
122 fn doTheTest() !void {
123 try expectError(error.One, foo());
124 }
125 };
126 try S.doTheTest();
127 try comptime S.doTheTest();
128}
129
130test "reference to errdefer payload" {
131 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
132 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
133 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
134 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
135
136 const S = struct {
137 fn foo() !i32 {
138 errdefer |a| {
139 const ptr = &a;
140 const ptr2 = &ptr;
141 expectEqual(error.One, ptr2.*.*) catch @panic("test failure");
142 expectEqual(error.One, ptr.*) catch @panic("test failure");
143 }
144 return error.One;
145 }
146 fn doTheTest() !void {
147 try expectError(error.One, foo());
148 }
149 };
150 try S.doTheTest();
151 try comptime S.doTheTest();
152}
153
154test "simple else prong doesn't emit an error for unreachable else prong" {
155 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
156 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
157
158 const S = struct {
159 fn foo() error{Foo}!void {
160 return error.Foo;
161 }
162 };
163 var a: u32 = 0;
164 defer a += 1;
165 S.foo() catch |err| switch (err) {
166 error.Foo => a += 1,
167 else => |e| return e,
168 };
169 try expect(a == 1);
170}
171
172test "errdefer used in function that doesn't return an error" {
173 const S = struct {
174 fn foo() u8 {
175 var a: u8 = 5;
176 errdefer a += 1;
177 return a;
178 }
179 };
180 try expect(S.foo() == 5);
181}
182
183// Originally reported at https://github.com/ziglang/zig/issues/10591
184const defer_assign = switch (block: {
185 var x = 0;
186 defer x = 1;
187 break :block x;
188}) {
189 else => |i| i,
190};
191comptime {
192 if (defer_assign != 0) @compileError("defer_assign failed!");
193}
194
195test "errdefer capture" {
196 const S = struct {
197 fail: bool = undefined,
198 fn bar0(self: *@This()) error{a}!void {
199 self.fail = false;
200 errdefer |err| if (@TypeOf(err) != error{a}) {
201 self.fail = true;
202 };
203 return error.a;
204 }
205 fn bar1(self: *@This()) error{a}!void {
206 self.fail = false;
207 errdefer |err| if (@TypeOf(err) != error{a}) {
208 self.fail = true;
209 };
210 const rv: error{a}!void = @errorCast(@as(error{a}!void, error.a));
211 return rv;
212 }
213 // https://github.com/ziglang/zig/issues/20371
214 fn bar2(self: *@This()) error{a}!void {
215 self.fail = false;
216 errdefer |err| if (@TypeOf(err) != error{a}) {
217 self.fail = true;
218 };
219 return @errorCast(@as(error{a}!void, error.a));
220 }
221 };
222
223 var s: S = .{};
224 s.bar0() catch {};
225 if (s.fail) return error.TestExpectedError;
226 s.bar1() catch {};
227 if (s.fail) return error.TestExpectedError;
228 s.bar2() catch {};
229 if (s.fail) return error.TestExpectedError;
230}
231
232test "errdefer in test block" {
233 errdefer |err| {
234 _ = &err;
235 }
236 var x: bool = false;
237 _ = &x;
238 if (x) return error.Something;
239}