1const builtin = @import("builtin");
2const std = @import("std");
3const assert = std.debug.assert;
4const expect = std.testing.expect;
5
6test "simple switch loop" {
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10
11 const S = struct {
12 fn doTheTest() !void {
13 var start: u32 = undefined;
14 start = 32;
15 const result: u32 = s: switch (start) {
16 0 => 0,
17 1 => 1,
18 2 => 2,
19 3 => 3,
20 else => |x| continue :s x / 2,
21 };
22 try expect(result == 2);
23 }
24 };
25 try S.doTheTest();
26 try comptime S.doTheTest();
27}
28
29test "switch loop with ranges" {
30 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
31 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
32 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
33
34 const S = struct {
35 fn doTheTest() !void {
36 var start: u32 = undefined;
37 start = 32;
38 const result = s: switch (start) {
39 0...3 => |x| x,
40 else => |x| continue :s x / 2,
41 };
42 try expect(result == 2);
43 }
44 };
45 try S.doTheTest();
46 try comptime S.doTheTest();
47}
48
49test "switch loop on enum" {
50 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
51 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
52 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
53
54 const S = struct {
55 const E = enum { a, b, c };
56
57 fn doTheTest() !void {
58 var start: E = undefined;
59 start = .a;
60 const result: u32 = s: switch (start) {
61 .a => continue :s .b,
62 .b => continue :s .c,
63 .c => 123,
64 };
65 try expect(result == 123);
66 }
67 };
68 try S.doTheTest();
69 try comptime S.doTheTest();
70}
71
72test "switch loop with error set" {
73 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
74 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
75 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
76
77 const S = struct {
78 const E = error{ Foo, Bar, Baz };
79
80 fn doTheTest() !void {
81 var start: E = undefined;
82 start = error.Foo;
83 const result: u32 = s: switch (start) {
84 error.Foo => continue :s error.Bar,
85 error.Bar => continue :s error.Baz,
86 error.Baz => 123,
87 };
88 try expect(result == 123);
89 }
90 };
91 try S.doTheTest();
92 try comptime S.doTheTest();
93}
94
95test "switch loop on tagged union" {
96 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
97 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
100 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
101
102 const S = struct {
103 const U = union(enum) {
104 a: u32,
105 b: f32,
106 c: f32,
107 };
108
109 fn doTheTest() !void {
110 var start: U = undefined;
111 start = .{ .a = 80 };
112 const result = s: switch (start) {
113 .a => |x| switch (x) {
114 0...49 => continue :s .{ .b = @floatFromInt(x) },
115 50 => continue :s .{ .c = @floatFromInt(x) },
116 else => continue :s .{ .a = x / 2 },
117 },
118 .b => |x| x,
119 .c => return error.TestFailed,
120 };
121 try expect(result == 40.0);
122 }
123 };
124 try S.doTheTest();
125 try comptime S.doTheTest();
126}
127
128test "switch loop dispatching instructions" {
129 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
130 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
131 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
132 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
133
134 const S = struct {
135 const Inst = union(enum) {
136 set: u32,
137 add: u32,
138 sub: u32,
139 end,
140 };
141
142 fn doTheTest() !void {
143 var insts: [5]Inst = undefined;
144 @memcpy(&insts, &[5]Inst{
145 .{ .set = 123 },
146 .{ .add = 100 },
147 .{ .sub = 50 },
148 .{ .sub = 10 },
149 .end,
150 });
151 var i: u32 = 0;
152 var cur: u32 = undefined;
153 eval: switch (insts[0]) {
154 .set => |x| {
155 cur = x;
156 i += 1;
157 continue :eval insts[i];
158 },
159 .add => |x| {
160 cur += x;
161 i += 1;
162 continue :eval insts[i];
163 },
164 .sub => |x| {
165 cur -= x;
166 i += 1;
167 continue :eval insts[i];
168 },
169 .end => {},
170 }
171 try expect(cur == 163);
172 }
173 };
174 try S.doTheTest();
175 try comptime S.doTheTest();
176}
177
178test "switch loop with pointer capture" {
179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
180 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
181 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
182
183 const S = struct {
184 const U = union(enum) {
185 a: u32,
186 b: u32,
187 c: u32,
188 };
189
190 fn doTheTest() !void {
191 var a: U = .{ .a = 100 };
192 var b: U = .{ .b = 200 };
193 var c: U = .{ .c = 300 };
194 inc: switch (a) {
195 .a => |*x| {
196 x.* += 1;
197 continue :inc b;
198 },
199 .b => |*x| {
200 x.* += 10;
201 continue :inc c;
202 },
203 .c => |*x| {
204 x.* += 50;
205 },
206 }
207 try expect(a.a == 101);
208 try expect(b.b == 210);
209 try expect(c.c == 350);
210 }
211 };
212 try S.doTheTest();
213 try comptime S.doTheTest();
214}
215
216test "unanalyzed continue with operand" {
217 @setRuntimeSafety(false);
218 label: switch (false) {
219 false => if (false) continue :label true,
220 true => {},
221 }
222}
223
224test "switch loop on larger than pointer integer" {
225 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
226 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
227
228 var entry: @Int(.unsigned, @bitSizeOf(usize) + 1) = undefined;
229 entry = 0;
230 loop: switch (entry) {
231 0 => {
232 entry += 1;
233 continue :loop 1;
234 },
235 1 => |x| {
236 entry += 1;
237 continue :loop x + 1;
238 },
239 2 => entry += 1,
240 else => unreachable,
241 }
242 try expect(entry == 3);
243}
244
245test "switch loop on non-exhaustive enum" {
246 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
247 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
248 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
249
250 const S = struct {
251 const E = enum(u8) { a, b, c, _ };
252
253 fn doTheTest() !void {
254 var start: E = undefined;
255 start = .a;
256 const result: u32 = s: switch (start) {
257 .a => continue :s .c,
258 else => continue :s @fromBackingInt(@intCast(123)),
259 .b, _ => |x| break :s @backingInt(x),
260 };
261 try expect(result == 123);
262 }
263 };
264 try S.doTheTest();
265 try comptime S.doTheTest();
266}
267
268test "switch loop with discarded tag capture" {
269 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
270
271 const S = struct {
272 const U = union(enum) {
273 a: u32,
274 b: u32,
275 c: u32,
276 };
277
278 fn doTheTest() void {
279 const a: U = .{ .a = 10 };
280 blk: switch (a) {
281 inline .b => |_, tag| {
282 _ = tag;
283 continue :blk .{ .c = 20 };
284 },
285 else => {},
286 }
287 }
288 };
289 S.doTheTest();
290 comptime S.doTheTest();
291}
292
293test "switch loop with single catch-all prong" {
294 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
295 const S = struct {
296 const E = enum { a, b, c };
297 const U = union(E) { a: u32, b: u16, c: u8 };
298
299 fn doTheTest() !void {
300 var x: usize = 0;
301 label: switch (E.a) {
302 else => {
303 x += 1;
304 if (x == 10) break :label;
305 if (x >= 5) continue :label .b;
306 continue :label .c;
307 },
308 }
309 try expect(x == 10);
310
311 label: switch (E.a) {
312 .a, .b, .c => {
313 x += 1;
314 if (x == 20) break :label;
315 if (x >= 15) continue :label .b;
316 continue :label .c;
317 },
318 }
319 try expect(x == 20);
320
321 label: switch (E.a) {
322 else => if (false) continue :label true,
323 }
324
325 const ok = label: switch (U{ .a = 123 }) {
326 else => |u| {
327 const y: u32 = switch (u) {
328 inline else => |y| y,
329 };
330 if (y == 456) break :label true;
331 continue :label .{ .b = 456 };
332 },
333 };
334 comptime assert(ok);
335 }
336 };
337 try S.doTheTest();
338 try comptime S.doTheTest();
339}
340
341test "switch loop on type with opv" {
342 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
343
344 const S = struct {
345 const E = enum { opv };
346 const U = union(E) { opv: u0 };
347
348 fn doTheTest() !void {
349 var x: usize = 0;
350 label: switch (E.opv) {
351 .opv => {
352 x += 1;
353 if (x == 10) break :label;
354 if (x >= 5) continue :label .opv;
355 continue :label .opv;
356 },
357 }
358 try expect(x == 10);
359
360 label: switch (E.opv) {
361 else => {
362 x += 1;
363 if (x == 20) break :label;
364 if (x >= 15) continue :label .opv;
365 continue :label .opv;
366 },
367 }
368 try expect(x == 20);
369
370 label: switch (E.opv) {
371 .opv => if (false) continue :label true,
372 }
373
374 label: switch (U{ .opv = 0 }) {
375 .opv => |val| {
376 x += 1;
377 if (x == 30) break :label;
378 if (x >= 25) continue :label .{ .opv = val };
379 continue :label .{ .opv = 0 };
380 },
381 }
382 try expect(x == 30);
383 }
384 };
385 try S.doTheTest();
386 try comptime S.doTheTest();
387}
388
389test "switch loop with tag capture" {
390 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
391
392 const U = union(enum) {
393 a,
394 b: i32,
395 c: u8,
396 d: i32,
397 e: noreturn,
398
399 fn doTheTest() !void {
400 try doTheSwitch(.a);
401 try doTheSwitch(.{ .b = 123 });
402 try doTheSwitch(.{ .c = 0xFF });
403 }
404 fn doTheSwitch(u: @This()) !void {
405 const ok1 = label: switch (u) {
406 .a => |nothing, tag| {
407 comptime assert(nothing == {});
408 comptime assert(tag == .a);
409 try expect(@backingInt(tag) == @backingInt(@This().a));
410 continue :label .{ .d = 456 };
411 },
412 .b, .d => |_, tag| {
413 try expect(tag == .b or tag == .d);
414 continue :label .{ .c = 0x0F };
415 },
416 .e => |payload, tag| {
417 _ = &payload;
418 _ = &tag;
419 return error.AnalyzedNoreturnProng;
420 },
421 else => |un, tag| {
422 try expect(tag == .c);
423 try expect(un == .c);
424 if (un.c == 0xFF) continue :label .a;
425 if (un.c == 0x00) break :label false;
426 break :label true;
427 },
428 };
429 try expect(ok1);
430
431 const ok2 = label: switch (u) {
432 inline .a, .b, .c => |payload, tag| {
433 if (@TypeOf(payload) == void) {
434 comptime assert(tag == .a);
435 continue :label .{ .b = 456 };
436 }
437 if (@TypeOf(payload) == i32) {
438 comptime assert(tag == .b);
439 continue :label .{ .d = payload };
440 }
441 if (@TypeOf(payload) == u8) {
442 comptime assert(tag == .c);
443 continue :label .{ .d = payload };
444 }
445 },
446 inline else => |payload, tag| {
447 if (@TypeOf(payload) == i32) comptime assert(tag == .d);
448 comptime assert(tag != .e);
449 if (payload == 0) break :label false;
450 break :label true;
451 },
452 };
453 try expect(ok2);
454 }
455 };
456
457 try U.doTheTest();
458 try comptime U.doTheTest();
459}
460
461test "switch loop for error handling" {
462 const Error = error{ MyError, MyOtherError };
463 const S = struct {
464 fn doTheTest() !void {
465 try doThePayloadSwitch(123);
466 try doTheErrSwitch(error.MyError);
467 try doTheErrSwitch(error.MyOtherError);
468 }
469 fn doThePayloadSwitch(eu: Error!u32) !void {
470 const x = eu catch |err| label: switch (err) {
471 error.MyError => continue :label error.MyOtherError,
472 error.MyOtherError => break :label 0,
473 };
474 try expect(x == 123);
475
476 const y = if (eu) |payload| label: {
477 break :label payload * 2;
478 } else |err| label: switch (err) {
479 error.MyError => continue :label error.MyOtherError,
480 error.MyOtherError => break :label 0,
481 };
482 try expect(y == 246);
483 }
484 fn doTheErrSwitch(eu: Error!u32) !void {
485 const x = eu catch |err| label: switch (err) {
486 error.MyError => continue :label error.MyOtherError,
487 error.MyOtherError => break :label 123,
488 };
489 try expect(x == 123);
490
491 const y = if (eu) |payload| label: {
492 break :label payload * 2;
493 } else |err| label: switch (err) {
494 error.MyError => continue :label error.MyOtherError,
495 error.MyOtherError => break :label 123,
496 };
497 try expect(y == 123);
498 }
499 };
500
501 try S.doTheTest();
502 try comptime S.doTheTest();
503}
504
505test "switch loop with packed structs" {
506 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
507 const P = packed struct {
508 a: u7,
509 b: u20,
510
511 fn doTheTest(p: @This()) !void {
512 const result = s: switch (p) {
513 .{ .a = 5, .b = 10 } => |x| x,
514 else => |x| continue :s .{ .a = x.a, .b = x.b + 1 },
515 };
516 try expect(result == @This(){ .a = 5, .b = 10 });
517 }
518 };
519 try P.doTheTest(.{ .a = 5, .b = 0 });
520 try comptime P.doTheTest(.{ .a = 5, .b = 0 });
521}
522
523test "switch loop with packed unions" {
524 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
525
526 const P = packed union {
527 a: u7,
528 b: i7,
529
530 fn doTheTest(p: @This()) !void {
531 const result = s: switch (p) {
532 .{ .a = 10 } => |x| x,
533 else => |x| continue :s .{ .b = @intCast(x.a + 1) },
534 };
535 try expect(result == @This(){ .b = 10 });
536 }
537 };
538 try P.doTheTest(.{ .a = 5 });
539 try comptime P.doTheTest(.{ .a = 5 });
540}
541
542test "switch loop with packed unions with OPV" {
543 const P = packed union {
544 a: u0,
545 b: void,
546
547 fn doTheTest(p: @This()) !void {
548 var looped = false;
549 s: switch (p) {
550 .{ .b = {} } => |x| {
551 comptime assert(x.a == 0);
552 if (looped) break :s;
553 looped = true;
554 continue :s .{ .a = 0 };
555 },
556 }
557 }
558 };
559 try P.doTheTest(.{ .a = 0 });
560 try comptime P.doTheTest(.{ .a = 0 });
561}
562
563test "switch loop on large types" {
564 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
565
566 const S = struct {
567 fn doTheTest(a: u128, b: i500) !void {
568 label: switch (a) {
569 0x0,
570 0x3...0xFFFF_FFFF_FFFF_FFFF_FFFF_ABCD,
571 0xFFFF_FFFF_FFFF_FFFF_FFFF_EF00,
572 => return error.TestFailed,
573 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_0000...0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFF0,
574 => |val| {
575 continue :label val + 1;
576 },
577 else => {},
578 }
579 label: switch (b) {
580 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_0000...0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_1234,
581 => return error.TestFailed,
582 0xFFFF_1234,
583 0xFFFF_FFFF_FFFF_FFFF_FFFF_0123...0xFFFF_FFFF_FFFF_FFFF_FFFF_4567,
584 => |val| {
585 continue :label val + 1;
586 },
587 else => {},
588 }
589 }
590 };
591 try S.doTheTest(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FF00, 0xFFFF_FFFF_FFFF_FFFF_FFFF_4550);
592 try comptime S.doTheTest(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FF00, 0xFFFF_FFFF_FFFF_FFFF_FFFF_4550);
593}