1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4const assert = std.debug.assert;
5
6test "while loop" {
7 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8
9 var i: i32 = 0;
10 while (i < 4) {
11 i += 1;
12 }
13 try expect(i == 4);
14 try expect(whileLoop1() == 1);
15}
16fn whileLoop1() i32 {
17 return whileLoop2();
18}
19fn whileLoop2() i32 {
20 while (true) {
21 return 1;
22 }
23}
24
25test "static eval while" {
26 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
27
28 try expect(static_eval_while_number == 1);
29}
30const static_eval_while_number = staticWhileLoop1();
31fn staticWhileLoop1() i32 {
32 return staticWhileLoop2();
33}
34fn staticWhileLoop2() i32 {
35 while (true) {
36 return 1;
37 }
38}
39
40test "while with continue expression" {
41 var sum: i32 = 0;
42 {
43 var i: i32 = 0;
44 while (i < 10) : (i += 1) {
45 if (i == 5) continue;
46 sum += i;
47 }
48 }
49 try expect(sum == 40);
50}
51
52test "while with else" {
53 var sum: i32 = 0;
54 var i: i32 = 0;
55 var got_else: i32 = 0;
56 while (i < 10) : (i += 1) {
57 sum += 1;
58 } else {
59 got_else += 1;
60 }
61 try expect(sum == 10);
62 try expect(got_else == 1);
63}
64
65var numbers_left: i32 = undefined;
66fn getNumberOrErr() anyerror!i32 {
67 return if (numbers_left == 0) error.OutOfNumbers else x: {
68 numbers_left -= 1;
69 break :x numbers_left;
70 };
71}
72fn getNumberOrNull() ?i32 {
73 return if (numbers_left == 0) null else x: {
74 numbers_left -= 1;
75 break :x numbers_left;
76 };
77}
78
79test "continue outer while loop" {
80 testContinueOuter();
81 comptime testContinueOuter();
82}
83
84fn testContinueOuter() void {
85 var i: usize = 0;
86 outer: while (i < 10) : (i += 1) {
87 while (true) {
88 continue :outer;
89 }
90 }
91}
92
93test "break from outer while loop" {
94 testBreakOuter();
95 comptime testBreakOuter();
96}
97
98fn testBreakOuter() void {
99 outer: while (true) {
100 while (true) {
101 break :outer;
102 }
103 }
104}
105
106test "while copies its payload" {
107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
108 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
109
110 const S = struct {
111 fn doTheTest() !void {
112 var tmp: ?i32 = 10;
113 while (tmp) |value| {
114 // Modify the original variable
115 tmp = null;
116 try expect(value == 10);
117 }
118 }
119 };
120 try S.doTheTest();
121 try comptime S.doTheTest();
122}
123
124test "continue and break" {
125 try runContinueAndBreakTest();
126 try expect(continue_and_break_counter == 8);
127}
128var continue_and_break_counter: i32 = 0;
129fn runContinueAndBreakTest() !void {
130 var i: i32 = 0;
131 while (true) {
132 continue_and_break_counter += 2;
133 i += 1;
134 if (i < 4) {
135 continue;
136 }
137 break;
138 }
139 try expect(i == 4);
140}
141
142test "while with optional as condition" {
143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
144 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
145 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
146
147 numbers_left = 10;
148 var sum: i32 = 0;
149 while (getNumberOrNull()) |value| {
150 sum += value;
151 }
152 try expect(sum == 45);
153}
154
155test "while with optional as condition with else" {
156 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
157 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
158 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
159
160 numbers_left = 10;
161 var sum: i32 = 0;
162 var got_else: i32 = 0;
163 while (getNumberOrNull()) |value| {
164 sum += value;
165 try expect(got_else == 0);
166 } else {
167 got_else += 1;
168 }
169 try expect(sum == 45);
170 try expect(got_else == 1);
171}
172
173test "while with error union condition" {
174 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
175 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
176
177 numbers_left = 10;
178 var sum: i32 = 0;
179 var got_else: i32 = 0;
180 while (getNumberOrErr()) |value| {
181 sum += value;
182 } else |err| {
183 try expect(err == error.OutOfNumbers);
184 got_else += 1;
185 }
186 try expect(sum == 45);
187 try expect(got_else == 1);
188}
189
190test "while on bool with else result follow else prong" {
191 const result = while (returnFalse()) {
192 break @as(i32, 10);
193 } else @as(i32, 2);
194 try expect(result == 2);
195}
196
197test "while on bool with else result follow break prong" {
198 const result = while (returnTrue()) {
199 break @as(i32, 10);
200 } else @as(i32, 2);
201 try expect(result == 10);
202}
203
204test "while on optional with else result follow else prong" {
205 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
206 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
207
208 const result = while (returnNull()) |value| {
209 break value;
210 } else @as(i32, 2);
211 try expect(result == 2);
212}
213
214test "while on optional with else result follow break prong" {
215 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
216 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
217
218 const result = while (returnOptional(10)) |value| {
219 break value;
220 } else @as(i32, 2);
221 try expect(result == 10);
222}
223
224fn returnNull() ?i32 {
225 return null;
226}
227fn returnOptional(x: i32) ?i32 {
228 return x;
229}
230fn returnError() anyerror!i32 {
231 return error.YouWantedAnError;
232}
233fn returnSuccess(x: i32) anyerror!i32 {
234 return x;
235}
236fn returnFalse() bool {
237 return false;
238}
239fn returnTrue() bool {
240 return true;
241}
242
243test "return with implicit cast from while loop" {
244 returnWithImplicitCastFromWhileLoopTest() catch unreachable;
245}
246fn returnWithImplicitCastFromWhileLoopTest() anyerror!void {
247 while (true) {
248 return;
249 }
250}
251
252test "while on error union with else result follow else prong" {
253 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
254
255 const result = while (returnError()) |value| {
256 break value;
257 } else |_| @as(i32, 2);
258 try expect(result == 2);
259}
260
261test "while on error union with else result follow break prong" {
262 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
263
264 const result = while (returnSuccess(10)) |value| {
265 break value;
266 } else |_| @as(i32, 2);
267 try expect(result == 10);
268}
269
270test "while bool 2 break statements and an else" {
271 const S = struct {
272 fn entry(t: bool, f: bool) !void {
273 var ok = false;
274 ok = while (t) {
275 if (f) break false;
276 if (t) break true;
277 } else false;
278 try expect(ok);
279 }
280 };
281 try S.entry(true, false);
282 try comptime S.entry(true, false);
283}
284
285test "while optional 2 break statements and an else" {
286 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
287 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
288
289 const S = struct {
290 fn entry(opt_t: ?bool, f: bool) !void {
291 var ok = false;
292 ok = while (opt_t) |t| {
293 if (f) break false;
294 if (t) break true;
295 } else false;
296 try expect(ok);
297 }
298 };
299 try S.entry(true, false);
300 try comptime S.entry(true, false);
301}
302
303test "while error 2 break statements and an else" {
304 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
305 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
306
307 const S = struct {
308 fn entry(opt_t: anyerror!bool, f: bool) !void {
309 var ok = false;
310 ok = while (opt_t) |t| {
311 if (f) break false;
312 if (t) break true;
313 } else |_| false;
314 try expect(ok);
315 }
316 };
317 try S.entry(true, false);
318 try comptime S.entry(true, false);
319}
320
321test "continue inline while loop" {
322 comptime var i = 0;
323 inline while (i < 10) : (i += 1) {
324 if (i < 5) continue;
325 break;
326 }
327 comptime assert(i == 5);
328}
329
330test "else continue outer while" {
331 var i: usize = 0;
332 while (true) {
333 i += 1;
334 while (i > 5) {
335 return;
336 } else continue;
337 }
338}
339
340test "try terminating an infinite loop" {
341 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
342 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
343
344 // Test coverage for https://github.com/ziglang/zig/issues/13546
345 const Foo = struct {
346 trash: i32,
347
348 fn bar() anyerror!@This() {
349 return .{ .trash = 1234 };
350 }
351 };
352 var t = true;
353 errdefer t = false;
354 try expect(while (true) {
355 if (t) break t;
356 _ = try Foo.bar();
357 } else unreachable);
358}
359
360test "while loop with comptime true condition needs no else block to return value with break" {
361 const x = while (true) {
362 break @as(u32, 69);
363 };
364 try expect(x == 69);
365}
366
367test "int returned from switch in while" {
368 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
369
370 var x: u32 = 3;
371 const val: usize = while (true) switch (x) {
372 1 => break 2,
373 else => x -= 1,
374 };
375 try std.testing.expect(val == 2);
376}
377
378test "breaking from a loop in an if statement" {
379 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
381
382 const S = struct {
383 fn retOpt() ?u32 {
384 return null;
385 }
386 };
387
388 var cond = true;
389 _ = &cond;
390 const opt = while (cond) {
391 if (S.retOpt()) |opt| {
392 break opt;
393 }
394 break 1;
395 } else 2;
396 _ = opt;
397}
398
399test "labeled break from else" {
400 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
401
402 const S = struct {
403 fn doTheTest(x: u32) !void {
404 const arr: []const u32 = &.{ 1, 3, 10 };
405 const ok = label: for (arr) |y| {
406 if (y == x) break :label false;
407 } else {
408 break :label true;
409 };
410 try expect(ok);
411 }
412 };
413
414 try S.doTheTest(5);
415 try comptime S.doTheTest(5);
416}