mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
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.
This commit is contained in:
parent
61a53a5875
commit
c4df9bf56f
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
74
test/behavior/call_stage1.zig
Normal file
74
test/behavior/call_stage1.zig
Normal file
@ -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));
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
114
test/behavior/defer_stage1.zig
Normal file
114
test/behavior/defer_stage1.zig
Normal file
@ -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();
|
||||
}
|
||||
@ -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 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 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 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 "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 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();
|
||||
}
|
||||
|
||||
1058
test/behavior/enum_stage1.zig
Normal file
1058
test/behavior/enum_stage1.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 });
|
||||
}
|
||||
|
||||
185
test/behavior/for_stage1.zig
Normal file
185
test/behavior/for_stage1.zig
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
549
test/behavior/switch_stage1.zig
Normal file
549
test/behavior/switch_stage1.zig
Normal file
@ -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();
|
||||
}
|
||||
@ -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()) |_| {
|
||||
|
||||
@ -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 "break from outer while loop" {
|
||||
testBreakOuter();
|
||||
comptime testBreakOuter();
|
||||
}
|
||||
|
||||
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);
|
||||
fn testBreakOuter() void {
|
||||
outer: while (true) {
|
||||
while (true) {
|
||||
break :outer;
|
||||
}
|
||||
};
|
||||
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 "while copies its payload" {
|
||||
|
||||
186
test/behavior/while_stage1.zig
Normal file
186
test/behavior/while_stage1.zig
Normal file
@ -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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user