Commit c4df9bf56f

Andrew Kelley <andrew@ziglang.org>
2021-10-03 05:15:03
AstGen: fix `while` and `for` with unreachable bodies
Companion commit to 61a53a587558ff1fe1b0ec98bb424022885edccf. This commit also moves over a bunch of behavior test cases to the passing-for-stage2 section.
1 parent 61a53a5
src/codegen/llvm.zig
@@ -1605,7 +1605,15 @@ pub const FuncGen = struct {
         self.builder.positionBuilderAtEnd(loop_block);
         try self.genBody(body);
 
-        _ = self.builder.buildBr(loop_block);
+        // TODO instead of this logic, change AIR to have the property that
+        // every block is guaranteed to end with a noreturn instruction.
+        // Then we can simply rely on the fact that a repeat or break instruction
+        // would have been emitted already. Also the main loop in genBody can
+        // be while(true) instead of for(body), which will eliminate 1 branch on
+        // a hot path.
+        if (body.len == 0 or !self.air.typeOfIndex(body[body.len - 1]).isNoReturn()) {
+            _ = self.builder.buildBr(loop_block);
+        }
         return null;
     }
 
src/AstGen.zig
@@ -5537,8 +5537,10 @@ fn whileExpr(
         });
     }
 
-    loop_scope.break_count += 1;
     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
+    if (!then_scope.endsWithNoReturn()) {
+        loop_scope.break_count += 1;
+    }
     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
 
     var else_scope = parent_gz.makeSubBlock(&continue_scope.base);
