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}