1const builtin = @import("builtin");
2const std = @import("std");
3const assert = std.debug.assert;
4const expect = std.testing.expect;
5const expectError = std.testing.expectError;
6const expectEqual = std.testing.expectEqual;
7const minInt = std.math.minInt;
8const maxInt = std.math.maxInt;
9
10test "switch with numbers" {
11 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12
13 try testSwitchWithNumbers(13);
14}
15
16fn testSwitchWithNumbers(x: u32) !void {
17 const result = switch (x) {
18 1, 2, 3, 4...8 => false,
19 13 => true,
20 else => false,
21 };
22 try expect(result);
23}
24
25test "switch with all ranges" {
26 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
27
28 try expect(testSwitchWithAllRanges(50, 3) == 1);
29 try expect(testSwitchWithAllRanges(101, 0) == 2);
30 try expect(testSwitchWithAllRanges(300, 5) == 3);
31 try expect(testSwitchWithAllRanges(301, 6) == 6);
32}
33
34fn testSwitchWithAllRanges(x: u32, y: u32) u32 {
35 return switch (x) {
36 0...100 => 1,
37 101...200 => 2,
38 201...300 => 3,
39 else => y,
40 };
41}
42
43test "switch arbitrary int size" {
44 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
46 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
47 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
48 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
49
50 try expect(testSwitchArbInt(u64, 0) == 0);
51 try expect(testSwitchArbInt(u64, 12) == 1);
52 try expect(testSwitchArbInt(u64, maxInt(u64)) == 2);
53 try expect(testSwitchArbInt(u64, 5555) == 3);
54
55 try expect(testSwitchArbInt(i64, minInt(i64)) == 0);
56 try expect(testSwitchArbInt(i64, 12) == 1);
57 try expect(testSwitchArbInt(i64, maxInt(i64)) == 2);
58 try expect(testSwitchArbInt(i64, -1000) == 3);
59
60 try expect(testSwitchArbInt(u128, 0) == 0);
61 try expect(testSwitchArbInt(u128, 12) == 1);
62 try expect(testSwitchArbInt(u128, maxInt(u128)) == 2);
63 try expect(testSwitchArbInt(u128, 5555) == 3);
64
65 try expect(testSwitchArbInt(i128, minInt(i128)) == 0);
66 try expect(testSwitchArbInt(i128, 12) == 1);
67 try expect(testSwitchArbInt(i128, maxInt(i128)) == 2);
68 try expect(testSwitchArbInt(i128, -1000) == 3);
69}
70
71fn testSwitchArbInt(comptime T: type, x: T) u32 {
72 return switch (x) {
73 minInt(T) => 0,
74 10...15 => 1,
75 maxInt(T) => 2,
76 else => 3,
77 };
78}
79
80test "implicit comptime switch" {
81 const x = 3 + 4;
82 const result = switch (x) {
83 3 => 10,
84 4 => 11,
85 5, 6 => 12,
86 7, 8 => 13,
87 else => 14,
88 };
89
90 comptime {
91 try expect(result + 1 == 14);
92 }
93}
94
95test "switch on enum" {
96 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
97
98 const fruit = Fruit.Orange;
99 try expect(nonConstSwitchOnEnum(fruit));
100}
101const Fruit = enum {
102 Apple,
103 Orange,
104 Banana,
105};
106fn nonConstSwitchOnEnum(fruit: Fruit) bool {
107 return switch (fruit) {
108 Fruit.Apple => false,
109 Fruit.Orange => true,
110 Fruit.Banana => false,
111 };
112}
113
114test "switch statement" {
115 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
116
117 try nonConstSwitch(SwitchStatementFoo.C);
118}
119fn nonConstSwitch(foo: SwitchStatementFoo) !void {
120 const val = switch (foo) {
121 SwitchStatementFoo.A => @as(i32, 1),
122 SwitchStatementFoo.B => 2,
123 SwitchStatementFoo.C => 3,
124 SwitchStatementFoo.D => 4,
125 };
126 try expect(val == 3);
127}
128const SwitchStatementFoo = enum { A, B, C, D };
129
130test "switch with multiple expressions" {
131 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
132
133 const x = switch (returnsFive()) {
134 1, 2, 3 => 1,
135 4, 5, 6 => 2,
136 else => @as(i32, 3),
137 };
138 try expect(x == 2);
139}
140fn returnsFive() i32 {
141 return 5;
142}
143
144test "switch on type" {
145 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
146
147 try expect(trueIfBoolFalseOtherwise(bool));
148 try expect(!trueIfBoolFalseOtherwise(i32));
149}
150
151fn trueIfBoolFalseOtherwise(comptime T: type) bool {
152 return switch (T) {
153 bool => true,
154 else => false,
155 };
156}
157
158test "switching on booleans" {
159 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
160
161 try testSwitchOnBools();
162 try comptime testSwitchOnBools();
163}
164
165fn testSwitchOnBools() !void {
166 try expect(testSwitchOnBoolsTrueAndFalse(true) == false);
167 try expect(testSwitchOnBoolsTrueAndFalse(false) == true);
168
169 try expect(testSwitchOnBoolsTrueWithElse(true) == false);
170 try expect(testSwitchOnBoolsTrueWithElse(false) == true);
171
172 try expect(testSwitchOnBoolsFalseWithElse(true) == false);
173 try expect(testSwitchOnBoolsFalseWithElse(false) == true);
174}
175
176fn testSwitchOnBoolsTrueAndFalse(x: bool) bool {
177 return switch (x) {
178 true => false,
179 false => true,
180 };
181}
182
183fn testSwitchOnBoolsTrueWithElse(x: bool) bool {
184 return switch (x) {
185 true => false,
186 else => true,
187 };
188}
189
190fn testSwitchOnBoolsFalseWithElse(x: bool) bool {
191 return switch (x) {
192 false => true,
193 else => false,
194 };
195}
196
197test "u0" {
198 var val: u0 = 0;
199 _ = &val;
200 switch (val) {
201 0 => try expect(val == 0),
202 }
203}
204
205test "undefined.u0" {
206 var val: u0 = undefined;
207 _ = &val;
208 switch (val) {
209 0 => try expect(val == 0),
210 }
211}
212
213test "switch with disjoint range" {
214 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
215 var q: u8 = 0;
216 _ = &q;
217 switch (q) {
218 0...125 => {},
219 127...255 => {},
220 126...126 => {},
221 }
222}
223
224test "switch variable for range and multiple prongs" {
225 const S = struct {
226 fn doTheTest() !void {
227 try doTheSwitch(16);
228 try doTheSwitch(42);
229 }
230 fn doTheSwitch(q: u8) !void {
231 switch (q) {
232 0...40 => |x| try expect(x == 16),
233 41, 42, 43 => |x| try expect(x == 42),
234 else => try expect(false),
235 }
236 }
237 };
238 try S.doTheTest();
239 try comptime S.doTheTest();
240}
241
242var state: u32 = 0;
243fn poll() void {
244 switch (state) {
245 0 => {
246 state = 1;
247 },
248 else => {
249 state += 1;
250 },
251 }
252}
253
254test "switch on global mutable var isn't constant-folded" {
255 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
256
257 while (state < 2) {
258 poll();
259 }
260}
261
262const SwitchProngWithVarEnum = union(enum) {
263 One: i32,
264 Two: f32,
265 Meh: void,
266};
267
268test "switch prong with variable" {
269 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
270 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
271 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
272 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
273 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
274
275 try switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 });
276 try switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 });
277 try switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} });
278}
279fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void {
280 switch (a) {
281 SwitchProngWithVarEnum.One => |x| {
282 try expect(x == 13);
283 },
284 SwitchProngWithVarEnum.Two => |x| {
285 try expect(x == 13.0);
286 },
287 SwitchProngWithVarEnum.Meh => |x| {
288 const v: void = x;
289 _ = v;
290 },
291 }
292}
293
294test "switch on enum using pointer capture" {
295 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
296 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
297 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
298
299 try testSwitchEnumPtrCapture();
300 try comptime testSwitchEnumPtrCapture();
301}
302
303fn testSwitchEnumPtrCapture() !void {
304 var value = SwitchProngWithVarEnum{ .One = 1234 };
305 switch (value) {
306 SwitchProngWithVarEnum.One => |*x| x.* += 1,
307 else => unreachable,
308 }
309 switch (value) {
310 SwitchProngWithVarEnum.One => |x| try expect(x == 1235),
311 else => unreachable,
312 }
313}
314
315test "switch handles all cases of number" {
316 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
317
318 try testSwitchHandleAllCases();
319 try comptime testSwitchHandleAllCases();
320}
321
322fn testSwitchHandleAllCases() !void {
323 try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
324 try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
325 try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
326 try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
327
328 try expect(testSwitchHandleAllCasesRange(100) == 0);
329 try expect(testSwitchHandleAllCasesRange(200) == 1);
330 try expect(testSwitchHandleAllCasesRange(201) == 2);
331 try expect(testSwitchHandleAllCasesRange(202) == 4);
332 try expect(testSwitchHandleAllCasesRange(230) == 3);
333}
334
335fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
336 return switch (x) {
337 0 => @as(u2, 3),
338 1 => 2,
339 2 => 1,
340 3 => 0,
341 };
342}
343
344fn testSwitchHandleAllCasesRange(x: u8) u8 {
345 return switch (x) {
346 0...100 => @as(u8, 0),
347 101...200 => 1,
348 201, 203 => 2,
349 202 => 4,
350 204...255 => 3,
351 };
352}
353
354test "switch on union with some prongs capturing" {
355 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
356 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
357
358 const X = union(enum) {
359 a,
360 b: i32,
361 };
362
363 var x: X = X{ .b = 10 };
364 _ = &x;
365 const y: i32 = switch (x) {
366 .a => unreachable,
367 .b => |b| b + 1,
368 };
369 try expect(y == 11);
370}
371
372const Number = union(enum) {
373 One: u64,
374 Two: u8,
375 Three: f32,
376};
377
378const number = Number{ .Three = 1.23 };
379
380fn returnsFalse() bool {
381 switch (number) {
382 Number.One => |x| return x > 1234,
383 Number.Two => |x| return x == 'a',
384 Number.Three => |x| return x > 12.34,
385 }
386}
387test "switch on const enum with var" {
388 try expect(!returnsFalse());
389}
390
391test "anon enum literal used in switch on union enum" {
392 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
393
394 const Foo = union(enum) {
395 a: i32,
396 };
397
398 var foo = Foo{ .a = 1234 };
399 _ = &foo;
400 switch (foo) {
401 .a => |x| {
402 try expect(x == 1234);
403 },
404 }
405}
406
407test "switch all prongs unreachable" {
408 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
409
410 try testAllProngsUnreachable();
411 try comptime testAllProngsUnreachable();
412}
413
414fn testAllProngsUnreachable() !void {
415 try expect(switchWithUnreachable(1) == 2);
416 try expect(switchWithUnreachable(2) == 10);
417}
418
419fn switchWithUnreachable(x: i32) i32 {
420 while (true) {
421 switch (x) {
422 1 => return 2,
423 2 => break,
424 else => continue,
425 }
426 }
427 return 10;
428}
429
430test "capture value of switch with all unreachable prongs" {
431 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
432
433 const x = return_a_number() catch |err| switch (err) {
434 else => unreachable,
435 };
436 try expect(x == 1);
437}
438
439fn return_a_number() anyerror!i32 {
440 return 1;
441}
442
443test "switch on integer with else capturing expr" {
444 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
445
446 const S = struct {
447 fn doTheTest() !void {
448 var x: i32 = 5;
449 _ = &x;
450 switch (x + 10) {
451 14 => return error.TestFailed,
452 16 => return error.TestFailed,
453 else => |e| try expect(e == 15),
454 }
455 }
456 };
457 try S.doTheTest();
458 try comptime S.doTheTest();
459}
460
461test "else prong of switch on error set excludes other cases" {
462 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
463 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
464 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
465 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
466
467 const S = struct {
468 fn doTheTest() !void {
469 try expectError(error.C, bar());
470 }
471 const E = error{
472 A,
473 B,
474 } || E2;
475
476 const E2 = error{
477 C,
478 D,
479 };
480
481 fn foo() E!void {
482 return error.C;
483 }
484
485 fn bar() E2!void {
486 foo() catch |err| switch (err) {
487 error.A, error.B => {},
488 else => |e| return e,
489 };
490 }
491 };
492 try S.doTheTest();
493 try comptime S.doTheTest();
494}
495
496test "switch prongs with error set cases make a new error set type for capture value" {
497 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
498 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
499 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
500 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
501
502 const S = struct {
503 fn doTheTest() !void {
504 try expectError(error.B, bar());
505 }
506 const E = E1 || E2;
507
508 const E1 = error{
509 A,
510 B,
511 };
512
513 const E2 = error{
514 C,
515 D,
516 };
517
518 fn foo() E!void {
519 return error.B;
520 }
521
522 fn bar() E1!void {
523 foo() catch |err| switch (err) {
524 error.A, error.B => |e| return e,
525 else => {},
526 };
527 }
528 };
529 try S.doTheTest();
530 try comptime S.doTheTest();
531}
532
533test "return result loc and then switch with range implicit casted to error union" {
534 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
535
536 const S = struct {
537 fn doTheTest() !void {
538 try expect((func(0xb) catch unreachable) == 0xb);
539 }
540 fn func(d: u8) anyerror!u8 {
541 return switch (d) {
542 0xa...0xf => d,
543 else => unreachable,
544 };
545 }
546 };
547 try S.doTheTest();
548 try comptime S.doTheTest();
549}
550
551test "switch with null and T peer types and inferred result location type" {
552 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
553 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
554
555 const S = struct {
556 fn doTheTest(c: u8) !void {
557 if (switch (c) {
558 0 => true,
559 else => null,
560 }) |v| {
561 _ = v;
562 return error.TestFailed;
563 }
564 }
565 };
566 try S.doTheTest(1);
567 try comptime S.doTheTest(1);
568}
569
570test "switch prongs with cases with identical payload types" {
571 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
572 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
573 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
574
575 const Union = union(enum) {
576 A: usize,
577 B: isize,
578 C: usize,
579 };
580 const S = struct {
581 fn doTheTest() !void {
582 try doTheSwitch1(Union{ .A = 8 });
583 try doTheSwitch2(Union{ .B = -8 });
584 }
585 fn doTheSwitch1(u: Union) !void {
586 switch (u) {
587 .A, .C => |e| {
588 comptime assert(@TypeOf(e) == usize);
589 try expect(e == 8);
590 },
591 .B => |e| {
592 _ = e;
593 return error.TestFailed;
594 },
595 }
596 }
597 fn doTheSwitch2(u: Union) !void {
598 switch (u) {
599 .A, .C => |e| {
600 _ = e;
601 return error.TestFailed;
602 },
603 .B => |e| {
604 comptime assert(@TypeOf(e) == isize);
605 try expect(e == -8);
606 },
607 }
608 }
609 };
610 try S.doTheTest();
611 try comptime S.doTheTest();
612}
613
614test "switch prong pointer capture alignment" {
615 const U = union(enum) {
616 a: u8 align(8),
617 b: u8 align(4),
618 c: u8,
619 };
620
621 const S = struct {
622 fn doTheTest() !void {
623 const u = U{ .a = 1 };
624 switch (u) {
625 .a => |*a| comptime assert(@TypeOf(a) == *align(8) const u8),
626 .b, .c => |*p| {
627 _ = p;
628 return error.TestFailed;
629 },
630 }
631
632 switch (u) {
633 .a, .b => |*p| comptime assert(@TypeOf(p) == *align(4) const u8),
634 .c => |*p| {
635 _ = p;
636 return error.TestFailed;
637 },
638 }
639
640 switch (u) {
641 .a, .c => |*p| comptime assert(@TypeOf(p) == *align(1) const u8),
642 .b => |*p| {
643 _ = p;
644 return error.TestFailed;
645 },
646 }
647 }
648
649 fn doTheTest2() !void {
650 const un1 = U{ .b = 1 };
651 switch (un1) {
652 .b => |*b| comptime assert(@TypeOf(b) == *align(4) const u8),
653 .a, .c => |*p| {
654 _ = p;
655 return error.TestFailed;
656 },
657 }
658
659 const un2 = U{ .c = 1 };
660 switch (un2) {
661 .c => |*c| comptime assert(@TypeOf(c) == *const u8),
662 .a, .b => |*p| {
663 _ = p;
664 return error.TestFailed;
665 },
666 }
667 }
668 };
669
670 try S.doTheTest();
671 try comptime S.doTheTest();
672
673 try S.doTheTest2();
674 try comptime S.doTheTest2();
675}
676
677test "switch on pointer type" {
678 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
679 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
680 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
681 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/llvm/llvm-project/issues/176634
682
683 const S = struct {
684 const X = struct {
685 field: u32,
686 };
687
688 const P1 = @as(*X, @ptrFromInt(0x400));
689 const P2 = @as(*X, @ptrFromInt(0x800));
690 const P3 = @as(*X, @ptrFromInt(0xC00));
691
692 fn doTheTest(arg: *X) i32 {
693 switch (arg) {
694 P1 => return 1,
695 P2 => return 2,
696 else => return 3,
697 }
698 }
699 };
700
701 try expect(1 == S.doTheTest(S.P1));
702 try expect(2 == S.doTheTest(S.P2));
703 try expect(3 == S.doTheTest(S.P3));
704 comptime assert(1 == S.doTheTest(S.P1));
705 comptime assert(2 == S.doTheTest(S.P2));
706 comptime assert(3 == S.doTheTest(S.P3));
707}
708
709test "switch on error set with single else" {
710 const S = struct {
711 fn doTheTest() !void {
712 var some: error{Foo} = error.Foo;
713 _ = &some;
714 try expect(switch (some) {
715 else => blk: {
716 break :blk true;
717 },
718 });
719 }
720 };
721
722 try S.doTheTest();
723 try comptime S.doTheTest();
724}
725
726test "switch capture copies its payload" {
727 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
728 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
729 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
730
731 const S = struct {
732 fn doTheTest() !void {
733 var tmp: union(enum) {
734 A: u8,
735 B: u32,
736 } = .{ .A = 42 };
737 switch (tmp) {
738 .A => |value| {
739 // Modify the original union
740 tmp = .{ .B = 0x10101010 };
741 try expectEqual(@as(u8, 42), value);
742 },
743 else => unreachable,
744 }
745 }
746 };
747 try S.doTheTest();
748 try comptime S.doTheTest();
749}
750
751test "capture of integer forwards the switch condition directly" {
752 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
753
754 const S = struct {
755 fn foo(x: u8) !void {
756 switch (x) {
757 40...45 => |capture| {
758 try expect(capture == 42);
759 },
760 else => |capture| {
761 try expect(capture == 100);
762 },
763 }
764 }
765 };
766 try S.foo(42);
767 try S.foo(100);
768 try comptime S.foo(42);
769 try comptime S.foo(100);
770}
771
772test "enum value without tag name used as switch item" {
773 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
774
775 const E = enum(u32) {
776 a = 1,
777 b = 2,
778 _,
779 };
780 var e: E = @fromBackingInt(@intCast(0));
781 _ = &e;
782 switch (e) {
783 @as(E, @fromBackingInt(@intCast(0))) => {},
784 .a => return error.TestFailed,
785 .b => return error.TestFailed,
786 _ => return error.TestFailed,
787 }
788}
789
790test "switch item sizeof" {
791 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
792 const S = struct {
793 fn doTheTest() !void {
794 var a: usize = 0;
795 _ = &a;
796 switch (a) {
797 @sizeOf(struct {}) => {},
798 else => return error.TestFailed,
799 }
800 }
801 };
802 try S.doTheTest();
803 try comptime S.doTheTest();
804}
805
806test "comptime inline switch" {
807 const U = union(enum) { a: type, b: type };
808 const value = comptime blk: {
809 var u: U = .{ .a = u32 };
810 _ = &u;
811 break :blk switch (u) {
812 inline .a, .b => |v| v,
813 };
814 };
815
816 try expectEqual(u32, value);
817}
818
819test "switch capture peer type resolution" {
820 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
821
822 const U = union(enum) {
823 a: u32,
824 b: u64,
825 fn innerVal(u: @This()) u64 {
826 switch (u) {
827 .a, .b => |x| return x,
828 }
829 }
830 };
831
832 try expectEqual(@as(u64, 100), U.innerVal(.{ .a = 100 }));
833 try expectEqual(@as(u64, 200), U.innerVal(.{ .b = 200 }));
834}
835
836test "switch capture peer type resolution for in-memory coercible payloads" {
837 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
838 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
839
840 const T1 = c_int;
841 const t1_info = @typeInfo(T1).int;
842 const T2 = @Int(t1_info.signedness, t1_info.bits);
843
844 comptime assert(T1 != T2);
845
846 const U = union(enum) {
847 a: T1,
848 b: T2,
849 fn innerVal(u: @This()) c_int {
850 switch (u) {
851 .a, .b => |x| return x,
852 }
853 }
854 };
855
856 try expectEqual(@as(c_int, 100), U.innerVal(.{ .a = 100 }));
857 try expectEqual(@as(c_int, 200), U.innerVal(.{ .b = 200 }));
858}
859
860test "switch pointer capture peer type resolution" {
861 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
862 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
863
864 const T1 = c_int;
865 const t1_info = @typeInfo(T1).int;
866 const T2 = @Int(t1_info.signedness, t1_info.bits);
867
868 comptime assert(T1 != T2);
869
870 const U = union(enum) {
871 a: T1,
872 b: T2,
873 fn innerVal(u: *@This()) *c_int {
874 switch (u.*) {
875 .a, .b => |*ptr| return ptr,
876 }
877 }
878 };
879
880 var ua: U = .{ .a = 100 };
881 var ub: U = .{ .b = 200 };
882
883 ua.innerVal().* = 111;
884 ub.innerVal().* = 222;
885
886 try expectEqual(U{ .a = 111 }, ua);
887 try expectEqual(U{ .b = 222 }, ub);
888}
889
890test "inline switch range that includes the maximum value of the switched type" {
891 const inputs: [3]u8 = .{ 0, 254, 255 };
892 for (inputs) |input| {
893 switch (input) {
894 inline 254...255 => |val| try expectEqual(input, val),
895 else => |val| try expectEqual(input, val),
896 }
897 }
898}
899
900test "nested break ignores switch conditions and breaks instead" {
901 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
902 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
903 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
904
905 const S = struct {
906 fn register_to_address(ident: []const u8) !u8 {
907 const reg: u8 = if (std.mem.eql(u8, ident, "zero")) 0x00 else blk: {
908 break :blk switch (ident[0]) {
909 0x61 => (try std.fmt.parseInt(u8, ident[1..], 0)) + 1,
910 0x66 => (try std.fmt.parseInt(u8, ident[1..], 0)) + 1,
911 else => {
912 break :blk 0xFF;
913 },
914 };
915 };
916 return reg;
917 }
918 };
919 // Originally reported at https://github.com/ziglang/zig/issues/10196
920 try expect(0x01 == try S.register_to_address("a0"));
921}
922
923test "peer type resolution on switch captures ignores unused payload bits" {
924 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
925 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
926
927 const Foo = union(enum) {
928 a: u32,
929 b: u64,
930 };
931
932 var val: Foo = undefined;
933 @memset(std.mem.asBytes(&val), 0xFF);
934
935 // This is runtime-known so the following store isn't comptime-known.
936 var rt: u32 = 123;
937 _ = &rt;
938 val = .{ .a = rt }; // will not necessarily zero remaning payload memory
939
940 // Fields intentionally backwards here
941 const x = switch (val) {
942 .b, .a => |x| x,
943 };
944
945 try expect(x == 123);
946}
947
948test "switch prong captures range" {
949 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
950 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
951
952 const S = struct {
953 fn a(b: []u3, c: u3) void {
954 switch (c) {
955 0...1 => b[c] = c,
956 2...3 => b[c] = c,
957 4...7 => |d| b[d] = c,
958 }
959 }
960 };
961
962 var arr: [8]u3 = undefined;
963 S.a(&arr, 5);
964 try expect(arr[5] == 5);
965}
966
967test "prong with inline call to unreachable" {
968 const U = union(enum) {
969 void: void,
970 bool: bool,
971
972 inline fn unreach() noreturn {
973 unreachable;
974 }
975 };
976 var u: U = undefined;
977 u = .{ .bool = true };
978 switch (u) {
979 .void => U.unreach(),
980 .bool => |ok| try expect(ok),
981 }
982}
983
984test "block error return trace index is reset between prongs" {
985 const S = struct {
986 fn returnError() error{TestFailed} {
987 return error.TestFailed;
988 }
989 };
990
991 var x: u1 = 0;
992 _ = &x;
993
994 const result = switch (x) {
995 0 => {
996 const result: anyerror!i32 = blk: {
997 break :blk 1;
998 };
999 _ = &result;
1000 },
1001 1 => blk: {
1002 const err = switch (x) {
1003 0 => {},
1004 1 => S.returnError(),
1005 };
1006 break :blk err;
1007 },
1008 };
1009 try result;
1010}
1011
1012test "labeled switch with break" {
1013 var six: u32 = undefined;
1014 six = 6;
1015
1016 const val = s: switch (six) {
1017 0...4 => break :s false,
1018 5 => break :s false,
1019 6...7 => break :s true,
1020 else => break :s false,
1021 };
1022
1023 try expect(val);
1024
1025 // Make sure the switch is implicitly comptime!
1026 const comptime_val = s: switch (@as(u32, 6)) {
1027 0...4 => break :s false,
1028 5 => break :s false,
1029 6...7 => break :s true,
1030 else => break :s false,
1031 };
1032
1033 comptime assert(comptime_val);
1034}
1035
1036test "unlabeled break ignores switch" {
1037 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1038 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1039 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1040
1041 const result = while (true) {
1042 _ = s: switch (@as(u32, 1)) {
1043 1 => continue :s 123,
1044 else => |x| break x,
1045 };
1046 comptime unreachable; // control flow never breaks from the switch
1047 };
1048 try expect(result == 123);
1049}
1050
1051test "switch on a signed value smaller than the smallest prong value" {
1052 var v: i32 = undefined;
1053 v = -1;
1054 switch (v) {
1055 inline 0...10 => return error.TestFailed,
1056 else => {},
1057 }
1058}
1059
1060test "switch on 8-bit mod result" {
1061 var x: u8 = undefined;
1062 x = 16;
1063 switch (x % 4) {
1064 0 => {},
1065 1, 2, 3 => return error.TestFailed,
1066 else => unreachable,
1067 }
1068}
1069
1070test "switch on non-exhaustive enum" {
1071 const E = enum(u4) {
1072 a,
1073 b,
1074 c,
1075 _,
1076
1077 fn doTheTest(e: @This()) !void {
1078 switch (e) {
1079 .a, .b => {},
1080 else => return error.TestFailed,
1081 }
1082 switch (e) {
1083 .a, .b => {},
1084 .c => return error.TestFailed,
1085 _ => return error.TestFailed,
1086 }
1087 switch (e) {
1088 .a, .b => {},
1089 .c, _ => return error.TestFailed,
1090 }
1091 switch (e) {
1092 .a => {},
1093 .b, .c, _ => return error.TestFailed,
1094 }
1095 switch (e) {
1096 .b => return error.TestFailed,
1097 else => {},
1098 _ => return error.TestFailed,
1099 }
1100 switch (e) {
1101 else => {},
1102 _ => return error.TestFailed,
1103 }
1104 switch (e) {
1105 inline else => {},
1106 _ => return error.TestFailed,
1107 }
1108 }
1109 };
1110
1111 try E.doTheTest(.a);
1112 try comptime E.doTheTest(.a);
1113}
1114
1115test "decl literals as switch cases" {
1116 const E = enum(u8) {
1117 bar = 3,
1118 _,
1119
1120 const foo: @This() = @fromBackingInt(@intCast(0xa));
1121
1122 fn doTheTest(e: @This()) !void {
1123 switch (e) {
1124 .bar => return error.TestFailed,
1125 .foo => {},
1126 else => return error.TestFailed,
1127 }
1128 }
1129 };
1130
1131 try E.doTheTest(.foo);
1132 try comptime E.doTheTest(.foo);
1133}
1134
1135// TODO audit after https://github.com/ziglang/zig/issues/15909 is fully decided.
1136// When we do that, consider adding an 'error{}' case if possible.
1137test "switch with uninstantiable union fields" {
1138 const U = union(enum) {
1139 ok: void,
1140 a: noreturn,
1141 b: enum {},
1142
1143 fn doTheTest(u: @This()) void {
1144 switch (u) {
1145 .ok => {},
1146 .a => comptime unreachable,
1147 .b => comptime unreachable,
1148 }
1149 switch (u) {
1150 .ok => {},
1151 .a, .b => comptime unreachable,
1152 }
1153 switch (u) {
1154 .ok => {},
1155 else => comptime unreachable,
1156 }
1157 switch (u) {
1158 .a => comptime unreachable,
1159 .ok, .b => {},
1160 }
1161 }
1162 };
1163
1164 U.doTheTest(.ok);
1165 comptime U.doTheTest(.ok);
1166}
1167
1168test "switch with tag capture" {
1169 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1170
1171 const U = union(enum) {
1172 a,
1173 b: i32,
1174 c: u8,
1175 d: i32,
1176 e: noreturn,
1177
1178 fn doTheTest() !void {
1179 try doTheSwitch(.a);
1180 try doTheSwitch(.{ .b = 123 });
1181 try doTheSwitch(.{ .c = 0xFF });
1182 }
1183 fn doTheSwitch(u: @This()) !void {
1184 switch (u) {
1185 .a => |nothing, tag| {
1186 comptime assert(nothing == {});
1187 comptime assert(tag == .a);
1188 try expect(@backingInt(tag) == @backingInt(@This().a));
1189 },
1190 .b, .d => |_, tag| {
1191 try expect(tag == .b or tag == .d);
1192 },
1193 .e => |payload, tag| {
1194 _ = &payload;
1195 _ = &tag;
1196 comptime unreachable;
1197 },
1198 else => |un, tag| {
1199 try expect(tag == .c);
1200 try expect(un == .c);
1201 try expect(un.c == 0xFF);
1202 },
1203 }
1204 switch (u) {
1205 inline .a, .b, .c => |payload, tag| {
1206 if (@TypeOf(payload) == void) comptime assert(tag == .a);
1207 if (@TypeOf(payload) == i32) comptime assert(tag == .b);
1208 if (@TypeOf(payload) == u8) comptime assert(tag == .c);
1209 },
1210 inline else => |payload, tag| {
1211 if (@TypeOf(payload) == i32) comptime assert(tag == .d);
1212 comptime assert(tag != .e);
1213 },
1214 }
1215 }
1216 };
1217
1218 try U.doTheTest();
1219 try comptime U.doTheTest();
1220}
1221
1222test "switch with complex item expressions" {
1223 const S = struct {
1224 fn doTheTest() !void {
1225 try doTheSwitch(2000, 20);
1226 try doTheSwitch(2000, 10);
1227 try doTheSwitch(2000, 5);
1228
1229 try doTheOtherSwitch(@fromBackingInt(@intCast(123)));
1230 try doTheOtherSwitch(@fromBackingInt(@intCast(456)));
1231 }
1232 fn doTheSwitch(x: u32, comptime factor: u32) !void {
1233 const ok = switch (x) {
1234 num(factor) => true,
1235 typedNum(u32, factor) => true,
1236 blk: {
1237 var val = 400;
1238 val *= factor;
1239 break :blk val;
1240 } => true,
1241 else => false,
1242 };
1243 try expect(ok);
1244 }
1245 fn num(factor: u32) u32 {
1246 return 100 * factor;
1247 }
1248 fn typedNum(comptime T: type, factor: T) T {
1249 return 200 * factor;
1250 }
1251
1252 const E = enum(u32) { _ };
1253 fn doTheOtherSwitch(e: E) !void {
1254 const ok = switch (e) {
1255 @fromBackingInt(@intCast(123)) => true,
1256 @fromBackingInt(@intCast(456)) => true,
1257 else => false,
1258 };
1259 try expect(ok);
1260 }
1261 };
1262
1263 try S.doTheTest();
1264 try comptime S.doTheTest();
1265}
1266
1267test "switch evaluation order" {
1268 const eu: anyerror!u32 = 0;
1269 _ = eu catch |err| switch (err) {
1270 if (true) comptime unreachable => unreachable,
1271 else => unreachable,
1272 };
1273}
1274
1275test "switch resolves lazy values correctly" {
1276 const S = extern struct {
1277 a: u16,
1278 b: i16,
1279 };
1280 switch (@sizeOf(S)) {
1281 4 => {},
1282 else => comptime unreachable,
1283 }
1284}
1285
1286test "single-item prong in switch on enum has comptime-known capture" {
1287 const E = enum {
1288 a,
1289 b,
1290 c,
1291 fn doTheTest(e: @This()) !void {
1292 switch (e) {
1293 .a => |tag| comptime assert(tag == .a),
1294 .b => return error.TestFailed,
1295 .c => return error.TestFailed,
1296 }
1297 }
1298 };
1299 try E.doTheTest(.a);
1300 try comptime E.doTheTest(.a);
1301}
1302
1303test "single range switch prong capture" {
1304 const S = struct {
1305 fn doTheTest(x: u8) !void {
1306 switch (x) {
1307 1...5 => |val| {
1308 try expect(val == 2);
1309 },
1310 else => return error.TestFailed,
1311 }
1312 switch (x) {
1313 1...5, 6 => |val| {
1314 try expect(val == 2);
1315 },
1316 else => return error.TestFailed,
1317 }
1318 }
1319 };
1320 try S.doTheTest(2);
1321 try comptime S.doTheTest(2);
1322}
1323
1324test "switch on packed struct" {
1325 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1326
1327 const P = packed struct {
1328 a: u1,
1329 b: u1,
1330
1331 fn doTheTest(p: @This()) !void {
1332 switch (p) {
1333 .{ .a = 0, .b = 1 } => {},
1334 else => return error.TestFailed,
1335 }
1336
1337 switch (p) {
1338 .{ .a = 0, .b = 1 } => {},
1339 .{ .a = 0, .b = 0 },
1340 .{ .a = 1, .b = 0 },
1341 .{ .a = 1, .b = 1 },
1342 => return error.TestFailed,
1343 }
1344
1345 switch (p) {
1346 inline else => |val| {
1347 if (val != @This(){ .a = 0, .b = 1 }) return error.TestFailed;
1348 },
1349 }
1350 }
1351 };
1352 try P.doTheTest(.{ .a = 0, .b = 1 });
1353 try comptime P.doTheTest(.{ .a = 0, .b = 1 });
1354}
1355
1356test "switch on packed union" {
1357 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1358
1359 const P = packed union(u2) {
1360 a: u2,
1361 b: i2,
1362 c: packed struct(u2) { x: u1, y: i1 },
1363
1364 fn doTheTest(p: @This()) !void {
1365 switch (p) {
1366 .{ .a = 1 } => {},
1367 else => return error.TestFailed,
1368 }
1369
1370 switch (p) {
1371 .{ .a = 1 } => {},
1372 .{ .a = 0 },
1373 .{ .a = 2 },
1374 .{ .a = 3 },
1375 => return error.TestFailed,
1376 }
1377
1378 switch (p) {
1379 .{ .a = 1 } => {},
1380 .{ .a = 0 },
1381 .{ .b = -2 },
1382 .{ .b = -1 },
1383 => return error.TestFailed,
1384 }
1385
1386 switch (p) {
1387 .{ .c = .{ .x = 1, .y = 0 } } => {},
1388 .{ .b = 0 },
1389 .{ .a = 2 },
1390 .{ .c = .{ .x = 1, .y = -1 } },
1391 => return error.TestFailed,
1392 }
1393
1394 switch (p) {
1395 inline else => |val| {
1396 if (val != @This(){ .c = .{ .x = 1, .y = 0 } }) return error.TestFailed;
1397 },
1398 }
1399 }
1400 };
1401 try P.doTheTest(.{ .a = 1 });
1402 try comptime P.doTheTest(.{ .a = 1 });
1403}
1404
1405test "switch on nested packed containers" {
1406 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1407
1408 const P = packed struct {
1409 iu: u17,
1410 is: i31,
1411 b: bool,
1412 e: enum(u5) { a = 5, b = 3, c = 12 },
1413 un: packed union {
1414 a: i9,
1415 b: u9,
1416 c: packed struct(u9) { a: i5, b: u4 },
1417 },
1418 p: packed struct(u9) { a: u3, b: u6 },
1419
1420 fn doTheTest(p: @This()) !void {
1421 switch (p) {
1422 .{
1423 .iu = 72,
1424 .is = 124,
1425 .b = false,
1426 .e = .c,
1427 .un = .{ .b = 13 },
1428 .p = .{ .a = 0, .b = 12 },
1429 } => return error.TestFailed,
1430 .{
1431 .iu = 129,
1432 .is = -162784612,
1433 .b = true,
1434 .e = .a,
1435 .un = .{ .c = .{ .a = -3, .b = 9 } },
1436 .p = .{ .a = 2, .b = 17 },
1437 } => {},
1438 else => return error.TestFailed,
1439 }
1440 }
1441 };
1442 try P.doTheTest(.{
1443 .iu = 129,
1444 .is = -162784612,
1445 .b = true,
1446 .e = .a,
1447 .un = .{ .c = .{ .a = -3, .b = 9 } },
1448 .p = .{ .a = 2, .b = 17 },
1449 });
1450 try comptime P.doTheTest(.{
1451 .iu = 129,
1452 .is = -162784612,
1453 .b = true,
1454 .e = .a,
1455 .un = .{ .c = .{ .a = -3, .b = 9 } },
1456 .p = .{ .a = 2, .b = 17 },
1457 });
1458}
1459
1460test "switch on large types" {
1461 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1462
1463 const S = struct {
1464 fn doTheTest(a: u128, b: i500) !void {
1465 switch (a) {
1466 0x0,
1467 0x3...0xFFFF_FFFF_FFFF_FFFF_FFFF_ABCD,
1468 0xFFFF_FFFF_FFFF_FFFF_FFFF_EF00,
1469 => return error.TestFailed,
1470 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_0000...0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFF0,
1471 => |val| {
1472 try expect(val == 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_1234);
1473 },
1474 else => return error.TestFailed,
1475 }
1476 switch (b) {
1477 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,
1478 => return error.TestFailed,
1479 0xFFFF_1234,
1480 0xFFFF_FFFF_FFFF_FFFF_FFFF_0123...0xFFFF_FFFF_FFFF_FFFF_FFFF_4567,
1481 => |val| {
1482 try expect(val == 0xFFFF_1234);
1483 },
1484 else => return error.TestFailed,
1485 }
1486 }
1487 };
1488 try S.doTheTest(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_1234, 0xFFFF_1234);
1489 try comptime S.doTheTest(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_1234, 0xFFFF_1234);
1490}
1491
1492test "error captures narrow error sets" {
1493 const S = struct {
1494 fn doTheTest(err: error{ A, B, C, D }) !void {
1495 switch (err) {
1496 error.A, error.B => |e| comptime assert(@TypeOf(e) == error{ A, B }),
1497 else => |e| comptime assert(@TypeOf(e) == error{ C, D }),
1498 }
1499 switch (err) {
1500 inline error.A, error.B => |e| comptime {
1501 if (e == error.A)
1502 assert(@TypeOf(e) == error{A})
1503 else if (e == error.B)
1504 assert(@TypeOf(e) == error{B})
1505 else
1506 unreachable;
1507 },
1508 inline else => |e| comptime {
1509 if (e == error.C)
1510 assert(@TypeOf(e) == error{C})
1511 else if (e == error.D)
1512 assert(@TypeOf(e) == error{D})
1513 else
1514 unreachable;
1515 },
1516 }
1517 }
1518 };
1519
1520 try S.doTheTest(error.B);
1521 try comptime S.doTheTest(error.B);
1522 try comptime S.doTheTest(error.C);
1523}
1524
1525test "repeated switch analysis overrides previous analysis results" {
1526 // This tests an implementation detail where semantic analysis of switch
1527 // statements uses the switch inst itself to store capture values and result
1528 // type information while analyzing (parts of) that switch inst.
1529 // If that inst has already been assigned a result by a previous analysis
1530 // that result needs to be overwritten.
1531
1532 comptime {
1533 const x: u32 = 123;
1534 for (0..2) |_| _ = switch (x) {
1535 123 => |capture| capture,
1536 else => unreachable,
1537 };
1538 }
1539 comptime {
1540 const x: union(enum) { a, b, c } = .a;
1541 for (0..2) |_| _ = switch (x) {
1542 .a => |_, tag| tag,
1543 else => unreachable,
1544 };
1545 }
1546 comptime {
1547 const x: enum { a, b, c } = .a;
1548 for (0..2) |_| _ = label: switch (x) {
1549 .a => continue :label .b,
1550 else => 123,
1551 };
1552 }
1553 comptime {
1554 const x: anyerror!void = error.MyError;
1555 for (0..2) |_| _ = x catch |err| switch (err) {
1556 error.MyError => {},
1557 else => unreachable,
1558 };
1559 }
1560}
1561
1562test "union field pointer capture preserves alignment in inline prong" {
1563 const U = union(enum) {
1564 a: u32,
1565 b: u32,
1566 fn doTheTest(u: *align(1) const @This()) !void {
1567 switch (u.*) {
1568 inline .a, .b => |*a_ptr| {
1569 comptime assert(@TypeOf(a_ptr) == *align(1) const u32);
1570 try expect(a_ptr.* == 123);
1571 },
1572 }
1573 }
1574 };
1575 try U.doTheTest(&.{ .a = 123 });
1576 try U.doTheTest(&.{ .b = 123 });
1577 try comptime U.doTheTest(&.{ .a = 123 });
1578 try comptime U.doTheTest(&.{ .b = 123 });
1579}