@@ -5549,7 +5551,6 @@ fn whileExpr(
         src: Ast.Node.Index,
         result: Zir.Inst.Ref,
     } = if (else_node != 0) blk: {
-        loop_scope.break_count += 1;
         const sub_scope = s: {
             if (while_full.error_token) |error_token| {
                 const tag: Zir.Inst.Tag = if (payload_is_ref)
@@ -5576,6 +5577,9 @@ fn whileExpr(
             }
         };
         const e = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
+        if (!else_scope.endsWithNoReturn()) {
+            loop_scope.break_count += 1;
+        }
         try checkUsed(parent_gz, &else_scope.base, sub_scope);
         break :blk .{
             .src = else_node,
@@ -5746,8 +5750,10 @@ fn forExpr(
         break :blk &index_scope.base;
     };
 
-    loop_scope.break_count += 1;
     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
+    if (!then_scope.endsWithNoReturn()) {
+        loop_scope.break_count += 1;
+    }
     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
 
     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
@@ -5758,11 +5764,14 @@ fn forExpr(
         src: Ast.Node.Index,
         result: Zir.Inst.Ref,
     } = if (else_node != 0) blk: {
-        loop_scope.break_count += 1;
         const sub_scope = &else_scope.base;
+        const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
+        if (!else_scope.endsWithNoReturn()) {
+            loop_scope.break_count += 1;
+        }
         break :blk .{
             .src = else_node,
-            .result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node),
+            .result = else_result,
         };
     } else .{
         .src = for_full.ast.then_expr,
test/behavior/call.zig
@@ -1,74 +1,3 @@
 const std = @import("std");
 const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
-
-test "basic invocations" {
-    const foo = struct {
-        fn foo() i32 {
-            return 1234;
-        }
-    }.foo;
-    try expect(@call(.{}, foo, .{}) == 1234);
-    comptime {
-        // modifiers that allow comptime calls
-        try expect(@call(.{}, foo, .{}) == 1234);
-        try expect(@call(.{ .modifier = .no_async }, foo, .{}) == 1234);
-        try expect(@call(.{ .modifier = .always_tail }, foo, .{}) == 1234);
-        try expect(@call(.{ .modifier = .always_inline }, foo, .{}) == 1234);
-    }
-    {
-        // comptime call without comptime keyword
-        const result = @call(.{ .modifier = .compile_time }, foo, .{}) == 1234;
-        comptime try expect(result);
-    }
-    {
-        // call of non comptime-known function
-        var alias_foo = foo;
-        try expect(@call(.{ .modifier = .no_async }, alias_foo, .{}) == 1234);
-        try expect(@call(.{ .modifier = .never_tail }, alias_foo, .{}) == 1234);
-        try expect(@call(.{ .modifier = .never_inline }, alias_foo, .{}) == 1234);
-    }
-}
-
-test "tuple parameters" {
-    const add = struct {
-        fn add(a: i32, b: i32) i32 {
-            return a + b;
-        }
-    }.add;
-    var a: i32 = 12;
-    var b: i32 = 34;
-    try expect(@call(.{}, add, .{ a, 34 }) == 46);
-    try expect(@call(.{}, add, .{ 12, b }) == 46);
-    try expect(@call(.{}, add, .{ a, b }) == 46);
-    try expect(@call(.{}, add, .{ 12, 34 }) == 46);
-    comptime try expect(@call(.{}, add, .{ 12, 34 }) == 46);
-    {
-        const separate_args0 = .{ a, b };
-        const separate_args1 = .{ a, 34 };
-        const separate_args2 = .{ 12, 34 };
-        const separate_args3 = .{ 12, b };
-        try expect(@call(.{ .modifier = .always_inline }, add, separate_args0) == 46);
-        try expect(@call(.{ .modifier = .always_inline }, add, separate_args1) == 46);
-        try expect(@call(.{ .modifier = .always_inline }, add, separate_args2) == 46);
-        try expect(@call(.{ .modifier = .always_inline }, add, separate_args3) == 46);
-    }
-}
-
-test "comptime call with bound function as parameter" {
-    const S = struct {
-        fn ReturnType(func: anytype) type {
-            return switch (@typeInfo(@TypeOf(func))) {
-                .BoundFn => |info| info,
-                else => unreachable,
-            }.return_type orelse void;
-        }
-
-        fn call_me_maybe() ?i32 {
-            return 123;
-        }
-    };
-
-    var inst: S = undefined;
-    try expectEqual(?i32, S.ReturnType(inst.call_me_maybe));
-}
test/behavior/call_stage1.zig
@@ -0,0 +1,74 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+
+test "basic invocations" {
+    const foo = struct {
+        fn foo() i32 {
+            return 1234;
+        }
+    }.foo;
+    try expect(@call(.{}, foo, .{}) == 1234);
+    comptime {
+        // modifiers that allow comptime calls
+        try expect(@call(.{}, foo, .{}) == 1234);
+        try expect(@call(.{ .modifier = .no_async }, foo, .{}) == 1234);
+        try expect(@call(.{ .modifier = .always_tail }, foo, .{}) == 1234);
+        try expect(@call(.{ .modifier = .always_inline }, foo, .{}) == 1234);
+    }
+    {
+        // comptime call without comptime keyword
+        const result = @call(.{ .modifier = .compile_time }, foo, .{}) == 1234;
+        comptime try expect(result);
+    }
+    {
+        // call of non comptime-known function
+        var alias_foo = foo;
+        try expect(@call(.{ .modifier = .no_async }, alias_foo, .{}) == 1234);
+        try expect(@call(.{ .modifier = .never_tail }, alias_foo, .{}) == 1234);
+        try expect(@call(.{ .modifier = .never_inline }, alias_foo, .{}) == 1234);
+    }
+}
+
+test "tuple parameters" {
+    const add = struct {
+        fn add(a: i32, b: i32) i32 {
+            return a + b;
+        }
+    }.add;
+    var a: i32 = 12;
+    var b: i32 = 34;
+    try expect(@call(.{}, add, .{ a, 34 }) == 46);
+    try expect(@call(.{}, add, .{ 12, b }) == 46);
+    try expect(@call(.{}, add, .{ a, b }) == 46);
+    try expect(@call(.{}, add, .{ 12, 34 }) == 46);
+    comptime try expect(@call(.{}, add, .{ 12, 34 }) == 46);
+    {
+        const separate_args0 = .{ a, b };
+        const separate_args1 = .{ a, 34 };
+        const separate_args2 = .{ 12, 34 };
+        const separate_args3 = .{ 12, b };
+        try expect(@call(.{ .modifier = .always_inline }, add, separate_args0) == 46);
+        try expect(@call(.{ .modifier = .always_inline }, add, separate_args1) == 46);
+        try expect(@call(.{ .modifier = .always_inline }, add, separate_args2) == 46);
+        try expect(@call(.{ .modifier = .always_inline }, add, separate_args3) == 46);
+    }
+}
+
+test "comptime call with bound function as parameter" {
+    const S = struct {
+        fn ReturnType(func: anytype) type {
+            return switch (@typeInfo(@TypeOf(func))) {
+                .BoundFn => |info| info,
+                else => unreachable,
+            }.return_type orelse void;
+        }
+
+        fn call_me_maybe() ?i32 {
+            return 123;
+        }
+    };
+
+    var inst: S = undefined;
+    try expectEqual(?i32, S.ReturnType(inst.call_me_maybe));
+}
test/behavior/cast.zig
@@ -16,3 +16,52 @@ test "integer literal to pointer cast" {
     const vga_mem = @intToPtr(*u16, 0xB8000);
     try expect(@ptrToInt(vga_mem) == 0xB8000);
 }
+
+test "peer type resolution: ?T and T" {
+    try expect(peerTypeTAndOptionalT(true, false).? == 0);
+    try expect(peerTypeTAndOptionalT(false, false).? == 3);
+    comptime {
+        try expect(peerTypeTAndOptionalT(true, false).? == 0);
+        try expect(peerTypeTAndOptionalT(false, false).? == 3);
+    }
+}
+fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
+    if (c) {
+        return if (b) null else @as(usize, 0);
+    }
+
+    return @as(usize, 3);
+}
+
+test "resolve undefined with integer" {
+    try testResolveUndefWithInt(true, 1234);
+    comptime try testResolveUndefWithInt(true, 1234);
+}
+fn testResolveUndefWithInt(b: bool, x: i32) !void {
+    const value = if (b) x else undefined;
+    if (b) {
+        try expect(value == x);
+    }
+}
+
+test "@intCast i32 to u7" {
+    var x: u128 = maxInt(u128);
+    var y: i32 = 120;
+    var z = x >> @intCast(u7, y);
+    try expect(z == 0xff);
+}
+
+test "@intCast to comptime_int" {
+    try expect(@intCast(comptime_int, 0) == 0);
+}
+
+test "implicit cast comptime numbers to any type when the value fits" {
+    const a: u64 = 255;
+    var b: u8 = a;
+    try expect(b == 255);
+}
+
+test "implicit cast comptime_int to comptime_float" {
+    comptime try expect(@as(comptime_float, 10) == @as(f32, 10));
+    try expect(2 == 2.0);
+}
test/behavior/cast_stage1.zig
@@ -121,22 +121,6 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A {
     return null;
 }
 
-test "peer type resolution: ?T and T" {
-    try expect(peerTypeTAndOptionalT(true, false).? == 0);
-    try expect(peerTypeTAndOptionalT(false, false).? == 3);
-    comptime {
-        try expect(peerTypeTAndOptionalT(true, false).? == 0);
-        try expect(peerTypeTAndOptionalT(false, false).? == 3);
-    }
-}
-fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
-    if (c) {
-        return if (b) null else @as(usize, 0);
-    }
-
-    return @as(usize, 3);
-}
-
 test "peer type resolution: [0]u8 and []const u8" {
     try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
     try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
@@ -203,17 +187,6 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
     return slice[0..1];
 }
 
-test "resolve undefined with integer" {
-    try testResolveUndefWithInt(true, 1234);
-    comptime try testResolveUndefWithInt(true, 1234);
-}
-fn testResolveUndefWithInt(b: bool, x: i32) !void {
-    const value = if (b) x else undefined;
-    if (b) {
-        try expect(value == x);
-    }
-}
-
 test "implicit cast from &const [N]T to []const T" {
     try testCastConstArrayRefToConstSlice();
     comptime try testCastConstArrayRefToConstSlice();
@@ -424,13 +397,6 @@ test "comptime_int @intToFloat" {
     }
 }
 
-test "@intCast i32 to u7" {
-    var x: u128 = maxInt(u128);
-    var y: i32 = 120;
-    var z = x >> @intCast(u7, y);
-    try expect(z == 0xff);
-}
-
 test "@floatCast cast down" {
     {
         var double: f64 = 0.001534;
@@ -533,20 +499,8 @@ test "implicit ptr to *c_void" {
     try expect(c.* == 1);
 }
 
-test "@intCast to comptime_int" {
-    try expect(@intCast(comptime_int, 0) == 0);
-}
-
-test "implicit cast comptime numbers to any type when the value fits" {
-    const a: u64 = 255;
-    var b: u8 = a;
-    try expect(b == 255);
-}
-
 test "@intToEnum passed a comptime_int to an enum with one item" {
-    const E = enum {
-        A,
-    };
+    const E = enum { A };
     const x = @intToEnum(E, 0);
     try expect(x == E.A);
 }
@@ -599,11 +553,6 @@ test "peer type resolution: unreachable, error set, unreachable" {
     try expect(transformed_err == error.SystemResources);
 }
 
-test "implicit cast comptime_int to comptime_float" {
-    comptime try expect(@as(comptime_float, 10) == @as(f32, 10));
-    try expect(2 == 2.0);
-}
-
 test "implicit cast *[0]T to E![]const u8" {
     var x = @as(anyerror![]const u8, &[0]u8{});
     try expect((x catch unreachable).len == 0);
@@ -645,12 +594,7 @@ test "*const [N]null u8 to ?[]const u8" {
 
 test "peer resolution of string literals" {
     const S = struct {
-        const E = enum {
-            a,
-            b,
-            c,
-            d,
-        };
+        const E = enum { a, b, c, d };
 
         fn doTheTest(e: E) !void {
             const cmd = switch (e) {
test/behavior/const_slice_child.zig
@@ -6,11 +6,7 @@ const expect = testing.expect;
 var argv: [*]const [*]const u8 = undefined;
 
 test "const slice child" {
-    const strs = [_][*]const u8{
-        "one",
-        "two",
-        "three",
-    };
+    const strs = [_][*]const u8{ "one", "two", "three" };
     argv = &strs;
     try bar(strs.len);
 }
test/behavior/defer.zig
@@ -2,113 +2,3 @@ const std = @import("std");
 const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
 const expectError = std.testing.expectError;
-
-var result: [3]u8 = undefined;
-var index: usize = undefined;
-
-fn runSomeErrorDefers(x: bool) !bool {
-    index = 0;
-    defer {
-        result[index] = 'a';
-        index += 1;
-    }
-    errdefer {
-        result[index] = 'b';
-        index += 1;
-    }
-    defer {
-        result[index] = 'c';
-        index += 1;
-    }
-    return if (x) x else error.FalseNotAllowed;
-}
-
-test "mixing normal and error defers" {
-    try expect(runSomeErrorDefers(true) catch unreachable);
-    try expect(result[0] == 'c');
-    try expect(result[1] == 'a');
-
-    const ok = runSomeErrorDefers(false) catch |err| x: {
-        try expect(err == error.FalseNotAllowed);
-        break :x true;
-    };
-    try expect(ok);
-    try expect(result[0] == 'c');
-    try expect(result[1] == 'b');
-    try expect(result[2] == 'a');
-}
-
-test "break and continue inside loop inside defer expression" {
-    testBreakContInDefer(10);
-    comptime testBreakContInDefer(10);
-}
-
-fn testBreakContInDefer(x: usize) void {
-    defer {
-        var i: usize = 0;
-        while (i < x) : (i += 1) {
-            if (i < 5) continue;
-            if (i == 5) break;
-        }
-        expect(i == 5) catch @panic("test failure");
-    }
-}
-
-test "defer and labeled break" {
-    var i = @as(usize, 0);
-
-    blk: {
-        defer i += 1;
-        break :blk;
-    }
-
-    try expect(i == 1);
-}
-
-test "errdefer does not apply to fn inside fn" {
-    if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| try expect(e == error.Bad);
-}
-
-fn testNestedFnErrDefer() anyerror!void {
-    var a: i32 = 0;
-    errdefer a += 1;
-    const S = struct {
-        fn baz() anyerror {
-            return error.Bad;
-        }
-    };
-    return S.baz();
-}
-
-test "return variable while defer expression in scope to modify it" {
-    const S = struct {
-        fn doTheTest() !void {
-            try expect(notNull().? == 1);
-        }
-
-        fn notNull() ?u8 {
-            var res: ?u8 = 1;
-            defer res = null;
-            return res;
-        }
-    };
-
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "errdefer with payload" {
-    const S = struct {
-        fn foo() !i32 {
-            errdefer |a| {
-                expectEqual(error.One, a) catch @panic("test failure");
-            }
-            return error.One;
-        }
-        fn doTheTest() !void {
-            try expectError(error.One, foo());
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
test/behavior/defer_stage1.zig
@@ -0,0 +1,114 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectError = std.testing.expectError;
+
+var result: [3]u8 = undefined;
+var index: usize = undefined;
+
+fn runSomeErrorDefers(x: bool) !bool {
+    index = 0;
+    defer {
+        result[index] = 'a';
+        index += 1;
+    }
+    errdefer {
+        result[index] = 'b';
+        index += 1;
+    }
+    defer {
+        result[index] = 'c';
+        index += 1;
+    }
+    return if (x) x else error.FalseNotAllowed;
+}
+
+test "mixing normal and error defers" {
+    try expect(runSomeErrorDefers(true) catch unreachable);
+    try expect(result[0] == 'c');
+    try expect(result[1] == 'a');
+
+    const ok = runSomeErrorDefers(false) catch |err| x: {
+        try expect(err == error.FalseNotAllowed);
+        break :x true;
+    };
+    try expect(ok);
+    try expect(result[0] == 'c');
+    try expect(result[1] == 'b');
+    try expect(result[2] == 'a');
+}
+
+test "break and continue inside loop inside defer expression" {
+    testBreakContInDefer(10);
+    comptime testBreakContInDefer(10);
+}
+
+fn testBreakContInDefer(x: usize) void {
+    defer {
+        var i: usize = 0;
+        while (i < x) : (i += 1) {
+            if (i < 5) continue;
+            if (i == 5) break;
+        }
+        expect(i == 5) catch @panic("test failure");
+    }
+}
+
+test "defer and labeled break" {
+    var i = @as(usize, 0);
+
+    blk: {
+        defer i += 1;
+        break :blk;
+    }
+
+    try expect(i == 1);
+}
+
+test "errdefer does not apply to fn inside fn" {
+    if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| try expect(e == error.Bad);
+}
+
+fn testNestedFnErrDefer() anyerror!void {
+    var a: i32 = 0;
+    errdefer a += 1;
+    const S = struct {
+        fn baz() anyerror {
+            return error.Bad;
+        }
+    };
+    return S.baz();
+}
+
+test "return variable while defer expression in scope to modify it" {
+    const S = struct {
+        fn doTheTest() !void {
+            try expect(notNull().? == 1);
+        }
+
+        fn notNull() ?u8 {
+            var res: ?u8 = 1;
+            defer res = null;
+            return res;
+        }
+    };
+
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "errdefer with payload" {
+    const S = struct {
+        fn foo() !i32 {
+            errdefer |a| {
+                expectEqual(error.One, a) catch @panic("test failure");
+            }
+            return error.One;
+        }
+        fn doTheTest() !void {
+            try expectError(error.One, foo());
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
test/behavior/enum.zig
@@ -1,133 +1,7 @@
-const expect = @import("std").testing.expect;
-const mem = @import("std").mem;
-const Tag = @import("std").meta.Tag;
-
-test "non-exhaustive enum" {
-    const S = struct {
-        const E = enum(u8) {
-            a,
-            b,
-            _,
-        };
-        fn doTheTest(y: u8) !void {
-            var e: E = .b;
-            try expect(switch (e) {
-                .a => false,
-                .b => true,
-                _ => false,
-            });
-            e = @intToEnum(E, 12);
-            try expect(switch (e) {
-                .a => false,
-                .b => false,
-                _ => true,
-            });
-
-            try expect(switch (e) {
-                .a => false,
-                .b => false,
-                else => true,
-            });
-            e = .b;
-            try expect(switch (e) {
-                .a => false,
-                else => true,
-            });
-
-            try expect(@typeInfo(E).Enum.fields.len == 2);
-            e = @intToEnum(E, 12);
-            try expect(@enumToInt(e) == 12);
-            e = @intToEnum(E, y);
-            try expect(@enumToInt(e) == 52);
-            try expect(@typeInfo(E).Enum.is_exhaustive == false);
-        }
-    };
-    try S.doTheTest(52);
-    comptime try S.doTheTest(52);
-}
-
-test "empty non-exhaustive enum" {
-    const S = struct {
-        const E = enum(u8) {
-            _,
-        };
-        fn doTheTest(y: u8) !void {
-            var e = @intToEnum(E, y);
-            try expect(switch (e) {
-                _ => true,
-            });
-            try expect(@enumToInt(e) == y);
-
-            try expect(@typeInfo(E).Enum.fields.len == 0);
-            try expect(@typeInfo(E).Enum.is_exhaustive == false);
-        }
-    };
-    try S.doTheTest(42);
-    comptime try S.doTheTest(42);
-}
-
-test "single field non-exhaustive enum" {
-    const S = struct {
-        const E = enum(u8) {
-            a,
-            _,
-        };
-        fn doTheTest(y: u8) !void {
-            var e: E = .a;
-            try expect(switch (e) {
-                .a => true,
-                _ => false,
-            });
-            e = @intToEnum(E, 12);
-            try expect(switch (e) {
-                .a => false,
-                _ => true,
-            });
-
-            try expect(switch (e) {
-                .a => false,
-                else => true,
-            });
-            e = .a;
-            try expect(switch (e) {
-                .a => true,
-                else => false,
-            });
-
-            try expect(@enumToInt(@intToEnum(E, y)) == y);
-            try expect(@typeInfo(E).Enum.fields.len == 1);
-            try expect(@typeInfo(E).Enum.is_exhaustive == false);
-        }
-    };
-    try S.doTheTest(23);
-    comptime try S.doTheTest(23);
-}
-
-test "enum type" {
-    const foo1 = Foo{ .One = 13 };
-    const foo2 = Foo{
-        .Two = Point{
-            .x = 1234,
-            .y = 5678,
-        },
-    };
-    try expect(foo1.One == 13);
-    try expect(foo2.Two.x == 1234 and foo2.Two.y == 5678);
-    const bar = Bar.B;
-
-    try expect(bar == Bar.B);
-    try expect(@typeInfo(Foo).Union.fields.len == 3);
-    try expect(@typeInfo(Bar).Enum.fields.len == 4);
-    try expect(@sizeOf(Foo) == @sizeOf(FooNoVoid));
-    try expect(@sizeOf(Bar) == 1);
-}
-
-test "enum as return value" {
-    switch (returnAnInt(13)) {
-        Foo.One => |value| try expect(value == 13),
-        else => unreachable,
-    }
-}
+const std = @import("std");
+const expect = std.testing.expect;
+const mem = std.mem;
+const Tag = std.meta.Tag;
 
 const Point = struct {
     x: u64,
@@ -153,13 +27,6 @@ fn returnAnInt(x: i32) Foo {
     return Foo{ .One = x };
 }
 
-test "constant enum with payload" {
-    var empty = AnEnumWithPayload{ .Empty = {} };
-    var full = AnEnumWithPayload{ .Full = 13 };
-    shouldBeEmpty(empty);
-    shouldBeNotEmpty(full);
-}
-
 fn shouldBeEmpty(x: AnEnumWithPayload) void {
     switch (x) {
         AnEnumWithPayload.Empty => {},
@@ -179,72 +46,19 @@ const AnEnumWithPayload = union(enum) {
     Full: i32,
 };
 
-const Number = enum {
-    Zero,
-    One,
-    Two,
-    Three,
-    Four,
-};
-
-test "enum to int" {
-    try shouldEqual(Number.Zero, 0);
-    try shouldEqual(Number.One, 1);
-    try shouldEqual(Number.Two, 2);
-    try shouldEqual(Number.Three, 3);
-    try shouldEqual(Number.Four, 4);
-}
+const Number = enum { Zero, One, Two, Three, Four };
 
 fn shouldEqual(n: Number, expected: u3) !void {
     try expect(@enumToInt(n) == expected);
 }
 
-test "int to enum" {
-    try testIntToEnumEval(3);
-}
-fn testIntToEnumEval(x: i32) !void {
-    try expect(@intToEnum(IntToEnumNumber, x) == IntToEnumNumber.Three);
-}
-const IntToEnumNumber = enum {
-    Zero,
-    One,
-    Two,
-    Three,
-    Four,
-};
-
-test "@tagName" {
-    try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
-    comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
-}
-
-test "@tagName non-exhaustive enum" {
-    try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
-    comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
-}
-
 fn testEnumTagNameBare(n: anytype) []const u8 {
     return @tagName(n);
 }
 
-const BareNumber = enum {
-    One,
-    Two,
-    Three,
-};
-
-const NonExhaustive = enum(u8) {
-    A,
-    B,
-    _,
-};
+const BareNumber = enum { One, Two, Three };
 
-test "enum alignment" {
-    comptime {
-        try expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8));
-        try expect(@alignOf(AlignTestEnum) >= @alignOf(u64));
-    }
-}
+const NonExhaustive = enum(u8) { A, B, _ };
 
 const AlignTestEnum = union(enum) {
     A: [9]u8,
@@ -776,67 +590,12 @@ const ValueCount257 = enum {
     I256,
 };
 
-test "enum sizes" {
-    comptime {
-        try expect(@sizeOf(ValueCount1) == 0);
-        try expect(@sizeOf(ValueCount2) == 1);
-        try expect(@sizeOf(ValueCount256) == 1);
-        try expect(@sizeOf(ValueCount257) == 2);
-    }
-}
+const Small2 = enum(u2) { One, Two };
+const Small = enum(u2) { One, Two, Three, Four };
 
-const Small2 = enum(u2) {
-    One,
-    Two,
-};
-const Small = enum(u2) {
-    One,
-    Two,
-    Three,
-    Four,
-};
-
-test "set enum tag type" {
-    {
-        var x = Small.One;
-        x = Small.Two;
-        comptime try expect(Tag(Small) == u2);
-    }
-    {
-        var x = Small2.One;
-        x = Small2.Two;
-        comptime try expect(Tag(Small2) == u2);
-    }
-}
-
-const A = enum(u3) {
-    One,
-    Two,
-    Three,
-    Four,
-    One2,
-    Two2,
-    Three2,
-    Four2,
-};
-
-const B = enum(u3) {
-    One3,
-    Two3,
-    Three3,
-    Four3,
-    One23,
-    Two23,
-    Three23,
-    Four23,
-};
-
-const C = enum(u2) {
-    One4,
-    Two4,
-    Three4,
-    Four4,
-};
+const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 };
+const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 };
+const C = enum(u2) { One4, Two4, Three4, Four4 };
 
 const BitFieldOfEnums = packed struct {
     a: A,
@@ -850,21 +609,6 @@ const bit_field_1 = BitFieldOfEnums{
     .c = C.Four4,
 };
 
-test "bit field access with enum fields" {
-    var data = bit_field_1;
-    try expect(getA(&data) == A.Two);
-    try expect(getB(&data) == B.Three3);
-    try expect(getC(&data) == C.Four4);
-    comptime try expect(@sizeOf(BitFieldOfEnums) == 1);
-
-    data.b = B.Four3;
-    try expect(data.b == B.Four3);
-
-    data.a = A.Three;
-    try expect(data.a == A.Three);
-    try expect(data.b == B.Four3);
-}
-
 fn getA(data: *const BitFieldOfEnums) A {
     return data.a;
 }
@@ -877,15 +621,6 @@ fn getC(data: *const BitFieldOfEnums) C {
     return data.c;
 }
 
-test "casting enum to its tag type" {
-    try testCastEnumTag(Small2.Two);
-    comptime try testCastEnumTag(Small2.Two);
-}
-
-fn testCastEnumTag(value: Small2) !void {
-    try expect(@enumToInt(value) == 1);
-}
-
 const MultipleChoice = enum(u32) {
     A = 20,
     B = 40,
@@ -893,21 +628,6 @@ const MultipleChoice = enum(u32) {
     D = 1000,
 };
 
-test "enum with specified tag values" {
-    try testEnumWithSpecifiedTagValues(MultipleChoice.C);
-    comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C);
-}
-
-fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {
-    try expect(@enumToInt(x) == 60);
-    try expect(1234 == switch (x) {
-        MultipleChoice.A => 1,
-        MultipleChoice.B => 2,
-        MultipleChoice.C => @as(u32, 1234),
-        MultipleChoice.D => 4,
-    });
-}
-
 const MultipleChoice2 = enum(u32) {
     Unspecified1,
     A = 20,
@@ -920,34 +640,7 @@ const MultipleChoice2 = enum(u32) {
     Unspecified5,
 };
 
-test "enum with specified and unspecified tag values" {
-    try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
-    comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
-}
-
-fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
-    try expect(@enumToInt(x) == 1000);
-    try expect(1234 == switch (x) {
-        MultipleChoice2.A => 1,
-        MultipleChoice2.B => 2,
-        MultipleChoice2.C => 3,
-        MultipleChoice2.D => @as(u32, 1234),
-        MultipleChoice2.Unspecified1 => 5,
-        MultipleChoice2.Unspecified2 => 6,
-        MultipleChoice2.Unspecified3 => 7,
-        MultipleChoice2.Unspecified4 => 8,
-        MultipleChoice2.Unspecified5 => 9,
-    });
-}
-
-test "cast integer literal to enum" {
-    try expect(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1);
-    try expect(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B);
-}
-
-const EnumWithOneMember = enum {
-    Eof,
-};
+const EnumWithOneMember = enum { Eof };
 
 fn doALoopThing(id: EnumWithOneMember) void {
     while (true) {
@@ -958,20 +651,7 @@ fn doALoopThing(id: EnumWithOneMember) void {
     }
 }
 
-test "comparison operator on enum with one member is comptime known" {
-    doALoopThing(EnumWithOneMember.Eof);
-}
-
-const State = enum {
-    Start,
-};
-test "switch on enum with one member is comptime known" {
-    var state = State.Start;
-    switch (state) {
-        State.Start => return,
-    }
-    @compileError("analysis should not reach here");
-}
+const State = enum { Start };
 
 const EnumWithTagValues = enum(u4) {
     A = 1 << 0,
@@ -979,24 +659,22 @@ const EnumWithTagValues = enum(u4) {
     C = 1 << 2,
     D = 1 << 3,
 };
-test "enum with tag values don't require parens" {
-    try expect(@enumToInt(EnumWithTagValues.C) == 0b0100);
-}
 
-test "enum with 1 field but explicit tag type should still have the tag type" {
-    const Enum = enum(u8) {
-        B = 2,
-    };
-    comptime try expect(@sizeOf(Enum) == @sizeOf(u8));
+test "enum to int" {
+    try shouldEqual(Number.Zero, 0);
+    try shouldEqual(Number.One, 1);
+    try shouldEqual(Number.Two, 2);
+    try shouldEqual(Number.Three, 3);
+    try shouldEqual(Number.Four, 4);
 }
 
-test "tag name with assigned enum values" {
-    const LocalFoo = enum(u8) {
-        A = 1,
-        B = 0,
-    };
-    var b = LocalFoo.B;
-    try expect(mem.eql(u8, @tagName(b), "B"));
+test "enum sizes" {
+    comptime {
+        try expect(@sizeOf(ValueCount1) == 0);
+        try expect(@sizeOf(ValueCount2) == 1);
+        try expect(@sizeOf(ValueCount256) == 1);
+        try expect(@sizeOf(ValueCount257) == 2);
+    }
 }
 
 test "enum literal equality" {
@@ -1009,11 +687,7 @@ test "enum literal equality" {
 }
 
 test "enum literal cast to enum" {
-    const Color = enum {
-        Auto,
-        Off,
-        On,
-    };
+    const Color = enum { Auto, Off, On };
 
     var color1: Color = .Auto;
     var color2 = Color.Auto;
@@ -1021,147 +695,8 @@ test "enum literal cast to enum" {
 }
 
 test "peer type resolution with enum literal" {
-    const Items = enum {
-        one,
-        two,
-    };
+    const Items = enum { one, two };
 
     try expect(Items.two == .two);
     try expect(.two == Items.two);
 }
-
-test "enum literal in array literal" {
-    const Items = enum {
-        one,
-        two,
-    };
-
-    const array = [_]Items{
-        .one,
-        .two,
-    };
-
-    try expect(array[0] == .one);
-    try expect(array[1] == .two);
-}
-
-test "signed integer as enum tag" {
-    const SignedEnum = enum(i2) {
-        A0 = -1,
-        A1 = 0,
-        A2 = 1,
-    };
-
-    try expect(@enumToInt(SignedEnum.A0) == -1);
-    try expect(@enumToInt(SignedEnum.A1) == 0);
-    try expect(@enumToInt(SignedEnum.A2) == 1);
-}
-
-test "enum value allocation" {
-    const LargeEnum = enum(u32) {
-        A0 = 0x80000000,
-        A1,
-        A2,
-    };
-
-    try expect(@enumToInt(LargeEnum.A0) == 0x80000000);
-    try expect(@enumToInt(LargeEnum.A1) == 0x80000001);
-    try expect(@enumToInt(LargeEnum.A2) == 0x80000002);
-}
-
-test "enum literal casting to tagged union" {
-    const Arch = union(enum) {
-        x86_64,
-        arm: Arm32,
-
-        const Arm32 = enum {
-            v8_5a,
-            v8_4a,
-        };
-    };
-
-    var t = true;
-    var x: Arch = .x86_64;
-    var y = if (t) x else .x86_64;
-    switch (y) {
-        .x86_64 => {},
-        else => @panic("fail"),
-    }
-}
-
-test "enum with one member and custom tag type" {
-    const E = enum(u2) {
-        One,
-    };
-    try expect(@enumToInt(E.One) == 0);
-    const E2 = enum(u2) {
-        One = 2,
-    };
-    try expect(@enumToInt(E2.One) == 2);
-}
-
-test "enum literal casting to optional" {
-    var bar: ?Bar = undefined;
-    bar = .B;
-
-    try expect(bar.? == Bar.B);
-}
-
-test "enum literal casting to error union with payload enum" {
-    var bar: error{B}!Bar = undefined;
-    bar = .B; // should never cast to the error set
-
-    try expect((try bar) == Bar.B);
-}
-
-test "enum with one member and u1 tag type @enumToInt" {
-    const Enum = enum(u1) {
-        Test,
-    };
-    try expect(@enumToInt(Enum.Test) == 0);
-}
-
-test "enum with comptime_int tag type" {
-    const Enum = enum(comptime_int) {
-        One = 3,
-        Two = 2,
-        Three = 1,
-    };
-    comptime try expect(Tag(Enum) == comptime_int);
-}
-
-test "enum with one member default to u0 tag type" {
-    const E0 = enum {
-        X,
-    };
-    comptime try expect(Tag(E0) == u0);
-}
-
-test "tagName on enum literals" {
-    try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
-    comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
-}
-
-test "method call on an enum" {
-    const S = struct {
-        const E = enum {
-            one,
-            two,
-
-            fn method(self: *E) bool {
-                return self.* == .two;
-            }
-
-            fn generic_method(self: *E, foo: anytype) bool {
-                return self.* == .two and foo == bool;
-            }
-        };
-        fn doTheTest() !void {
-            var e = E.two;
-            try expect(e.method());
-            try expect(e.generic_method(bool));
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
test/behavior/enum_stage1.zig
@@ -0,0 +1,1058 @@
+const expect = @import("std").testing.expect;
+const mem = @import("std").mem;
+const Tag = @import("std").meta.Tag;
+
+test "non-exhaustive enum" {
+    const S = struct {
+        const E = enum(u8) {
+            a,
+            b,
+            _,
+        };
+        fn doTheTest(y: u8) !void {
+            var e: E = .b;
+            try expect(switch (e) {
+                .a => false,
+                .b => true,
+                _ => false,
+            });
+            e = @intToEnum(E, 12);
+            try expect(switch (e) {
+                .a => false,
+                .b => false,
+                _ => true,
+            });
+
+            try expect(switch (e) {
+                .a => false,
+                .b => false,
+                else => true,
+            });
+            e = .b;
+            try expect(switch (e) {
+                .a => false,
+                else => true,
+            });
+
+            try expect(@typeInfo(E).Enum.fields.len == 2);
+            e = @intToEnum(E, 12);
+            try expect(@enumToInt(e) == 12);
+            e = @intToEnum(E, y);
+            try expect(@enumToInt(e) == 52);
+            try expect(@typeInfo(E).Enum.is_exhaustive == false);
+        }
+    };
+    try S.doTheTest(52);
+    comptime try S.doTheTest(52);
+}
+
+test "empty non-exhaustive enum" {
+    const S = struct {
+        const E = enum(u8) {
+            _,
+        };
+        fn doTheTest(y: u8) !void {
+            var e = @intToEnum(E, y);
+            try expect(switch (e) {
+                _ => true,
+            });
+            try expect(@enumToInt(e) == y);
+
+            try expect(@typeInfo(E).Enum.fields.len == 0);
+            try expect(@typeInfo(E).Enum.is_exhaustive == false);
+        }
+    };
+    try S.doTheTest(42);
+    comptime try S.doTheTest(42);
+}
+
+test "single field non-exhaustive enum" {
+    const S = struct {
+        const E = enum(u8) { a, _ };
+        fn doTheTest(y: u8) !void {
+            var e: E = .a;
+            try expect(switch (e) {
+                .a => true,
+                _ => false,
+            });
+            e = @intToEnum(E, 12);
+            try expect(switch (e) {
+                .a => false,
+                _ => true,
+            });
+
+            try expect(switch (e) {
+                .a => false,
+                else => true,
+            });
+            e = .a;
+            try expect(switch (e) {
+                .a => true,
+                else => false,
+            });
+
+            try expect(@enumToInt(@intToEnum(E, y)) == y);
+            try expect(@typeInfo(E).Enum.fields.len == 1);
+            try expect(@typeInfo(E).Enum.is_exhaustive == false);
+        }
+    };
+    try S.doTheTest(23);
+    comptime try S.doTheTest(23);
+}
+
+test "enum type" {
+    const foo1 = Foo{ .One = 13 };
+    const foo2 = Foo{
+        .Two = Point{
+            .x = 1234,
+            .y = 5678,
+        },
+    };
+    try expect(foo1.One == 13);
+    try expect(foo2.Two.x == 1234 and foo2.Two.y == 5678);
+    const bar = Bar.B;
+
+    try expect(bar == Bar.B);
+    try expect(@typeInfo(Foo).Union.fields.len == 3);
+    try expect(@typeInfo(Bar).Enum.fields.len == 4);
+    try expect(@sizeOf(Foo) == @sizeOf(FooNoVoid));
+    try expect(@sizeOf(Bar) == 1);
+}
+
+test "enum as return value" {
+    switch (returnAnInt(13)) {
+        Foo.One => |value| try expect(value == 13),
+        else => unreachable,
+    }
+}
+
+const Point = struct {
+    x: u64,
+    y: u64,
+};
+const Foo = union(enum) {
+    One: i32,
+    Two: Point,
+    Three: void,
+};
+const FooNoVoid = union(enum) {
+    One: i32,
+    Two: Point,
+};
+const Bar = enum {
+    A,
+    B,
+    C,
+    D,
+};
+
+fn returnAnInt(x: i32) Foo {
+    return Foo{ .One = x };
+}
+
+test "constant enum with payload" {
+    var empty = AnEnumWithPayload{ .Empty = {} };
+    var full = AnEnumWithPayload{ .Full = 13 };
+    shouldBeEmpty(empty);
+    shouldBeNotEmpty(full);
+}
+
+fn shouldBeEmpty(x: AnEnumWithPayload) void {
+    switch (x) {
+        AnEnumWithPayload.Empty => {},
+        else => unreachable,
+    }
+}
+
+fn shouldBeNotEmpty(x: AnEnumWithPayload) void {
+    switch (x) {
+        AnEnumWithPayload.Empty => unreachable,
+        else => {},
+    }
+}
+
+const AnEnumWithPayload = union(enum) {
+    Empty: void,
+    Full: i32,
+};
+
+const Number = enum { Zero, One, Two, Three, Four };
+
+fn shouldEqual(n: Number, expected: u3) !void {
+    try expect(@enumToInt(n) == expected);
+}
+
+test "int to enum" {
+    try testIntToEnumEval(3);
+}
+fn testIntToEnumEval(x: i32) !void {
+    try expect(@intToEnum(IntToEnumNumber, x) == IntToEnumNumber.Three);
+}
+const IntToEnumNumber = enum { Zero, One, Two, Three, Four };
+
+test "@tagName" {
+    try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
+    comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
+}
+
+test "@tagName non-exhaustive enum" {
+    try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+    comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+}
+
+fn testEnumTagNameBare(n: anytype) []const u8 {
+    return @tagName(n);
+}
+
+const BareNumber = enum { One, Two, Three };
+
+const NonExhaustive = enum(u8) { A, B, _ };
+
+test "enum alignment" {
+    comptime {
+        try expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8));
+        try expect(@alignOf(AlignTestEnum) >= @alignOf(u64));
+    }
+}
+
+const AlignTestEnum = union(enum) {
+    A: [9]u8,
+    B: u64,
+};
+
+const ValueCount1 = enum {
+    I0,
+};
+const ValueCount2 = enum {
+    I0,
+    I1,
+};
+const ValueCount256 = enum {
+    I0,
+    I1,
+    I2,
+    I3,
+    I4,
+    I5,
+    I6,
+    I7,
+    I8,
+    I9,
+    I10,
+    I11,
+    I12,
+    I13,
+    I14,
+    I15,
+    I16,
+    I17,
+    I18,
+    I19,
+    I20,
+    I21,
+    I22,
+    I23,
+    I24,
+    I25,
+    I26,
+    I27,
+    I28,
+    I29,
+    I30,
+    I31,
+    I32,
+    I33,
+    I34,
+    I35,
+    I36,
+    I37,
+    I38,
+    I39,
+    I40,
+    I41,
+    I42,
+    I43,
+    I44,
+    I45,
+    I46,
+    I47,
+    I48,
+    I49,
+    I50,
+    I51,
+    I52,
+    I53,
+    I54,
+    I55,
+    I56,
+    I57,
+    I58,
+    I59,
+    I60,
+    I61,
+    I62,
+    I63,
+    I64,
+    I65,
+    I66,
+    I67,
+    I68,
+    I69,
+    I70,
+    I71,
+    I72,
+    I73,
+    I74,
+    I75,
+    I76,
+    I77,
+    I78,
+    I79,
+    I80,
+    I81,
+    I82,
+    I83,
+    I84,
+    I85,
+    I86,
+    I87,
+    I88,
+    I89,
+    I90,
+    I91,
+    I92,
+    I93,
+    I94,
+    I95,
+    I96,
+    I97,
+    I98,
+    I99,
+    I100,
+    I101,
+    I102,
+    I103,
+    I104,
+    I105,
+    I106,
+    I107,
+    I108,
+    I109,
+    I110,
+    I111,
+    I112,
+    I113,
+    I114,
+    I115,
+    I116,
+    I117,
+    I118,
+    I119,
+    I120,
+    I121,
+    I122,
+    I123,
+    I124,
+    I125,
+    I126,
+    I127,
+    I128,
+    I129,
+    I130,
+    I131,
+    I132,
+    I133,
+    I134,
+    I135,
+    I136,
+    I137,
+    I138,
+    I139,
+    I140,
+    I141,
+    I142,
+    I143,
+    I144,
+    I145,
+    I146,
+    I147,
+    I148,
+    I149,
+    I150,
+    I151,
+    I152,
+    I153,
+    I154,
+    I155,
+    I156,
+    I157,
+    I158,
+    I159,
+    I160,
+    I161,
+    I162,
+    I163,
+    I164,
+    I165,
+    I166,
+    I167,
+    I168,
+    I169,
+    I170,
+    I171,
+    I172,
+    I173,
+    I174,
+    I175,
+    I176,
+    I177,
+    I178,
+    I179,
+    I180,
+    I181,
+    I182,
+    I183,
+    I184,
+    I185,
+    I186,
+    I187,
+    I188,
+    I189,
+    I190,
+    I191,
+    I192,
+    I193,
+    I194,
+    I195,
+    I196,
+    I197,
+    I198,
+    I199,
+    I200,
+    I201,
+    I202,
+    I203,
+    I204,
+    I205,
+    I206,
+    I207,
+    I208,
+    I209,
+    I210,
+    I211,
+    I212,
+    I213,
+    I214,
+    I215,
+    I216,
+    I217,
+    I218,
+    I219,
+    I220,
+    I221,
+    I222,
+    I223,
+    I224,
+    I225,
+    I226,
+    I227,
+    I228,
+    I229,
+    I230,
+    I231,
+    I232,
+    I233,
+    I234,
+    I235,
+    I236,
+    I237,
+    I238,
+    I239,
+    I240,
+    I241,
+    I242,
+    I243,
+    I244,
+    I245,
+    I246,
+    I247,
+    I248,
+    I249,
+    I250,
+    I251,
+    I252,
+    I253,
+    I254,
+    I255,
+};
+const ValueCount257 = enum {
+    I0,
+    I1,
+    I2,
+    I3,
+    I4,
+    I5,
+    I6,
+    I7,
+    I8,
+    I9,
+    I10,
+    I11,
+    I12,
+    I13,
+    I14,
+    I15,
+    I16,
+    I17,
+    I18,
+    I19,
+    I20,
+    I21,
+    I22,
+    I23,
+    I24,
+    I25,
+    I26,
+    I27,
+    I28,
+    I29,
+    I30,
+    I31,
+    I32,
+    I33,
+    I34,
+    I35,
+    I36,
+    I37,
+    I38,
+    I39,
+    I40,
+    I41,
+    I42,
+    I43,
+    I44,
+    I45,
+    I46,
+    I47,
+    I48,
+    I49,
+    I50,
+    I51,
+    I52,
+    I53,
+    I54,
+    I55,
+    I56,
+    I57,
+    I58,
+    I59,
+    I60,
+    I61,
+    I62,
+    I63,
+    I64,
+    I65,
+    I66,
+    I67,
+    I68,
+    I69,
+    I70,
+    I71,
+    I72,
+    I73,
+    I74,
+    I75,
+    I76,
+    I77,
+    I78,
+    I79,
+    I80,
+    I81,
+    I82,
+    I83,
+    I84,
+    I85,
+    I86,
+    I87,
+    I88,
+    I89,
+    I90,
+    I91,
+    I92,
+    I93,
+    I94,
+    I95,
+    I96,
+    I97,
+    I98,
+    I99,
+    I100,
+    I101,
+    I102,
+    I103,
+    I104,
+    I105,
+    I106,
+    I107,
+    I108,
+    I109,
+    I110,
+    I111,
+    I112,
+    I113,
+    I114,
+    I115,
+    I116,
+    I117,
+    I118,
+    I119,
+    I120,
+    I121,
+    I122,
+    I123,
+    I124,
+    I125,
+    I126,
+    I127,
+    I128,
+    I129,
+    I130,
+    I131,
+    I132,
+    I133,
+    I134,
+    I135,
+    I136,
+    I137,
+    I138,
+    I139,
+    I140,
+    I141,
+    I142,
+    I143,
+    I144,
+    I145,
+    I146,
+    I147,
+    I148,
+    I149,
+    I150,
+    I151,
+    I152,
+    I153,
+    I154,
+    I155,
+    I156,
+    I157,
+    I158,
+    I159,
+    I160,
+    I161,
+    I162,
+    I163,
+    I164,
+    I165,
+    I166,
+    I167,
+    I168,
+    I169,
+    I170,
+    I171,
+    I172,
+    I173,
+    I174,
+    I175,
+    I176,
+    I177,
+    I178,
+    I179,
+    I180,
+    I181,
+    I182,
+    I183,
+    I184,
+    I185,
+    I186,
+    I187,
+    I188,
+    I189,
+    I190,
+    I191,
+    I192,
+    I193,
+    I194,
+    I195,
+    I196,
+    I197,
+    I198,
+    I199,
+    I200,
+    I201,
+    I202,
+    I203,
+    I204,
+    I205,
+    I206,
+    I207,
+    I208,
+    I209,
+    I210,
+    I211,
+    I212,
+    I213,
+    I214,
+    I215,
+    I216,
+    I217,
+    I218,
+    I219,
+    I220,
+    I221,
+    I222,
+    I223,
+    I224,
+    I225,
+    I226,
+    I227,
+    I228,
+    I229,
+    I230,
+    I231,
+    I232,
+    I233,
+    I234,
+    I235,
+    I236,
+    I237,
+    I238,
+    I239,
+    I240,
+    I241,
+    I242,
+    I243,
+    I244,
+    I245,
+    I246,
+    I247,
+    I248,
+    I249,
+    I250,
+    I251,
+    I252,
+    I253,
+    I254,
+    I255,
+    I256,
+};
+
+const Small2 = enum(u2) {
+    One,
+    Two,
+};
+const Small = enum(u2) {
+    One,
+    Two,
+    Three,
+    Four,
+};
+
+test "set enum tag type" {
+    {
+        var x = Small.One;
+        x = Small.Two;
+        comptime try expect(Tag(Small) == u2);
+    }
+    {
+        var x = Small2.One;
+        x = Small2.Two;
+        comptime try expect(Tag(Small2) == u2);
+    }
+}
+
+const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 };
+const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 };
+const C = enum(u2) { One4, Two4, Three4, Four4 };
+
+const BitFieldOfEnums = packed struct {
+    a: A,
+    b: B,
+    c: C,
+};
+
+const bit_field_1 = BitFieldOfEnums{
+    .a = A.Two,
+    .b = B.Three3,
+    .c = C.Four4,
+};
+
+test "bit field access with enum fields" {
+    var data = bit_field_1;
+    try expect(getA(&data) == A.Two);
+    try expect(getB(&data) == B.Three3);
+    try expect(getC(&data) == C.Four4);
+    comptime try expect(@sizeOf(BitFieldOfEnums) == 1);
+
+    data.b = B.Four3;
+    try expect(data.b == B.Four3);
+
+    data.a = A.Three;
+    try expect(data.a == A.Three);
+    try expect(data.b == B.Four3);
+}
+
+fn getA(data: *const BitFieldOfEnums) A {
+    return data.a;
+}
+
+fn getB(data: *const BitFieldOfEnums) B {
+    return data.b;
+}
+
+fn getC(data: *const BitFieldOfEnums) C {
+    return data.c;
+}
+
+test "casting enum to its tag type" {
+    try testCastEnumTag(Small2.Two);
+    comptime try testCastEnumTag(Small2.Two);
+}
+
+fn testCastEnumTag(value: Small2) !void {
+    try expect(@enumToInt(value) == 1);
+}
+
+const MultipleChoice = enum(u32) {
+    A = 20,
+    B = 40,
+    C = 60,
+    D = 1000,
+};
+
+test "enum with specified tag values" {
+    try testEnumWithSpecifiedTagValues(MultipleChoice.C);
+    comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C);
+}
+
+fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {
+    try expect(@enumToInt(x) == 60);
+    try expect(1234 == switch (x) {
+        MultipleChoice.A => 1,
+        MultipleChoice.B => 2,
+        MultipleChoice.C => @as(u32, 1234),
+        MultipleChoice.D => 4,
+    });
+}
+
+const MultipleChoice2 = enum(u32) {
+    Unspecified1,
+    A = 20,
+    Unspecified2,
+    B = 40,
+    Unspecified3,
+    C = 60,
+    Unspecified4,
+    D = 1000,
+    Unspecified5,
+};
+
+test "enum with specified and unspecified tag values" {
+    try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
+    comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
+}
+
+fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
+    try expect(@enumToInt(x) == 1000);
+    try expect(1234 == switch (x) {
+        MultipleChoice2.A => 1,
+        MultipleChoice2.B => 2,
+        MultipleChoice2.C => 3,
+        MultipleChoice2.D => @as(u32, 1234),
+        MultipleChoice2.Unspecified1 => 5,
+        MultipleChoice2.Unspecified2 => 6,
+        MultipleChoice2.Unspecified3 => 7,
+        MultipleChoice2.Unspecified4 => 8,
+        MultipleChoice2.Unspecified5 => 9,
+    });
+}
+
+test "cast integer literal to enum" {
+    try expect(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1);
+    try expect(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B);
+}
+
+const EnumWithOneMember = enum { Eof };
+
+fn doALoopThing(id: EnumWithOneMember) void {
+    while (true) {
+        if (id == EnumWithOneMember.Eof) {
+            break;
+        }
+        @compileError("above if condition should be comptime");
+    }
+}
+
+test "comparison operator on enum with one member is comptime known" {
+    doALoopThing(EnumWithOneMember.Eof);
+}
+
+const State = enum { Start };
+test "switch on enum with one member is comptime known" {
+    var state = State.Start;
+    switch (state) {
+        State.Start => return,
+    }
+    @compileError("analysis should not reach here");
+}
+
+const EnumWithTagValues = enum(u4) {
+    A = 1 << 0,
+    B = 1 << 1,
+    C = 1 << 2,
+    D = 1 << 3,
+};
+test "enum with tag values don't require parens" {
+    try expect(@enumToInt(EnumWithTagValues.C) == 0b0100);
+}
+
+test "enum with 1 field but explicit tag type should still have the tag type" {
+    const Enum = enum(u8) {
+        B = 2,
+    };
+    comptime try expect(@sizeOf(Enum) == @sizeOf(u8));
+}
+
+test "tag name with assigned enum values" {
+    const LocalFoo = enum(u8) {
+        A = 1,
+        B = 0,
+    };
+    var b = LocalFoo.B;
+    try expect(mem.eql(u8, @tagName(b), "B"));
+}
+
+test "enum literal in array literal" {
+    const Items = enum { one, two };
+    const array = [_]Items{ .one, .two };
+
+    try expect(array[0] == .one);
+    try expect(array[1] == .two);
+}
+
+test "signed integer as enum tag" {
+    const SignedEnum = enum(i2) {
+        A0 = -1,
+        A1 = 0,
+        A2 = 1,
+    };
+
+    try expect(@enumToInt(SignedEnum.A0) == -1);
+    try expect(@enumToInt(SignedEnum.A1) == 0);
+    try expect(@enumToInt(SignedEnum.A2) == 1);
+}
+
+test "enum value allocation" {
+    const LargeEnum = enum(u32) {
+        A0 = 0x80000000,
+        A1,
+        A2,
+    };
+
+    try expect(@enumToInt(LargeEnum.A0) == 0x80000000);
+    try expect(@enumToInt(LargeEnum.A1) == 0x80000001);
+    try expect(@enumToInt(LargeEnum.A2) == 0x80000002);
+}
+
+test "enum literal casting to tagged union" {
+    const Arch = union(enum) {
+        x86_64,
+        arm: Arm32,
+
+        const Arm32 = enum {
+            v8_5a,
+            v8_4a,
+        };
+    };
+
+    var t = true;
+    var x: Arch = .x86_64;
+    var y = if (t) x else .x86_64;
+    switch (y) {
+        .x86_64 => {},
+        else => @panic("fail"),
+    }
+}
+
+test "enum with one member and custom tag type" {
+    const E = enum(u2) {
+        One,
+    };
+    try expect(@enumToInt(E.One) == 0);
+    const E2 = enum(u2) {
+        One = 2,
+    };
+    try expect(@enumToInt(E2.One) == 2);
+}
+
+test "enum literal casting to optional" {
+    var bar: ?Bar = undefined;
+    bar = .B;
+
+    try expect(bar.? == Bar.B);
+}
+
+test "enum literal casting to error union with payload enum" {
+    var bar: error{B}!Bar = undefined;
+    bar = .B; // should never cast to the error set
+
+    try expect((try bar) == Bar.B);
+}
+
+test "enum with one member and u1 tag type @enumToInt" {
+    const Enum = enum(u1) {
+        Test,
+    };
+    try expect(@enumToInt(Enum.Test) == 0);
+}
+
+test "enum with comptime_int tag type" {
+    const Enum = enum(comptime_int) {
+        One = 3,
+        Two = 2,
+        Three = 1,
+    };
+    comptime try expect(Tag(Enum) == comptime_int);
+}
+
+test "enum with one member default to u0 tag type" {
+    const E0 = enum { X };
+    comptime try expect(Tag(E0) == u0);
+}
+
+test "tagName on enum literals" {
+    try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
+    comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
+}
+
+test "method call on an enum" {
+    const S = struct {
+        const E = enum {
+            one,
+            two,
+
+            fn method(self: *E) bool {
+                return self.* == .two;
+            }
+
+            fn generic_method(self: *E, foo: anytype) bool {
+                return self.* == .two and foo == bool;
+            }
+        };
+        fn doTheTest() !void {
+            var e = E.two;
+            try expect(e.method());
+            try expect(e.generic_method(bool));
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
test/behavior/for.zig
@@ -2,179 +2,3 @@ const std = @import("std");
 const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
 const mem = std.mem;
-
-test "continue in for loop" {
-    const array = [_]i32{
-        1,
-        2,
-        3,
-        4,
-        5,
-    };
-    var sum: i32 = 0;
-    for (array) |x| {
-        sum += x;
-        if (x < 3) {
-            continue;
-        }
-        break;
-    }
-    if (sum != 6) unreachable;
-}
-
-test "for loop with pointer elem var" {
-    const source = "abcdefg";
-    var target: [source.len]u8 = undefined;
-    mem.copy(u8, target[0..], source);
-    mangleString(target[0..]);
-    try expect(mem.eql(u8, &target, "bcdefgh"));
-
-    for (source) |*c, i| {
-        _ = i;
-        try expect(@TypeOf(c) == *const u8);
-    }
-    for (target) |*c, i| {
-        _ = i;
-        try expect(@TypeOf(c) == *u8);
-    }
-}
-
-fn mangleString(s: []u8) void {
-    for (s) |*c| {
-        c.* += 1;
-    }
-}
-
-test "basic for loop" {
-    const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
-
-    var buffer: [expected_result.len]u8 = undefined;
-    var buf_index: usize = 0;
-
-    const array = [_]u8{ 9, 8, 7, 6 };
-    for (array) |item| {
-        buffer[buf_index] = item;
-        buf_index += 1;
-    }
-    for (array) |item, index| {
-        _ = item;
-        buffer[buf_index] = @intCast(u8, index);
-        buf_index += 1;
-    }
-    const array_ptr = &array;
-    for (array_ptr) |item| {
-        buffer[buf_index] = item;
-        buf_index += 1;
-    }
-    for (array_ptr) |item, index| {
-        _ = item;
-        buffer[buf_index] = @intCast(u8, index);
-        buf_index += 1;
-    }
-    const unknown_size: []const u8 = &array;
-    for (unknown_size) |item| {
-        buffer[buf_index] = item;
-        buf_index += 1;
-    }
-    for (unknown_size) |_, index| {
-        buffer[buf_index] = @intCast(u8, index);
-        buf_index += 1;
-    }
-
-    try expect(mem.eql(u8, buffer[0..buf_index], &expected_result));
-}
-
-test "break from outer for loop" {
-    try testBreakOuter();
-    comptime try testBreakOuter();
-}
-
-fn testBreakOuter() !void {
-    var array = "aoeu";
-    var count: usize = 0;
-    outer: for (array) |_| {
-        for (array) |_| {
-            count += 1;
-            break :outer;
-        }
-    }
-    try expect(count == 1);
-}
-
-test "continue outer for loop" {
-    try testContinueOuter();
-    comptime try testContinueOuter();
-}
-
-fn testContinueOuter() !void {
-    var array = "aoeu";
-    var counter: usize = 0;
-    outer: for (array) |_| {
-        for (array) |_| {
-            counter += 1;
-            continue :outer;
-        }
-    }
-    try expect(counter == array.len);
-}
-
-test "2 break statements and an else" {
-    const S = struct {
-        fn entry(t: bool, f: bool) !void {
-            var buf: [10]u8 = undefined;
-            var ok = false;
-            ok = for (buf) |item| {
-                _ = item;
-                if (f) break false;
-                if (t) break true;
-            } else false;
-            try expect(ok);
-        }
-    };
-    try S.entry(true, false);
-    comptime try S.entry(true, false);
-}
-
-test "for with null and T peer types and inferred result location type" {
-    const S = struct {
-        fn doTheTest(slice: []const u8) !void {
-            if (for (slice) |item| {
-                if (item == 10) {
-                    break item;
-                }
-            } else null) |v| {
-                _ = v;
-                @panic("fail");
-            }
-        }
-    };
-    try S.doTheTest(&[_]u8{ 1, 2 });
-    comptime try S.doTheTest(&[_]u8{ 1, 2 });
-}
-
-test "for copies its payload" {
-    const S = struct {
-        fn doTheTest() !void {
-            var x = [_]usize{ 1, 2, 3 };
-            for (x) |value, i| {
-                // Modify the original array
-                x[i] += 99;
-                try expectEqual(value, i + 1);
-            }
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "for on slice with allowzero ptr" {
-    const S = struct {
-        fn doTheTest(slice: []const u8) !void {
-            var ptr = @ptrCast([*]allowzero const u8, slice.ptr)[0..slice.len];
-            for (ptr) |x, i| try expect(x == i + 1);
-            for (ptr) |*x, i| try expect(x.* == i + 1);
-        }
-    };
-    try S.doTheTest(&[_]u8{ 1, 2, 3, 4 });
-    comptime try S.doTheTest(&[_]u8{ 1, 2, 3, 4 });
-}
test/behavior/for_stage1.zig
@@ -0,0 +1,185 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const mem = std.mem;
+
+test "continue in for loop" {
+    const array = [_]i32{ 1, 2, 3, 4, 5 };
+    var sum: i32 = 0;
+    for (array) |x| {
+        sum += x;
+        if (x < 3) {
+            continue;
+        }
+        break;
+    }
+    if (sum != 6) unreachable;
+}
+
+test "for loop with pointer elem var" {
+    const source = "abcdefg";
+    var target: [source.len]u8 = undefined;
+    mem.copy(u8, target[0..], source);
+    mangleString(target[0..]);
+    try expect(mem.eql(u8, &target, "bcdefgh"));
+
+    for (source) |*c, i| {
+        _ = i;
+        try expect(@TypeOf(c) == *const u8);
+    }
+    for (target) |*c, i| {
+        _ = i;
+        try expect(@TypeOf(c) == *u8);
+    }
+}
+
+fn mangleString(s: []u8) void {
+    for (s) |*c| {
+        c.* += 1;
+    }
+}
+
+test "basic for loop" {
+    const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
+
+    var buffer: [expected_result.len]u8 = undefined;
+    var buf_index: usize = 0;
+
+    const array = [_]u8{ 9, 8, 7, 6 };
+    for (array) |item| {
+        buffer[buf_index] = item;
+        buf_index += 1;
+    }
+    for (array) |item, index| {
+        _ = item;
+        buffer[buf_index] = @intCast(u8, index);
+        buf_index += 1;
+    }
+    const array_ptr = &array;
+    for (array_ptr) |item| {
+        buffer[buf_index] = item;
+        buf_index += 1;
+    }
+    for (array_ptr) |item, index| {
+        _ = item;
+        buffer[buf_index] = @intCast(u8, index);
+        buf_index += 1;
+    }
+    const unknown_size: []const u8 = &array;
+    for (unknown_size) |item| {
+        buffer[buf_index] = item;
+        buf_index += 1;
+    }
+    for (unknown_size) |_, index| {
+        buffer[buf_index] = @intCast(u8, index);
+        buf_index += 1;
+    }
+
+    try expect(mem.eql(u8, buffer[0..buf_index], &expected_result));
+}
+
+test "break from outer for loop" {
+    try testBreakOuter();
+    comptime try testBreakOuter();
+}
+
+fn testBreakOuter() !void {
+    var array = "aoeu";
+    var count: usize = 0;
+    outer: for (array) |_| {
+        for (array) |_| {
+            count += 1;
+            break :outer;
+        }
+    }
+    try expect(count == 1);
+}
+
+test "continue outer for loop" {
+    try testContinueOuter();
+    comptime try testContinueOuter();
+}
+
+fn testContinueOuter() !void {
+    var array = "aoeu";
+    var counter: usize = 0;
+    outer: for (array) |_| {
+        for (array) |_| {
+            counter += 1;
+            continue :outer;
+        }
+    }
+    try expect(counter == array.len);
+}
+
+test "2 break statements and an else" {
+    const S = struct {
+        fn entry(t: bool, f: bool) !void {
+            var buf: [10]u8 = undefined;
+            var ok = false;
+            ok = for (buf) |item| {
+                _ = item;
+                if (f) break false;
+                if (t) break true;
+            } else false;
+            try expect(ok);
+        }
+    };
+    try S.entry(true, false);
+    comptime try S.entry(true, false);
+}
+
+test "for with null and T peer types and inferred result location type" {
+    const S = struct {
+        fn doTheTest(slice: []const u8) !void {
+            if (for (slice) |item| {
+                if (item == 10) {
+                    break item;
+                }
+            } else null) |v| {
+                _ = v;
+                @panic("fail");
+            }
+        }
+    };
+    try S.doTheTest(&[_]u8{ 1, 2 });
+    comptime try S.doTheTest(&[_]u8{ 1, 2 });
+}
+
+test "for copies its payload" {
+    const S = struct {
+        fn doTheTest() !void {
+            var x = [_]usize{ 1, 2, 3 };
+            for (x) |value, i| {
+                // Modify the original array
+                x[i] += 99;
+                try expectEqual(value, i + 1);
+            }
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "for on slice with allowzero ptr" {
+    const S = struct {
+        fn doTheTest(slice: []const u8) !void {
+            var ptr = @ptrCast([*]allowzero const u8, slice.ptr)[0..slice.len];
+            for (ptr) |x, i| try expect(x == i + 1);
+            for (ptr) |*x, i| try expect(x.* == i + 1);
+        }
+    };
+    try S.doTheTest(&[_]u8{ 1, 2, 3, 4 });
+    comptime try S.doTheTest(&[_]u8{ 1, 2, 3, 4 });
+}
+
+test "ignore lval with underscore (for loop)" {
+    for ([_]void{}) |_, i| {
+        _ = i;
+        for ([_]void{}) |_, j| {
+            _ = j;
+            break;
+        }
+        break;
+    }
+}
test/behavior/if.zig
@@ -73,3 +73,18 @@ test "const result loc, runtime if cond, else unreachable" {
     const x = if (t) Num.Two else unreachable;
     try expect(x == .Two);
 }
+
+test "if copies its payload" {
+    const S = struct {
+        fn doTheTest() !void {
+            var tmp: ?i32 = 10;
+            if (tmp) |value| {
+                // Modify the original variable
+                tmp = null;
+                try expect(value == 10);
+            } else unreachable;
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
test/behavior/if_stage1.zig
@@ -17,18 +17,3 @@ test "if prongs cast to expected type instead of peer type resolution" {
     try S.doTheTest(false);
     comptime try S.doTheTest(false);
 }
-
-test "while copies its payload" {
-    const S = struct {
-        fn doTheTest() !void {
-            var tmp: ?i32 = 10;
-            if (tmp) |value| {
-                // Modify the original variable
-                tmp = null;
-                try expectEqual(@as(i32, 10), value);
-            } else unreachable;
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
test/behavior/switch.zig
@@ -2,548 +2,3 @@ const std = @import("std");
 const expect = std.testing.expect;
 const expectError = std.testing.expectError;
 const expectEqual = std.testing.expectEqual;
-
-test "switch with numbers" {
-    try testSwitchWithNumbers(13);
-}
-
-fn testSwitchWithNumbers(x: u32) !void {
-    const result = switch (x) {
-        1, 2, 3, 4...8 => false,
-        13 => true,
-        else => false,
-    };
-    try expect(result);
-}
-
-test "switch with all ranges" {
-    try expect(testSwitchWithAllRanges(50, 3) == 1);
-    try expect(testSwitchWithAllRanges(101, 0) == 2);
-    try expect(testSwitchWithAllRanges(300, 5) == 3);
-    try expect(testSwitchWithAllRanges(301, 6) == 6);
-}
-
-fn testSwitchWithAllRanges(x: u32, y: u32) u32 {
-    return switch (x) {
-        0...100 => 1,
-        101...200 => 2,
-        201...300 => 3,
-        else => y,
-    };
-}
-
-test "implicit comptime switch" {
-    const x = 3 + 4;
-    const result = switch (x) {
-        3 => 10,
-        4 => 11,
-        5, 6 => 12,
-        7, 8 => 13,
-        else => 14,
-    };
-
-    comptime {
-        try expect(result + 1 == 14);
-    }
-}
-
-test "switch on enum" {
-    const fruit = Fruit.Orange;
-    nonConstSwitchOnEnum(fruit);
-}
-const Fruit = enum {
-    Apple,
-    Orange,
-    Banana,
-};
-fn nonConstSwitchOnEnum(fruit: Fruit) void {
-    switch (fruit) {
-        Fruit.Apple => unreachable,
-        Fruit.Orange => {},
-        Fruit.Banana => unreachable,
-    }
-}
-
-test "switch statement" {
-    try nonConstSwitch(SwitchStatementFoo.C);
-}
-fn nonConstSwitch(foo: SwitchStatementFoo) !void {
-    const val = switch (foo) {
-        SwitchStatementFoo.A => @as(i32, 1),
-        SwitchStatementFoo.B => 2,
-        SwitchStatementFoo.C => 3,
-        SwitchStatementFoo.D => 4,
-    };
-    try expect(val == 3);
-}
-const SwitchStatementFoo = enum {
-    A,
-    B,
-    C,
-    D,
-};
-
-test "switch prong with variable" {
-    try switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 });
-    try switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 });
-    try switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} });
-}
-const SwitchProngWithVarEnum = union(enum) {
-    One: i32,
-    Two: f32,
-    Meh: void,
-};
-fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void {
-    switch (a) {
-        SwitchProngWithVarEnum.One => |x| {
-            try expect(x == 13);
-        },
-        SwitchProngWithVarEnum.Two => |x| {
-            try expect(x == 13.0);
-        },
-        SwitchProngWithVarEnum.Meh => |x| {
-            const v: void = x;
-            _ = v;
-        },
-    }
-}
-
-test "switch on enum using pointer capture" {
-    try testSwitchEnumPtrCapture();
-    comptime try testSwitchEnumPtrCapture();
-}
-
-fn testSwitchEnumPtrCapture() !void {
-    var value = SwitchProngWithVarEnum{ .One = 1234 };
-    switch (value) {
-        SwitchProngWithVarEnum.One => |*x| x.* += 1,
-        else => unreachable,
-    }
-    switch (value) {
-        SwitchProngWithVarEnum.One => |x| try expect(x == 1235),
-        else => unreachable,
-    }
-}
-
-test "switch with multiple expressions" {
-    const x = switch (returnsFive()) {
-        1, 2, 3 => 1,
-        4, 5, 6 => 2,
-        else => @as(i32, 3),
-    };
-    try expect(x == 2);
-}
-fn returnsFive() i32 {
-    return 5;
-}
-
-const Number = union(enum) {
-    One: u64,
-    Two: u8,
-    Three: f32,
-};
-
-const number = Number{ .Three = 1.23 };
-
-fn returnsFalse() bool {
-    switch (number) {
-        Number.One => |x| return x > 1234,
-        Number.Two => |x| return x == 'a',
-        Number.Three => |x| return x > 12.34,
-    }
-}
-test "switch on const enum with var" {
-    try expect(!returnsFalse());
-}
-
-test "switch on type" {
-    try expect(trueIfBoolFalseOtherwise(bool));
-    try expect(!trueIfBoolFalseOtherwise(i32));
-}
-
-fn trueIfBoolFalseOtherwise(comptime T: type) bool {
-    return switch (T) {
-        bool => true,
-        else => false,
-    };
-}
-
-test "switch handles all cases of number" {
-    try testSwitchHandleAllCases();
-    comptime try testSwitchHandleAllCases();
-}
-
-fn testSwitchHandleAllCases() !void {
-    try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
-    try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
-    try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
-    try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
-
-    try expect(testSwitchHandleAllCasesRange(100) == 0);
-    try expect(testSwitchHandleAllCasesRange(200) == 1);
-    try expect(testSwitchHandleAllCasesRange(201) == 2);
-    try expect(testSwitchHandleAllCasesRange(202) == 4);
-    try expect(testSwitchHandleAllCasesRange(230) == 3);
-}
-
-fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
-    return switch (x) {
-        0 => @as(u2, 3),
-        1 => 2,
-        2 => 1,
-        3 => 0,
-    };
-}
-
-fn testSwitchHandleAllCasesRange(x: u8) u8 {
-    return switch (x) {
-        0...100 => @as(u8, 0),
-        101...200 => 1,
-        201, 203 => 2,
-        202 => 4,
-        204...255 => 3,
-    };
-}
-
-test "switch all prongs unreachable" {
-    try testAllProngsUnreachable();
-    comptime try testAllProngsUnreachable();
-}
-
-fn testAllProngsUnreachable() !void {
-    try expect(switchWithUnreachable(1) == 2);
-    try expect(switchWithUnreachable(2) == 10);
-}
-
-fn switchWithUnreachable(x: i32) i32 {
-    while (true) {
-        switch (x) {
-            1 => return 2,
-            2 => break,
-            else => continue,
-        }
-    }
-    return 10;
-}
-
-fn return_a_number() anyerror!i32 {
-    return 1;
-}
-
-test "capture value of switch with all unreachable prongs" {
-    const x = return_a_number() catch |err| switch (err) {
-        else => unreachable,
-    };
-    try expect(x == 1);
-}
-
-test "switching on booleans" {
-    try testSwitchOnBools();
-    comptime try testSwitchOnBools();
-}
-
-fn testSwitchOnBools() !void {
-    try expect(testSwitchOnBoolsTrueAndFalse(true) == false);
-    try expect(testSwitchOnBoolsTrueAndFalse(false) == true);
-
-    try expect(testSwitchOnBoolsTrueWithElse(true) == false);
-    try expect(testSwitchOnBoolsTrueWithElse(false) == true);
-
-    try expect(testSwitchOnBoolsFalseWithElse(true) == false);
-    try expect(testSwitchOnBoolsFalseWithElse(false) == true);
-}
-
-fn testSwitchOnBoolsTrueAndFalse(x: bool) bool {
-    return switch (x) {
-        true => false,
-        false => true,
-    };
-}
-
-fn testSwitchOnBoolsTrueWithElse(x: bool) bool {
-    return switch (x) {
-        true => false,
-        else => true,
-    };
-}
-
-fn testSwitchOnBoolsFalseWithElse(x: bool) bool {
-    return switch (x) {
-        false => true,
-        else => false,
-    };
-}
-
-test "u0" {
-    var val: u0 = 0;
-    switch (val) {
-        0 => try expect(val == 0),
-    }
-}
-
-test "undefined.u0" {
-    var val: u0 = undefined;
-    switch (val) {
-        0 => try expect(val == 0),
-    }
-}
-
-test "anon enum literal used in switch on union enum" {
-    const Foo = union(enum) {
-        a: i32,
-    };
-
-    var foo = Foo{ .a = 1234 };
-    switch (foo) {
-        .a => |x| {
-            try expect(x == 1234);
-        },
-    }
-}
-
-test "else prong of switch on error set excludes other cases" {
-    const S = struct {
-        fn doTheTest() !void {
-            try expectError(error.C, bar());
-        }
-        const E = error{
-            A,
-            B,
-        } || E2;
-
-        const E2 = error{
-            C,
-            D,
-        };
-
-        fn foo() E!void {
-            return error.C;
-        }
-
-        fn bar() E2!void {
-            foo() catch |err| switch (err) {
-                error.A, error.B => {},
-                else => |e| return e,
-            };
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "switch prongs with error set cases make a new error set type for capture value" {
-    const S = struct {
-        fn doTheTest() !void {
-            try expectError(error.B, bar());
-        }
-        const E = E1 || E2;
-
-        const E1 = error{
-            A,
-            B,
-        };
-
-        const E2 = error{
-            C,
-            D,
-        };
-
-        fn foo() E!void {
-            return error.B;
-        }
-
-        fn bar() E1!void {
-            foo() catch |err| switch (err) {
-                error.A, error.B => |e| return e,
-                else => {},
-            };
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "return result loc and then switch with range implicit casted to error union" {
-    const S = struct {
-        fn doTheTest() !void {
-            try expect((func(0xb) catch unreachable) == 0xb);
-        }
-        fn func(d: u8) anyerror!u8 {
-            return switch (d) {
-                0xa...0xf => d,
-                else => unreachable,
-            };
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "switch with null and T peer types and inferred result location type" {
-    const S = struct {
-        fn doTheTest(c: u8) !void {
-            if (switch (c) {
-                0 => true,
-                else => null,
-            }) |v| {
-                _ = v;
-                @panic("fail");
-            }
-        }
-    };
-    try S.doTheTest(1);
-    comptime try S.doTheTest(1);
-}
-
-test "switch prongs with cases with identical payload types" {
-    const Union = union(enum) {
-        A: usize,
-        B: isize,
-        C: usize,
-    };
-    const S = struct {
-        fn doTheTest() !void {
-            try doTheSwitch1(Union{ .A = 8 });
-            try doTheSwitch2(Union{ .B = -8 });
-        }
-        fn doTheSwitch1(u: Union) !void {
-            switch (u) {
-                .A, .C => |e| {
-                    try expect(@TypeOf(e) == usize);
-                    try expect(e == 8);
-                },
-                .B => |e| {
-                    _ = e;
-                    @panic("fail");
-                },
-            }
-        }
-        fn doTheSwitch2(u: Union) !void {
-            switch (u) {
-                .A, .C => |e| {
-                    _ = e;
-                    @panic("fail");
-                },
-                .B => |e| {
-                    try expect(@TypeOf(e) == isize);
-                    try expect(e == -8);
-                },
-            }
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "switch with disjoint range" {
-    var q: u8 = 0;
-    switch (q) {
-        0...125 => {},
-        127...255 => {},
-        126...126 => {},
-    }
-}
-
-test "switch variable for range and multiple prongs" {
-    const S = struct {
-        fn doTheTest() !void {
-            var u: u8 = 16;
-            try doTheSwitch(u);
-            comptime try doTheSwitch(u);
-            var v: u8 = 42;
-            try doTheSwitch(v);
-            comptime try doTheSwitch(v);
-        }
-        fn doTheSwitch(q: u8) !void {
-            switch (q) {
-                0...40 => |x| try expect(x == 16),
-                41, 42, 43 => |x| try expect(x == 42),
-                else => try expect(false),
-            }
-        }
-    };
-    _ = S;
-}
-
-var state: u32 = 0;
-fn poll() void {
-    switch (state) {
-        0 => {
-            state = 1;
-        },
-        else => {
-            state += 1;
-        },
-    }
-}
-
-test "switch on global mutable var isn't constant-folded" {
-    while (state < 2) {
-        poll();
-    }
-}
-
-test "switch on pointer type" {
-    const S = struct {
-        const X = struct {
-            field: u32,
-        };
-
-        const P1 = @intToPtr(*X, 0x400);
-        const P2 = @intToPtr(*X, 0x800);
-        const P3 = @intToPtr(*X, 0xC00);
-
-        fn doTheTest(arg: *X) i32 {
-            switch (arg) {
-                P1 => return 1,
-                P2 => return 2,
-                else => return 3,
-            }
-        }
-    };
-
-    try expect(1 == S.doTheTest(S.P1));
-    try expect(2 == S.doTheTest(S.P2));
-    try expect(3 == S.doTheTest(S.P3));
-    comptime try expect(1 == S.doTheTest(S.P1));
-    comptime try expect(2 == S.doTheTest(S.P2));
-    comptime try expect(3 == S.doTheTest(S.P3));
-}
-
-test "switch on error set with single else" {
-    const S = struct {
-        fn doTheTest() !void {
-            var some: error{Foo} = error.Foo;
-            try expect(switch (some) {
-                else => |a| blk: {
-                    a catch {};
-                    break :blk true;
-                },
-            });
-        }
-    };
-
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "while copies its payload" {
-    const S = struct {
-        fn doTheTest() !void {
-            var tmp: union(enum) {
-                A: u8,
-                B: u32,
-            } = .{ .A = 42 };
-            switch (tmp) {
-                .A => |value| {
-                    // Modify the original union
-                    tmp = .{ .B = 0x10101010 };
-                    try expectEqual(@as(u8, 42), value);
-                },
-                else => unreachable,
-            }
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
test/behavior/switch_stage1.zig
@@ -0,0 +1,549 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectError = std.testing.expectError;
+const expectEqual = std.testing.expectEqual;
+
+test "switch with numbers" {
+    try testSwitchWithNumbers(13);
+}
+
+fn testSwitchWithNumbers(x: u32) !void {
+    const result = switch (x) {
+        1, 2, 3, 4...8 => false,
+        13 => true,
+        else => false,
+    };
+    try expect(result);
+}
+
+test "switch with all ranges" {
+    try expect(testSwitchWithAllRanges(50, 3) == 1);
+    try expect(testSwitchWithAllRanges(101, 0) == 2);
+    try expect(testSwitchWithAllRanges(300, 5) == 3);
+    try expect(testSwitchWithAllRanges(301, 6) == 6);
+}
+
+fn testSwitchWithAllRanges(x: u32, y: u32) u32 {
+    return switch (x) {
+        0...100 => 1,
+        101...200 => 2,
+        201...300 => 3,
+        else => y,
+    };
+}
+
+test "implicit comptime switch" {
+    const x = 3 + 4;
+    const result = switch (x) {
+        3 => 10,
+        4 => 11,
+        5, 6 => 12,
+        7, 8 => 13,
+        else => 14,
+    };
+
+    comptime {
+        try expect(result + 1 == 14);
+    }
+}
+
+test "switch on enum" {
+    const fruit = Fruit.Orange;
+    nonConstSwitchOnEnum(fruit);
+}
+const Fruit = enum {
+    Apple,
+    Orange,
+    Banana,
+};
+fn nonConstSwitchOnEnum(fruit: Fruit) void {
+    switch (fruit) {
+        Fruit.Apple => unreachable,
+        Fruit.Orange => {},
+        Fruit.Banana => unreachable,
+    }
+}
+
+test "switch statement" {
+    try nonConstSwitch(SwitchStatementFoo.C);
+}
+fn nonConstSwitch(foo: SwitchStatementFoo) !void {
+    const val = switch (foo) {
+        SwitchStatementFoo.A => @as(i32, 1),
+        SwitchStatementFoo.B => 2,
+        SwitchStatementFoo.C => 3,
+        SwitchStatementFoo.D => 4,
+    };
+    try expect(val == 3);
+}
+const SwitchStatementFoo = enum {
+    A,
+    B,
+    C,
+    D,
+};
+
+test "switch prong with variable" {
+    try switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 });
+    try switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 });
+    try switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} });
+}
+const SwitchProngWithVarEnum = union(enum) {
+    One: i32,
+    Two: f32,
+    Meh: void,
+};
+fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void {
+    switch (a) {
+        SwitchProngWithVarEnum.One => |x| {
+            try expect(x == 13);
+        },
+        SwitchProngWithVarEnum.Two => |x| {
+            try expect(x == 13.0);
+        },
+        SwitchProngWithVarEnum.Meh => |x| {
+            const v: void = x;
+            _ = v;
+        },
+    }
+}
+
+test "switch on enum using pointer capture" {
+    try testSwitchEnumPtrCapture();
+    comptime try testSwitchEnumPtrCapture();
+}
+
+fn testSwitchEnumPtrCapture() !void {
+    var value = SwitchProngWithVarEnum{ .One = 1234 };
+    switch (value) {
+        SwitchProngWithVarEnum.One => |*x| x.* += 1,
+        else => unreachable,
+    }
+    switch (value) {
+        SwitchProngWithVarEnum.One => |x| try expect(x == 1235),
+        else => unreachable,
+    }
+}
+
+test "switch with multiple expressions" {
+    const x = switch (returnsFive()) {
+        1, 2, 3 => 1,
+        4, 5, 6 => 2,
+        else => @as(i32, 3),
+    };
+    try expect(x == 2);
+}
+fn returnsFive() i32 {
+    return 5;
+}
+
+const Number = union(enum) {
+    One: u64,
+    Two: u8,
+    Three: f32,
+};
+
+const number = Number{ .Three = 1.23 };
+
+fn returnsFalse() bool {
+    switch (number) {
+        Number.One => |x| return x > 1234,
+        Number.Two => |x| return x == 'a',
+        Number.Three => |x| return x > 12.34,
+    }
+}
+test "switch on const enum with var" {
+    try expect(!returnsFalse());
+}
+
+test "switch on type" {
+    try expect(trueIfBoolFalseOtherwise(bool));
+    try expect(!trueIfBoolFalseOtherwise(i32));
+}
+
+fn trueIfBoolFalseOtherwise(comptime T: type) bool {
+    return switch (T) {
+        bool => true,
+        else => false,
+    };
+}
+
+test "switch handles all cases of number" {
+    try testSwitchHandleAllCases();
+    comptime try testSwitchHandleAllCases();
+}
+
+fn testSwitchHandleAllCases() !void {
+    try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
+    try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
+    try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
+    try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
+
+    try expect(testSwitchHandleAllCasesRange(100) == 0);
+    try expect(testSwitchHandleAllCasesRange(200) == 1);
+    try expect(testSwitchHandleAllCasesRange(201) == 2);
+    try expect(testSwitchHandleAllCasesRange(202) == 4);
+    try expect(testSwitchHandleAllCasesRange(230) == 3);
+}
+
+fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
+    return switch (x) {
+        0 => @as(u2, 3),
+        1 => 2,
+        2 => 1,
+        3 => 0,
+    };
+}
+
+fn testSwitchHandleAllCasesRange(x: u8) u8 {
+    return switch (x) {
+        0...100 => @as(u8, 0),
+        101...200 => 1,
+        201, 203 => 2,
+        202 => 4,
+        204...255 => 3,
+    };
+}
+
+test "switch all prongs unreachable" {
+    try testAllProngsUnreachable();
+    comptime try testAllProngsUnreachable();
+}
+
+fn testAllProngsUnreachable() !void {
+    try expect(switchWithUnreachable(1) == 2);
+    try expect(switchWithUnreachable(2) == 10);
+}
+
+fn switchWithUnreachable(x: i32) i32 {
+    while (true) {
+        switch (x) {
+            1 => return 2,
+            2 => break,
+            else => continue,
+        }
+    }
+    return 10;
+}
+
+fn return_a_number() anyerror!i32 {
+    return 1;
+}
+
+test "capture value of switch with all unreachable prongs" {
+    const x = return_a_number() catch |err| switch (err) {
+        else => unreachable,
+    };
+    try expect(x == 1);
+}
+
+test "switching on booleans" {
+    try testSwitchOnBools();
+    comptime try testSwitchOnBools();
+}
+
+fn testSwitchOnBools() !void {
+    try expect(testSwitchOnBoolsTrueAndFalse(true) == false);
+    try expect(testSwitchOnBoolsTrueAndFalse(false) == true);
+
+    try expect(testSwitchOnBoolsTrueWithElse(true) == false);
+    try expect(testSwitchOnBoolsTrueWithElse(false) == true);
+
+    try expect(testSwitchOnBoolsFalseWithElse(true) == false);
+    try expect(testSwitchOnBoolsFalseWithElse(false) == true);
+}
+
+fn testSwitchOnBoolsTrueAndFalse(x: bool) bool {
+    return switch (x) {
+        true => false,
+        false => true,
+    };
+}
+
+fn testSwitchOnBoolsTrueWithElse(x: bool) bool {
+    return switch (x) {
+        true => false,
+        else => true,
+    };
+}
+
+fn testSwitchOnBoolsFalseWithElse(x: bool) bool {
+    return switch (x) {
+        false => true,
+        else => false,
+    };
+}
+
+test "u0" {
+    var val: u0 = 0;
+    switch (val) {
+        0 => try expect(val == 0),
+    }
+}
+
+test "undefined.u0" {
+    var val: u0 = undefined;
+    switch (val) {
+        0 => try expect(val == 0),
+    }
+}
+
+test "anon enum literal used in switch on union enum" {
+    const Foo = union(enum) {
+        a: i32,
+    };
+
+    var foo = Foo{ .a = 1234 };
+    switch (foo) {
+        .a => |x| {
+            try expect(x == 1234);
+        },
+    }
+}
+
+test "else prong of switch on error set excludes other cases" {
+    const S = struct {
+        fn doTheTest() !void {
+            try expectError(error.C, bar());
+        }
+        const E = error{
+            A,
+            B,
+        } || E2;
+
+        const E2 = error{
+            C,
+            D,
+        };
+
+        fn foo() E!void {
+            return error.C;
+        }
+
+        fn bar() E2!void {
+            foo() catch |err| switch (err) {
+                error.A, error.B => {},
+                else => |e| return e,
+            };
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "switch prongs with error set cases make a new error set type for capture value" {
+    const S = struct {
+        fn doTheTest() !void {
+            try expectError(error.B, bar());
+        }
+        const E = E1 || E2;
+
+        const E1 = error{
+            A,
+            B,
+        };
+
+        const E2 = error{
+            C,
+            D,
+        };
+
+        fn foo() E!void {
+            return error.B;
+        }
+
+        fn bar() E1!void {
+            foo() catch |err| switch (err) {
+                error.A, error.B => |e| return e,
+                else => {},
+            };
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "return result loc and then switch with range implicit casted to error union" {
+    const S = struct {
+        fn doTheTest() !void {
+            try expect((func(0xb) catch unreachable) == 0xb);
+        }
+        fn func(d: u8) anyerror!u8 {
+            return switch (d) {
+                0xa...0xf => d,
+                else => unreachable,
+            };
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "switch with null and T peer types and inferred result location type" {
+    const S = struct {
+        fn doTheTest(c: u8) !void {
+            if (switch (c) {
+                0 => true,
+                else => null,
+            }) |v| {
+                _ = v;
+                @panic("fail");
+            }
+        }
+    };
+    try S.doTheTest(1);
+    comptime try S.doTheTest(1);
+}
+
+test "switch prongs with cases with identical payload types" {
+    const Union = union(enum) {
+        A: usize,
+        B: isize,
+        C: usize,
+    };
+    const S = struct {
+        fn doTheTest() !void {
+            try doTheSwitch1(Union{ .A = 8 });
+            try doTheSwitch2(Union{ .B = -8 });
+        }
+        fn doTheSwitch1(u: Union) !void {
+            switch (u) {
+                .A, .C => |e| {
+                    try expect(@TypeOf(e) == usize);
+                    try expect(e == 8);
+                },
+                .B => |e| {
+                    _ = e;
+                    @panic("fail");
+                },
+            }
+        }
+        fn doTheSwitch2(u: Union) !void {
+            switch (u) {
+                .A, .C => |e| {
+                    _ = e;
+                    @panic("fail");
+                },
+                .B => |e| {
+                    try expect(@TypeOf(e) == isize);
+                    try expect(e == -8);
+                },
+            }
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "switch with disjoint range" {
+    var q: u8 = 0;
+    switch (q) {
+        0...125 => {},
+        127...255 => {},
+        126...126 => {},
+    }
+}
+
+test "switch variable for range and multiple prongs" {
+    const S = struct {
+        fn doTheTest() !void {
+            var u: u8 = 16;
+            try doTheSwitch(u);
+            comptime try doTheSwitch(u);
+            var v: u8 = 42;
+            try doTheSwitch(v);
+            comptime try doTheSwitch(v);
+        }
+        fn doTheSwitch(q: u8) !void {
+            switch (q) {
+                0...40 => |x| try expect(x == 16),
+                41, 42, 43 => |x| try expect(x == 42),
+                else => try expect(false),
+            }
+        }
+    };
+    _ = S;
+}
+
+var state: u32 = 0;
+fn poll() void {
+    switch (state) {
+        0 => {
+            state = 1;
+        },
+        else => {
+            state += 1;
+        },
+    }
+}
+
+test "switch on global mutable var isn't constant-folded" {
+    while (state < 2) {
+        poll();
+    }
+}
+
+test "switch on pointer type" {
+    const S = struct {
+        const X = struct {
+            field: u32,
+        };
+
+        const P1 = @intToPtr(*X, 0x400);
+        const P2 = @intToPtr(*X, 0x800);
+        const P3 = @intToPtr(*X, 0xC00);
+
+        fn doTheTest(arg: *X) i32 {
+            switch (arg) {
+                P1 => return 1,
+                P2 => return 2,
+                else => return 3,
+            }
+        }
+    };
+
+    try expect(1 == S.doTheTest(S.P1));
+    try expect(2 == S.doTheTest(S.P2));
+    try expect(3 == S.doTheTest(S.P3));
+    comptime try expect(1 == S.doTheTest(S.P1));
+    comptime try expect(2 == S.doTheTest(S.P2));
+    comptime try expect(3 == S.doTheTest(S.P3));
+}
+
+test "switch on error set with single else" {
+    const S = struct {
+        fn doTheTest() !void {
+            var some: error{Foo} = error.Foo;
+            try expect(switch (some) {
+                else => |a| blk: {
+                    a catch {};
+                    break :blk true;
+                },
+            });
+        }
+    };
+
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "while copies its payload" {
+    const S = struct {
+        fn doTheTest() !void {
+            var tmp: union(enum) {
+                A: u8,
+                B: u32,
+            } = .{ .A = 42 };
+            switch (tmp) {
+                .A => |value| {
+                    // Modify the original union
+                    tmp = .{ .B = 0x10101010 };
+                    try expectEqual(@as(u8, 42), value);
+                },
+                else => unreachable,
+            }
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
test/behavior/underscore.zig
@@ -5,17 +5,6 @@ test "ignore lval with underscore" {
     _ = false;
 }
 
-test "ignore lval with underscore (for loop)" {
-    for ([_]void{}) |_, i| {
-        _ = i;
-        for ([_]void{}) |_, j| {
-            _ = j;
-            break;
-        }
-        break;
-    }
-}
-
 test "ignore lval with underscore (while loop)" {
     while (optionalReturnError()) |_| {
         while (optionalReturnError()) |_| {
test/behavior/enum_with_members.zig → test/behavior/union_with_members.zig
File renamed without changes
test/behavior/while.zig
@@ -23,7 +23,7 @@ test "static eval while" {
 }
 const static_eval_while_number = staticWhileLoop1();
 fn staticWhileLoop1() i32 {
-    return whileLoop2();
+    return staticWhileLoop2();
 }
 fn staticWhileLoop2() i32 {
     while (true) {
@@ -31,33 +31,6 @@ fn staticWhileLoop2() i32 {
     }
 }
 
-test "continue and break" {
-    try runContinueAndBreakTest();
-    try expect(continue_and_break_counter == 8);
-}
-var continue_and_break_counter: i32 = 0;
-fn runContinueAndBreakTest() !void {
-    var i: i32 = 0;
-    while (true) {
-        continue_and_break_counter += 2;
-        i += 1;
-        if (i < 4) {
-            continue;
-        }
-        break;
-    }
-    try expect(i == 4);
-}
-
-test "return with implicit cast from while loop" {
-    returnWithImplicitCastFromWhileLoopTest() catch unreachable;
-}
-fn returnWithImplicitCastFromWhileLoopTest() anyerror!void {
-    while (true) {
-        return;
-    }
-}
-
 test "while with continue expression" {
     var sum: i32 = 0;
     {
@@ -83,43 +56,6 @@ test "while with else" {
     try expect(got_else == 1);
 }
 
-test "while with optional as condition" {
-    numbers_left = 10;
-    var sum: i32 = 0;
-    while (getNumberOrNull()) |value| {
-        sum += value;
-    }
-    try expect(sum == 45);
-}
-
-test "while with optional as condition with else" {
-    numbers_left = 10;
-    var sum: i32 = 0;
-    var got_else: i32 = 0;
-    while (getNumberOrNull()) |value| {
-        sum += value;
-        try expect(got_else == 0);
-    } else {
-        got_else += 1;
-    }
-    try expect(sum == 45);
-    try expect(got_else == 1);
-}
-
-test "while with error union condition" {
-    numbers_left = 10;
-    var sum: i32 = 0;
-    var got_else: i32 = 0;
-    while (getNumberOrErr()) |value| {
-        sum += value;
-    } else |err| {
-        try expect(err == error.OutOfNumbers);
-        got_else += 1;
-    }
-    try expect(sum == 45);
-    try expect(got_else == 1);
-}
-
 var numbers_left: i32 = undefined;
 fn getNumberOrErr() anyerror!i32 {
     return if (numbers_left == 0) error.OutOfNumbers else x: {
@@ -134,61 +70,6 @@ fn getNumberOrNull() ?i32 {
     };
 }
 
-test "while on optional with else result follow else prong" {
-    const result = while (returnNull()) |value| {
-        break value;
-    } else @as(i32, 2);
-    try expect(result == 2);
-}
-
-test "while on optional with else result follow break prong" {
-    const result = while (returnOptional(10)) |value| {
-        break value;
-    } else @as(i32, 2);
-    try expect(result == 10);
-}
-
-test "while on error union with else result follow else prong" {
-    const result = while (returnError()) |value| {
-        break value;
-    } else |_| @as(i32, 2);
-    try expect(result == 2);
-}
-
-test "while on error union with else result follow break prong" {
-    const result = while (returnSuccess(10)) |value| {
-        break value;
-    } else |_| @as(i32, 2);
-    try expect(result == 10);
-}
-
-test "while on bool with else result follow else prong" {
-    const result = while (returnFalse()) {
-        break @as(i32, 10);
-    } else @as(i32, 2);
-    try expect(result == 2);
-}
-
-test "while on bool with else result follow break prong" {
-    const result = while (returnTrue()) {
-        break @as(i32, 10);
-    } else @as(i32, 2);
-    try expect(result == 10);
-}
-
-test "break from outer while loop" {
-    testBreakOuter();
-    comptime testBreakOuter();
-}
-
-fn testBreakOuter() void {
-    outer: while (true) {
-        while (true) {
-            break :outer;
-        }
-    }
-}
-
 test "continue outer while loop" {
     testContinueOuter();
     comptime testContinueOuter();
@@ -203,68 +84,17 @@ fn testContinueOuter() void {
     }
 }
 
-fn returnNull() ?i32 {
-    return null;
-}
-fn returnOptional(x: i32) ?i32 {
-    return x;
-}
-fn returnError() anyerror!i32 {
-    return error.YouWantedAnError;
-}
-fn returnSuccess(x: i32) anyerror!i32 {
-    return x;
-}
-fn returnFalse() bool {
-    return false;
-}
-fn returnTrue() bool {
-    return true;
-}
-
-test "while bool 2 break statements and an else" {
-    const S = struct {
-        fn entry(t: bool, f: bool) !void {
-            var ok = false;
-            ok = while (t) {
-                if (f) break false;
-                if (t) break true;
-            } else false;
-            try expect(ok);
-        }
-    };
-    try S.entry(true, false);
-    comptime try S.entry(true, false);
-}
-
-test "while optional 2 break statements and an else" {
-    const S = struct {
-        fn entry(opt_t: ?bool, f: bool) !void {
-            var ok = false;
-            ok = while (opt_t) |t| {
-                if (f) break false;
-                if (t) break true;
-            } else false;
-            try expect(ok);
-        }
-    };
-    try S.entry(true, false);
-    comptime try S.entry(true, false);
+test "break from outer while loop" {
+    testBreakOuter();
+    comptime testBreakOuter();
 }
 
-test "while error 2 break statements and an else" {
-    const S = struct {
-        fn entry(opt_t: anyerror!bool, f: bool) !void {
-            var ok = false;
-            ok = while (opt_t) |t| {
-                if (f) break false;
-                if (t) break true;
-            } else |_| false;
-            try expect(ok);
+fn testBreakOuter() void {
+    outer: while (true) {
+        while (true) {
+            break :outer;
         }
-    };
-    try S.entry(true, false);
-    comptime try S.entry(true, false);
+    }
 }
 
 test "while copies its payload" {
test/behavior/while_stage1.zig
@@ -0,0 +1,186 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "continue and break" {
+    try runContinueAndBreakTest();
+    try expect(continue_and_break_counter == 8);
+}
+var continue_and_break_counter: i32 = 0;
+fn runContinueAndBreakTest() !void {
+    var i: i32 = 0;
+    while (true) {
+        continue_and_break_counter += 2;
+        i += 1;
+        if (i < 4) {
+            continue;
+        }
+        break;
+    }
+    try expect(i == 4);
+}
+
+test "return with implicit cast from while loop" {
+    returnWithImplicitCastFromWhileLoopTest() catch unreachable;
+}
+fn returnWithImplicitCastFromWhileLoopTest() anyerror!void {
+    while (true) {
+        return;
+    }
+}
+
+test "while with optional as condition" {
+    numbers_left = 10;
+    var sum: i32 = 0;
+    while (getNumberOrNull()) |value| {
+        sum += value;
+    }
+    try expect(sum == 45);
+}
+
+test "while with optional as condition with else" {
+    numbers_left = 10;
+    var sum: i32 = 0;
+    var got_else: i32 = 0;
+    while (getNumberOrNull()) |value| {
+        sum += value;
+        try expect(got_else == 0);
+    } else {
+        got_else += 1;
+    }
+    try expect(sum == 45);
+    try expect(got_else == 1);
+}
+
+test "while with error union condition" {
+    numbers_left = 10;
+    var sum: i32 = 0;
+    var got_else: i32 = 0;
+    while (getNumberOrErr()) |value| {
+        sum += value;
+    } else |err| {
+        try expect(err == error.OutOfNumbers);
+        got_else += 1;
+    }
+    try expect(sum == 45);
+    try expect(got_else == 1);
+}
+
+var numbers_left: i32 = undefined;
+fn getNumberOrErr() anyerror!i32 {
+    return if (numbers_left == 0) error.OutOfNumbers else x: {
+        numbers_left -= 1;
+        break :x numbers_left;
+    };
+}
+fn getNumberOrNull() ?i32 {
+    return if (numbers_left == 0) null else x: {
+        numbers_left -= 1;
+        break :x numbers_left;
+    };
+}
+
+test "while on optional with else result follow else prong" {
+    const result = while (returnNull()) |value| {
+        break value;
+    } else @as(i32, 2);
+    try expect(result == 2);
+}
+
+test "while on optional with else result follow break prong" {
+    const result = while (returnOptional(10)) |value| {
+        break value;
+    } else @as(i32, 2);
+    try expect(result == 10);
+}
+
+test "while on error union with else result follow else prong" {
+    const result = while (returnError()) |value| {
+        break value;
+    } else |_| @as(i32, 2);
+    try expect(result == 2);
+}
+
+test "while on error union with else result follow break prong" {
+    const result = while (returnSuccess(10)) |value| {
+        break value;
+    } else |_| @as(i32, 2);
+    try expect(result == 10);
+}
+
+test "while on bool with else result follow else prong" {
+    const result = while (returnFalse()) {
+        break @as(i32, 10);
+    } else @as(i32, 2);
+    try expect(result == 2);
+}
+
+test "while on bool with else result follow break prong" {
+    const result = while (returnTrue()) {
+        break @as(i32, 10);
+    } else @as(i32, 2);
+    try expect(result == 10);
+}
+
+fn returnNull() ?i32 {
+    return null;
+}
+fn returnOptional(x: i32) ?i32 {
+    return x;
+}
+fn returnError() anyerror!i32 {
+    return error.YouWantedAnError;
+}
+fn returnSuccess(x: i32) anyerror!i32 {
+    return x;
+}
+fn returnFalse() bool {
+    return false;
+}
+fn returnTrue() bool {
+    return true;
+}
+
+test "while bool 2 break statements and an else" {
+    const S = struct {
+        fn entry(t: bool, f: bool) !void {
+            var ok = false;
+            ok = while (t) {
+                if (f) break false;
+                if (t) break true;
+            } else false;
+            try expect(ok);
+        }
+    };
+    try S.entry(true, false);
+    comptime try S.entry(true, false);
+}
+
+test "while optional 2 break statements and an else" {
+    const S = struct {
+        fn entry(opt_t: ?bool, f: bool) !void {
+            var ok = false;
+            ok = while (opt_t) |t| {
+                if (f) break false;
+                if (t) break true;
+            } else false;
+            try expect(ok);
+        }
+    };
+    try S.entry(true, false);
+    comptime try S.entry(true, false);
+}
+
+test "while error 2 break statements and an else" {
+    const S = struct {
+        fn entry(opt_t: anyerror!bool, f: bool) !void {
+            var ok = false;
+            ok = while (opt_t) |t| {
+                if (f) break false;
+                if (t) break true;
+            } else |_| false;
+            try expect(ok);
+        }
+    };
+    try S.entry(true, false);
+    comptime try S.entry(true, false);
+}
test/behavior.zig
@@ -15,8 +15,12 @@ test {
     _ = @import("behavior/bugs/4769_b.zig");
     _ = @import("behavior/bugs/6850.zig");
     _ = @import("behavior/bugs/9584.zig");
+    _ = @import("behavior/call.zig");
     _ = @import("behavior/cast.zig");
+    _ = @import("behavior/defer.zig");
+    _ = @import("behavior/enum.zig");
     _ = @import("behavior/eval.zig");
+    _ = @import("behavior/for.zig");
     _ = @import("behavior/generics.zig");
     _ = @import("behavior/if.zig");
     _ = @import("behavior/math.zig");
@@ -24,11 +28,14 @@ test {
     _ = @import("behavior/pointers.zig");
     _ = @import("behavior/sizeof_and_typeof.zig");
     _ = @import("behavior/struct.zig");
+    _ = @import("behavior/switch.zig");
     _ = @import("behavior/this.zig");
     _ = @import("behavior/translate_c_macros.zig");
+    _ = @import("behavior/underscore.zig");
     _ = @import("behavior/union.zig");
     _ = @import("behavior/usingnamespace.zig");
     _ = @import("behavior/widening.zig");
+    _ = @import("behavior/while.zig");
 
     if (builtin.zig_is_stage2) {
         // When all comptime_memory.zig tests pass, #9646 can be closed.
@@ -98,12 +105,11 @@ test {
         _ = @import("behavior/bugs/7250.zig");
         _ = @import("behavior/byteswap.zig");
         _ = @import("behavior/byval_arg_var.zig");
-        _ = @import("behavior/call.zig");
+        _ = @import("behavior/call_stage1.zig");
         _ = @import("behavior/cast_stage1.zig");
         _ = @import("behavior/const_slice_child.zig");
-        _ = @import("behavior/defer.zig");
-        _ = @import("behavior/enum.zig");
-        _ = @import("behavior/enum_with_members.zig");
+        _ = @import("behavior/defer_stage1.zig");
+        _ = @import("behavior/enum_stage1.zig");
         _ = @import("behavior/error.zig");
         _ = @import("behavior/eval_stage1.zig");
         _ = @import("behavior/field_parent_ptr.zig");
@@ -111,7 +117,7 @@ test {
         _ = @import("behavior/fn.zig");
         _ = @import("behavior/fn_in_struct_in_comptime.zig");
         _ = @import("behavior/fn_delegation.zig");
-        _ = @import("behavior/for.zig");
+        _ = @import("behavior/for_stage1.zig");
         _ = @import("behavior/generics_stage1.zig");
         _ = @import("behavior/hasdecl.zig");
         _ = @import("behavior/hasfield.zig");
@@ -143,7 +149,7 @@ test {
         _ = @import("behavior/struct_stage1.zig");
         _ = @import("behavior/struct_contains_null_ptr_itself.zig");
         _ = @import("behavior/struct_contains_slice_of_itself.zig");
-        _ = @import("behavior/switch.zig");
+        _ = @import("behavior/switch_stage1.zig");
         _ = @import("behavior/switch_prong_err_enum.zig");
         _ = @import("behavior/switch_prong_implicit_cast.zig");
         _ = @import("behavior/truncate.zig");
@@ -153,8 +159,8 @@ test {
         _ = @import("behavior/type_info.zig");
         _ = @import("behavior/typename.zig");
         _ = @import("behavior/undefined.zig");
-        _ = @import("behavior/underscore.zig");
         _ = @import("behavior/union_stage1.zig");
+        _ = @import("behavior/union_with_members.zig");
         _ = @import("behavior/usingnamespace_stage1.zig");
         _ = @import("behavior/var_args.zig");
         _ = @import("behavior/vector.zig");
@@ -162,7 +168,7 @@ test {
         if (builtin.target.cpu.arch == .wasm32) {
             _ = @import("behavior/wasm.zig");
         }
-        _ = @import("behavior/while.zig");
+        _ = @import("behavior/while_stage1.zig");
         _ = @import("behavior/src.zig");
         _ = @import("behavior/translate_c_macros_stage1.zig");
     }