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