aboutsummaryrefslogtreecommitdiff
path: root/test/behavior
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-04-29 15:54:04 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-04-29 15:54:04 -0700
commit4307436b9945f814ff5731981df1d19febf3ba0a (patch)
treeefaaec94a41d632d45f255d464d9787eb02c4c9b /test/behavior
parent5a02c938dafdf2bb11b2350b6ad3161ef93744f0 (diff)
downloadzig-4307436b9945f814ff5731981df1d19febf3ba0a.tar.gz
zig-4307436b9945f814ff5731981df1d19febf3ba0a.zip
move behavior tests from test/stage1/ to test/
And fix test cases to make them pass. This is in preparation for starting to pass behavior tests with self-hosted.
Diffstat (limited to 'test/behavior')
-rw-r--r--test/behavior/align.zig347
-rw-r--r--test/behavior/alignof.zig39
-rw-r--r--test/behavior/array.zig489
-rw-r--r--test/behavior/asm.zig94
-rw-r--r--test/behavior/async_fn.zig1673
-rw-r--r--test/behavior/atomics.zig221
-rw-r--r--test/behavior/await_struct.zig44
-rw-r--r--test/behavior/bit_shifting.zig104
-rw-r--r--test/behavior/bitcast.zig197
-rw-r--r--test/behavior/bitreverse.zig69
-rw-r--r--test/behavior/bool.zig35
-rw-r--r--test/behavior/bugs/1025.zig12
-rw-r--r--test/behavior/bugs/1076.zig23
-rw-r--r--test/behavior/bugs/1111.zig11
-rw-r--r--test/behavior/bugs/1120.zig23
-rw-r--r--test/behavior/bugs/1277.zig15
-rw-r--r--test/behavior/bugs/1310.zig24
-rw-r--r--test/behavior/bugs/1322.zig19
-rw-r--r--test/behavior/bugs/1381.zig21
-rw-r--r--test/behavior/bugs/1421.zig13
-rw-r--r--test/behavior/bugs/1442.zig11
-rw-r--r--test/behavior/bugs/1467.zig7
-rw-r--r--test/behavior/bugs/1486.zig10
-rw-r--r--test/behavior/bugs/1500.zig10
-rw-r--r--test/behavior/bugs/1607.zig15
-rw-r--r--test/behavior/bugs/1735.zig46
-rw-r--r--test/behavior/bugs/1741.zig6
-rw-r--r--test/behavior/bugs/1851.zig26
-rw-r--r--test/behavior/bugs/1914.zig31
-rw-r--r--test/behavior/bugs/2006.zig12
-rw-r--r--test/behavior/bugs/2114.zig19
-rw-r--r--test/behavior/bugs/2346.zig6
-rw-r--r--test/behavior/bugs/2578.zig12
-rw-r--r--test/behavior/bugs/2692.zig6
-rw-r--r--test/behavior/bugs/2889.zig31
-rw-r--r--test/behavior/bugs/3007.zig23
-rw-r--r--test/behavior/bugs/3046.zig19
-rw-r--r--test/behavior/bugs/3112.zig17
-rw-r--r--test/behavior/bugs/3367.zig12
-rw-r--r--test/behavior/bugs/3384.zig11
-rw-r--r--test/behavior/bugs/3468.zig6
-rw-r--r--test/behavior/bugs/3586.zig11
-rw-r--r--test/behavior/bugs/3742.zig38
-rw-r--r--test/behavior/bugs/394.zig18
-rw-r--r--test/behavior/bugs/421.zig15
-rw-r--r--test/behavior/bugs/4328.zig71
-rw-r--r--test/behavior/bugs/4560.zig32
-rw-r--r--test/behavior/bugs/4769_a.zig1
-rw-r--r--test/behavior/bugs/4769_b.zig1
-rw-r--r--test/behavior/bugs/4769_c.zig1
-rw-r--r--test/behavior/bugs/4954.zig8
-rw-r--r--test/behavior/bugs/529.zig14
-rw-r--r--test/behavior/bugs/529_other_file.zig5
-rw-r--r--test/behavior/bugs/529_other_file_2.zig4
-rw-r--r--test/behavior/bugs/5398.zig31
-rw-r--r--test/behavior/bugs/5413.zig6
-rw-r--r--test/behavior/bugs/5474.zig57
-rw-r--r--test/behavior/bugs/5487.zig12
-rw-r--r--test/behavior/bugs/624.zig23
-rw-r--r--test/behavior/bugs/6456.zig42
-rw-r--r--test/behavior/bugs/655.zig12
-rw-r--r--test/behavior/bugs/655_other_file.zig1
-rw-r--r--test/behavior/bugs/656.zig31
-rw-r--r--test/behavior/bugs/6781.zig74
-rw-r--r--test/behavior/bugs/679.zig17
-rw-r--r--test/behavior/bugs/6850.zig12
-rw-r--r--test/behavior/bugs/7003.zig8
-rw-r--r--test/behavior/bugs/7027.zig17
-rw-r--r--test/behavior/bugs/704.zig7
-rw-r--r--test/behavior/bugs/7047.zig22
-rw-r--r--test/behavior/bugs/718.zig17
-rw-r--r--test/behavior/bugs/7250.zig15
-rw-r--r--test/behavior/bugs/726.zig15
-rw-r--r--test/behavior/bugs/828.zig33
-rw-r--r--test/behavior/bugs/920.zig65
-rw-r--r--test/behavior/byteswap.zig68
-rw-r--r--test/behavior/byval_arg_var.zig27
-rw-r--r--test/behavior/call.zig74
-rw-r--r--test/behavior/cast.zig927
-rw-r--r--test/behavior/const_slice_child.zig47
-rw-r--r--test/behavior/defer.zig114
-rw-r--r--test/behavior/enum.zig1204
-rw-r--r--test/behavior/enum_with_members.zig27
-rw-r--r--test/behavior/error.zig452
-rw-r--r--test/behavior/eval.zig832
-rw-r--r--test/behavior/field_parent_ptr.zig41
-rw-r--r--test/behavior/floatop.zig465
-rw-r--r--test/behavior/fn.zig287
-rw-r--r--test/behavior/fn_delegation.zig39
-rw-r--r--test/behavior/fn_in_struct_in_comptime.zig17
-rw-r--r--test/behavior/for.zig172
-rw-r--r--test/behavior/generics.zig169
-rw-r--r--test/behavior/hasdecl.zig21
-rw-r--r--test/behavior/hasdecl/foo.zig2
-rw-r--r--test/behavior/hasfield.zig37
-rw-r--r--test/behavior/if.zig109
-rw-r--r--test/behavior/import.zig22
-rw-r--r--test/behavior/import/a_namespace.zig3
-rw-r--r--test/behavior/import/empty.zig0
-rw-r--r--test/behavior/incomplete_struct_param_tld.zig30
-rw-r--r--test/behavior/inttoptr.zig26
-rw-r--r--test/behavior/ir_block_deps.zig21
-rw-r--r--test/behavior/math.zig872
-rw-r--r--test/behavior/merge_error_sets.zig21
-rw-r--r--test/behavior/misc.zig761
-rw-r--r--test/behavior/muladd.zig34
-rw-r--r--test/behavior/namespace_depends_on_compile_var.zig14
-rw-r--r--test/behavior/namespace_depends_on_compile_var/a.zig1
-rw-r--r--test/behavior/namespace_depends_on_compile_var/b.zig1
-rw-r--r--test/behavior/null.zig162
-rw-r--r--test/behavior/optional.zig269
-rw-r--r--test/behavior/pointers.zig339
-rw-r--r--test/behavior/popcount.zig43
-rw-r--r--test/behavior/ptrcast.zig73
-rw-r--r--test/behavior/pub_enum.zig13
-rw-r--r--test/behavior/pub_enum/other.zig6
-rw-r--r--test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig37
-rw-r--r--test/behavior/reflection.zig55
-rw-r--r--test/behavior/shuffle.zig63
-rw-r--r--test/behavior/sizeof_and_typeof.zig264
-rw-r--r--test/behavior/slice.zig337
-rw-r--r--test/behavior/slice_sentinel_comptime.zig199
-rw-r--r--test/behavior/src.zig17
-rw-r--r--test/behavior/struct.zig945
-rw-r--r--test/behavior/struct_contains_null_ptr_itself.zig21
-rw-r--r--test/behavior/struct_contains_slice_of_itself.zig85
-rw-r--r--test/behavior/switch.zig537
-rw-r--r--test/behavior/switch_prong_err_enum.zig30
-rw-r--r--test/behavior/switch_prong_implicit_cast.zig22
-rw-r--r--test/behavior/syntax.zig68
-rw-r--r--test/behavior/this.zig34
-rw-r--r--test/behavior/translate_c_macros.h18
-rw-r--r--test/behavior/translate_c_macros.zig22
-rw-r--r--test/behavior/truncate.zig36
-rw-r--r--test/behavior/try.zig43
-rw-r--r--test/behavior/tuple.zig113
-rw-r--r--test/behavior/type.zig453
-rw-r--r--test/behavior/type_info.zig485
-rw-r--r--test/behavior/typename.zig7
-rw-r--r--test/behavior/undefined.zig69
-rw-r--r--test/behavior/underscore.zig28
-rw-r--r--test/behavior/union.zig806
-rw-r--r--test/behavior/usingnamespace.zig22
-rw-r--r--test/behavior/var_args.zig83
-rw-r--r--test/behavior/vector.zig655
-rw-r--r--test/behavior/void.zig40
-rw-r--r--test/behavior/wasm.zig8
-rw-r--r--test/behavior/while.zig289
-rw-r--r--test/behavior/widening.zig39
149 files changed, 18317 insertions, 0 deletions
diff --git a/test/behavior/align.zig b/test/behavior/align.zig
new file mode 100644
index 0000000000..3ef147746c
--- /dev/null
+++ b/test/behavior/align.zig
@@ -0,0 +1,347 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const builtin = @import("builtin");
+const native_arch = builtin.target.cpu.arch;
+
+var foo: u8 align(4) = 100;
+
+test "global variable alignment" {
+ comptime expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
+ comptime expect(@TypeOf(&foo) == *align(4) u8);
+ {
+ const slice = @as(*[1]u8, &foo)[0..];
+ comptime expect(@TypeOf(slice) == *align(4) [1]u8);
+ }
+ {
+ var runtime_zero: usize = 0;
+ const slice = @as(*[1]u8, &foo)[runtime_zero..];
+ comptime expect(@TypeOf(slice) == []align(4) u8);
+ }
+}
+
+fn derp() align(@sizeOf(usize) * 2) i32 {
+ return 1234;
+}
+fn noop1() align(1) void {}
+fn noop4() align(4) void {}
+
+test "function alignment" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect(derp() == 1234);
+ expect(@TypeOf(noop1) == fn () align(1) void);
+ expect(@TypeOf(noop4) == fn () align(4) void);
+ noop1();
+ noop4();
+}
+
+var baz: packed struct {
+ a: u32,
+ b: u32,
+} = undefined;
+
+test "packed struct alignment" {
+ expect(@TypeOf(&baz.b) == *align(1) u32);
+}
+
+const blah: packed struct {
+ a: u3,
+ b: u3,
+ c: u2,
+} = undefined;
+
+test "bit field alignment" {
+ expect(@TypeOf(&blah.b) == *align(1:3:1) const u3);
+}
+
+test "default alignment allows unspecified in type syntax" {
+ expect(*u32 == *align(@alignOf(u32)) u32);
+}
+
+test "implicitly decreasing pointer alignment" {
+ const a: u32 align(4) = 3;
+ const b: u32 align(8) = 4;
+ expect(addUnaligned(&a, &b) == 7);
+}
+
+fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
+ return a.* + b.*;
+}
+
+test "implicitly decreasing slice alignment" {
+ const a: u32 align(4) = 3;
+ const b: u32 align(8) = 4;
+ expect(addUnalignedSlice(@as(*const [1]u32, &a)[0..], @as(*const [1]u32, &b)[0..]) == 7);
+}
+fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
+ return a[0] + b[0];
+}
+
+test "specifying alignment allows pointer cast" {
+ testBytesAlign(0x33);
+}
+fn testBytesAlign(b: u8) void {
+ var bytes align(4) = [_]u8{
+ b,
+ b,
+ b,
+ b,
+ };
+ const ptr = @ptrCast(*u32, &bytes[0]);
+ expect(ptr.* == 0x33333333);
+}
+
+test "@alignCast pointers" {
+ var x: u32 align(4) = 1;
+ expectsOnly1(&x);
+ expect(x == 2);
+}
+fn expectsOnly1(x: *align(1) u32) void {
+ expects4(@alignCast(4, x));
+}
+fn expects4(x: *align(4) u32) void {
+ x.* += 1;
+}
+
+test "@alignCast slices" {
+ var array align(4) = [_]u32{
+ 1,
+ 1,
+ };
+ const slice = array[0..];
+ sliceExpectsOnly1(slice);
+ expect(slice[0] == 2);
+}
+fn sliceExpectsOnly1(slice: []align(1) u32) void {
+ sliceExpects4(@alignCast(4, slice));
+}
+fn sliceExpects4(slice: []align(4) u32) void {
+ slice[0] += 1;
+}
+
+test "implicitly decreasing fn alignment" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ testImplicitlyDecreaseFnAlign(alignedSmall, 1234);
+ testImplicitlyDecreaseFnAlign(alignedBig, 5678);
+}
+
+fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void {
+ expect(ptr() == answer);
+}
+
+fn alignedSmall() align(8) i32 {
+ return 1234;
+}
+fn alignedBig() align(16) i32 {
+ return 5678;
+}
+
+test "@alignCast functions" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect(fnExpectsOnly1(simple4) == 0x19);
+}
+fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 {
+ return fnExpects4(@alignCast(4, ptr));
+}
+fn fnExpects4(ptr: fn () align(4) i32) i32 {
+ return ptr();
+}
+fn simple4() align(4) i32 {
+ return 0x19;
+}
+
+test "generic function with align param" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect(whyWouldYouEverDoThis(1) == 0x1);
+ expect(whyWouldYouEverDoThis(4) == 0x1);
+ expect(whyWouldYouEverDoThis(8) == 0x1);
+}
+
+fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
+ return 0x1;
+}
+
+test "@ptrCast preserves alignment of bigger source" {
+ var x: u32 align(16) = 1234;
+ const ptr = @ptrCast(*u8, &x);
+ expect(@TypeOf(ptr) == *align(16) u8);
+}
+
+test "runtime known array index has best alignment possible" {
+ // take full advantage of over-alignment
+ var array align(4) = [_]u8{ 1, 2, 3, 4 };
+ expect(@TypeOf(&array[0]) == *align(4) u8);
+ expect(@TypeOf(&array[1]) == *u8);
+ expect(@TypeOf(&array[2]) == *align(2) u8);
+ expect(@TypeOf(&array[3]) == *u8);
+
+ // because align is too small but we still figure out to use 2
+ var bigger align(2) = [_]u64{ 1, 2, 3, 4 };
+ expect(@TypeOf(&bigger[0]) == *align(2) u64);
+ expect(@TypeOf(&bigger[1]) == *align(2) u64);
+ expect(@TypeOf(&bigger[2]) == *align(2) u64);
+ expect(@TypeOf(&bigger[3]) == *align(2) u64);
+
+ // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
+ var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
+ var runtime_zero: usize = 0;
+ comptime expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
+ comptime expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
+
+ // has to use ABI alignment because index known at runtime only
+ testIndex2(array[runtime_zero..].ptr, 0, *u8);
+ testIndex2(array[runtime_zero..].ptr, 1, *u8);
+ testIndex2(array[runtime_zero..].ptr, 2, *u8);
+ testIndex2(array[runtime_zero..].ptr, 3, *u8);
+}
+fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void {
+ comptime expect(@TypeOf(&smaller[index]) == T);
+}
+fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void {
+ comptime expect(@TypeOf(&ptr[index]) == T);
+}
+
+test "alignstack" {
+ expect(fnWithAlignedStack() == 1234);
+}
+
+fn fnWithAlignedStack() i32 {
+ @setAlignStack(256);
+ return 1234;
+}
+
+test "alignment of structs" {
+ expect(@alignOf(struct {
+ a: i32,
+ b: *i32,
+ }) == @alignOf(usize));
+}
+
+test "alignment of function with c calling convention" {
+ var runtime_nothing = nothing;
+ const casted1 = @ptrCast(*const u8, runtime_nothing);
+ const casted2 = @ptrCast(fn () callconv(.C) void, casted1);
+ casted2();
+}
+
+fn nothing() callconv(.C) void {}
+
+test "return error union with 128-bit integer" {
+ expect(3 == try give());
+}
+fn give() anyerror!u128 {
+ return 3;
+}
+
+test "alignment of >= 128-bit integer type" {
+ expect(@alignOf(u128) == 16);
+ expect(@alignOf(u129) == 16);
+}
+
+test "alignment of struct with 128-bit field" {
+ expect(@alignOf(struct {
+ x: u128,
+ }) == 16);
+
+ comptime {
+ expect(@alignOf(struct {
+ x: u128,
+ }) == 16);
+ }
+}
+
+test "size of extern struct with 128-bit field" {
+ expect(@sizeOf(extern struct {
+ x: u128,
+ y: u8,
+ }) == 32);
+
+ comptime {
+ expect(@sizeOf(extern struct {
+ x: u128,
+ y: u8,
+ }) == 32);
+ }
+}
+
+const DefaultAligned = struct {
+ nevermind: u32,
+ badguy: i128,
+};
+
+test "read 128-bit field from default aligned struct in stack memory" {
+ var default_aligned = DefaultAligned{
+ .nevermind = 1,
+ .badguy = 12,
+ };
+ expect((@ptrToInt(&default_aligned.badguy) % 16) == 0);
+ expect(12 == default_aligned.badguy);
+}
+
+var default_aligned_global = DefaultAligned{
+ .nevermind = 1,
+ .badguy = 12,
+};
+
+test "read 128-bit field from default aligned struct in global memory" {
+ expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0);
+ expect(12 == default_aligned_global.badguy);
+}
+
+test "struct field explicit alignment" {
+ const S = struct {
+ const Node = struct {
+ next: *Node,
+ massive_byte: u8 align(64),
+ };
+ };
+
+ var node: S.Node = undefined;
+ node.massive_byte = 100;
+ expect(node.massive_byte == 100);
+ comptime expect(@TypeOf(&node.massive_byte) == *align(64) u8);
+ expect(@ptrToInt(&node.massive_byte) % 64 == 0);
+}
+
+test "align(@alignOf(T)) T does not force resolution of T" {
+ const S = struct {
+ const A = struct {
+ a: *align(@alignOf(A)) A,
+ };
+ fn doTheTest() void {
+ suspend {
+ resume @frame();
+ }
+ _ = bar(@Frame(doTheTest));
+ }
+ fn bar(comptime T: type) *align(@alignOf(T)) T {
+ ok = true;
+ return undefined;
+ }
+
+ var ok = false;
+ };
+ _ = async S.doTheTest();
+ expect(S.ok);
+}
+
+test "align(N) on functions" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);
+}
+fn overaligned_fn() align(0x1000) i32 {
+ return 42;
+}
diff --git a/test/behavior/alignof.zig b/test/behavior/alignof.zig
new file mode 100644
index 0000000000..d2ead5d2b8
--- /dev/null
+++ b/test/behavior/alignof.zig
@@ -0,0 +1,39 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const builtin = @import("builtin");
+const native_arch = builtin.target.cpu.arch;
+const maxInt = std.math.maxInt;
+
+const Foo = struct {
+ x: u32,
+ y: u32,
+ z: u32,
+};
+
+test "@alignOf(T) before referencing T" {
+ comptime expect(@alignOf(Foo) != maxInt(usize));
+ if (native_arch == .x86_64) {
+ comptime expect(@alignOf(Foo) == 4);
+ }
+}
+
+test "comparison of @alignOf(T) against zero" {
+ {
+ const T = struct { x: u32 };
+ expect(!(@alignOf(T) == 0));
+ expect(@alignOf(T) != 0);
+ expect(!(@alignOf(T) < 0));
+ expect(!(@alignOf(T) <= 0));
+ expect(@alignOf(T) > 0);
+ expect(@alignOf(T) >= 0);
+ }
+ {
+ const T = struct {};
+ expect(@alignOf(T) == 0);
+ expect(!(@alignOf(T) != 0));
+ expect(!(@alignOf(T) < 0));
+ expect(@alignOf(T) <= 0);
+ expect(!(@alignOf(T) > 0));
+ expect(@alignOf(T) >= 0);
+ }
+}
diff --git a/test/behavior/array.zig b/test/behavior/array.zig
new file mode 100644
index 0000000000..39ccc72960
--- /dev/null
+++ b/test/behavior/array.zig
@@ -0,0 +1,489 @@
+const std = @import("std");
+const testing = std.testing;
+const mem = std.mem;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
+
+test "arrays" {
+ var array: [5]u32 = undefined;
+
+ var i: u32 = 0;
+ while (i < 5) {
+ array[i] = i + 1;
+ i = array[i];
+ }
+
+ i = 0;
+ var accumulator = @as(u32, 0);
+ while (i < 5) {
+ accumulator += array[i];
+
+ i += 1;
+ }
+
+ expect(accumulator == 15);
+ expect(getArrayLen(&array) == 5);
+}
+fn getArrayLen(a: []const u32) usize {
+ return a.len;
+}
+
+test "array with sentinels" {
+ const S = struct {
+ fn doTheTest(is_ct: bool) void {
+ if (is_ct) {
+ var zero_sized: [0:0xde]u8 = [_:0xde]u8{};
+ // Disabled at runtime because of
+ // https://github.com/ziglang/zig/issues/4372
+ expectEqual(@as(u8, 0xde), zero_sized[0]);
+ var reinterpreted = @ptrCast(*[1]u8, &zero_sized);
+ expectEqual(@as(u8, 0xde), reinterpreted[0]);
+ }
+ var arr: [3:0x55]u8 = undefined;
+ // Make sure the sentinel pointer is pointing after the last element
+ if (!is_ct) {
+ const sentinel_ptr = @ptrToInt(&arr[3]);
+ const last_elem_ptr = @ptrToInt(&arr[2]);
+ expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr);
+ }
+ // Make sure the sentinel is writeable
+ arr[3] = 0x55;
+ }
+ };
+
+ S.doTheTest(false);
+ comptime S.doTheTest(true);
+}
+
+test "void arrays" {
+ var array: [4]void = undefined;
+ array[0] = void{};
+ array[1] = array[2];
+ expect(@sizeOf(@TypeOf(array)) == 0);
+ expect(array.len == 4);
+}
+
+test "array literal" {
+ const hex_mult = [_]u16{
+ 4096,
+ 256,
+ 16,
+ 1,
+ };
+
+ expect(hex_mult.len == 4);
+ expect(hex_mult[1] == 256);
+}
+
+test "array dot len const expr" {
+ expect(comptime x: {
+ break :x some_array.len == 4;
+ });
+}
+
+const ArrayDotLenConstExpr = struct {
+ y: [some_array.len]u8,
+};
+const some_array = [_]u8{
+ 0,
+ 1,
+ 2,
+ 3,
+};
+
+test "nested arrays" {
+ const array_of_strings = [_][]const u8{
+ "hello",
+ "this",
+ "is",
+ "my",
+ "thing",
+ };
+ for (array_of_strings) |s, i| {
+ if (i == 0) expect(mem.eql(u8, s, "hello"));
+ if (i == 1) expect(mem.eql(u8, s, "this"));
+ if (i == 2) expect(mem.eql(u8, s, "is"));
+ if (i == 3) expect(mem.eql(u8, s, "my"));
+ if (i == 4) expect(mem.eql(u8, s, "thing"));
+ }
+}
+
+var s_array: [8]Sub = undefined;
+const Sub = struct {
+ b: u8,
+};
+const Str = struct {
+ a: []Sub,
+};
+test "set global var array via slice embedded in struct" {
+ var s = Str{ .a = s_array[0..] };
+
+ s.a[0].b = 1;
+ s.a[1].b = 2;
+ s.a[2].b = 3;
+
+ expect(s_array[0].b == 1);
+ expect(s_array[1].b == 2);
+ expect(s_array[2].b == 3);
+}
+
+test "array literal with specified size" {
+ var array = [2]u8{
+ 1,
+ 2,
+ };
+ expect(array[0] == 1);
+ expect(array[1] == 2);
+}
+
+test "array len field" {
+ var arr = [4]u8{ 0, 0, 0, 0 };
+ var ptr = &arr;
+ expect(arr.len == 4);
+ comptime expect(arr.len == 4);
+ expect(ptr.len == 4);
+ comptime expect(ptr.len == 4);
+}
+
+test "single-item pointer to array indexing and slicing" {
+ testSingleItemPtrArrayIndexSlice();
+ comptime testSingleItemPtrArrayIndexSlice();
+}
+
+fn testSingleItemPtrArrayIndexSlice() void {
+ {
+ var array: [4]u8 = "aaaa".*;
+ doSomeMangling(&array);
+ expect(mem.eql(u8, "azya", &array));
+ }
+ {
+ var array = "aaaa".*;
+ doSomeMangling(&array);
+ expect(mem.eql(u8, "azya", &array));
+ }
+}
+
+fn doSomeMangling(array: *[4]u8) void {
+ array[1] = 'z';
+ array[2..3][0] = 'y';
+}
+
+test "implicit cast single-item pointer" {
+ testImplicitCastSingleItemPtr();
+ comptime testImplicitCastSingleItemPtr();
+}
+
+fn testImplicitCastSingleItemPtr() void {
+ var byte: u8 = 100;
+ const slice = @as(*[1]u8, &byte)[0..];
+ slice[0] += 1;
+ expect(byte == 101);
+}
+
+fn testArrayByValAtComptime(b: [2]u8) u8 {
+ return b[0];
+}
+
+test "comptime evalutating function that takes array by value" {
+ const arr = [_]u8{ 0, 1 };
+ _ = comptime testArrayByValAtComptime(arr);
+ _ = comptime testArrayByValAtComptime(arr);
+}
+
+test "implicit comptime in array type size" {
+ var arr: [plusOne(10)]bool = undefined;
+ expect(arr.len == 11);
+}
+
+fn plusOne(x: u32) u32 {
+ return x + 1;
+}
+
+test "runtime initialize array elem and then implicit cast to slice" {
+ var two: i32 = 2;
+ const x: []const i32 = &[_]i32{two};
+ expect(x[0] == 2);
+}
+
+test "array literal as argument to function" {
+ const S = struct {
+ fn entry(two: i32) void {
+ foo(&[_]i32{
+ 1,
+ 2,
+ 3,
+ });
+ foo(&[_]i32{
+ 1,
+ two,
+ 3,
+ });
+ foo2(true, &[_]i32{
+ 1,
+ 2,
+ 3,
+ });
+ foo2(true, &[_]i32{
+ 1,
+ two,
+ 3,
+ });
+ }
+ fn foo(x: []const i32) void {
+ expect(x[0] == 1);
+ expect(x[1] == 2);
+ expect(x[2] == 3);
+ }
+ fn foo2(trash: bool, x: []const i32) void {
+ expect(trash);
+ expect(x[0] == 1);
+ expect(x[1] == 2);
+ expect(x[2] == 3);
+ }
+ };
+ S.entry(2);
+ comptime S.entry(2);
+}
+
+test "double nested array to const slice cast in array literal" {
+ const S = struct {
+ fn entry(two: i32) void {
+ const cases = [_][]const []const i32{
+ &[_][]const i32{&[_]i32{1}},
+ &[_][]const i32{&[_]i32{ 2, 3 }},
+ &[_][]const i32{
+ &[_]i32{4},
+ &[_]i32{ 5, 6, 7 },
+ },
+ };
+ check(&cases);
+
+ const cases2 = [_][]const i32{
+ &[_]i32{1},
+ &[_]i32{ two, 3 },
+ };
+ expect(cases2.len == 2);
+ expect(cases2[0].len == 1);
+ expect(cases2[0][0] == 1);
+ expect(cases2[1].len == 2);
+ expect(cases2[1][0] == 2);
+ expect(cases2[1][1] == 3);
+
+ const cases3 = [_][]const []const i32{
+ &[_][]const i32{&[_]i32{1}},
+ &[_][]const i32{&[_]i32{ two, 3 }},
+ &[_][]const i32{
+ &[_]i32{4},
+ &[_]i32{ 5, 6, 7 },
+ },
+ };
+ check(&cases3);
+ }
+
+ fn check(cases: []const []const []const i32) void {
+ expect(cases.len == 3);
+ expect(cases[0].len == 1);
+ expect(cases[0][0].len == 1);
+ expect(cases[0][0][0] == 1);
+ expect(cases[1].len == 1);
+ expect(cases[1][0].len == 2);
+ expect(cases[1][0][0] == 2);
+ expect(cases[1][0][1] == 3);
+ expect(cases[2].len == 2);
+ expect(cases[2][0].len == 1);
+ expect(cases[2][0][0] == 4);
+ expect(cases[2][1].len == 3);
+ expect(cases[2][1][0] == 5);
+ expect(cases[2][1][1] == 6);
+ expect(cases[2][1][2] == 7);
+ }
+ };
+ S.entry(2);
+ comptime S.entry(2);
+}
+
+test "read/write through global variable array of struct fields initialized via array mult" {
+ const S = struct {
+ fn doTheTest() void {
+ expect(storage[0].term == 1);
+ storage[0] = MyStruct{ .term = 123 };
+ expect(storage[0].term == 123);
+ }
+
+ pub const MyStruct = struct {
+ term: usize,
+ };
+
+ var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
+ };
+ S.doTheTest();
+}
+
+test "implicit cast zero sized array ptr to slice" {
+ {
+ var b = "".*;
+ const c: []const u8 = &b;
+ expect(c.len == 0);
+ }
+ {
+ var b: [0]u8 = "".*;
+ const c: []const u8 = &b;
+ expect(c.len == 0);
+ }
+}
+
+test "anonymous list literal syntax" {
+ const S = struct {
+ fn doTheTest() void {
+ var array: [4]u8 = .{ 1, 2, 3, 4 };
+ expect(array[0] == 1);
+ expect(array[1] == 2);
+ expect(array[2] == 3);
+ expect(array[3] == 4);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "anonymous literal in array" {
+ const S = struct {
+ const Foo = struct {
+ a: usize = 2,
+ b: usize = 4,
+ };
+ fn doTheTest() void {
+ var array: [2]Foo = .{
+ .{ .a = 3 },
+ .{ .b = 3 },
+ };
+ expect(array[0].a == 3);
+ expect(array[0].b == 4);
+ expect(array[1].a == 2);
+ expect(array[1].b == 3);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "access the null element of a null terminated array" {
+ const S = struct {
+ fn doTheTest() void {
+ var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' };
+ expect(array[4] == 0);
+ var len: usize = 4;
+ expect(array[len] == 0);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "type deduction for array subscript expression" {
+ const S = struct {
+ fn doTheTest() void {
+ var array = [_]u8{ 0x55, 0xAA };
+ var v0 = true;
+ expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]);
+ var v1 = false;
+ expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "sentinel element count towards the ABI size calculation" {
+ const S = struct {
+ fn doTheTest() void {
+ const T = packed struct {
+ fill_pre: u8 = 0x55,
+ data: [0:0]u8 = undefined,
+ fill_post: u8 = 0xAA,
+ };
+ var x = T{};
+ var as_slice = mem.asBytes(&x);
+ expectEqual(@as(usize, 3), as_slice.len);
+ expectEqual(@as(u8, 0x55), as_slice[0]);
+ expectEqual(@as(u8, 0xAA), as_slice[2]);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "zero-sized array with recursive type definition" {
+ const U = struct {
+ fn foo(comptime T: type, comptime n: usize) type {
+ return struct {
+ s: [n]T,
+ x: usize = n,
+ };
+ }
+ };
+
+ const S = struct {
+ list: U.foo(@This(), 0),
+ };
+
+ var t: S = .{ .list = .{ .s = undefined } };
+ expectEqual(@as(usize, 0), t.list.x);
+}
+
+test "type coercion of anon struct literal to array" {
+ const S = struct {
+ const U = union{
+ a: u32,
+ b: bool,
+ c: []const u8,
+ };
+
+ fn doTheTest() void {
+ var x1: u8 = 42;
+ const t1 = .{ x1, 56, 54 };
+ var arr1: [3]u8 = t1;
+ expect(arr1[0] == 42);
+ expect(arr1[1] == 56);
+ expect(arr1[2] == 54);
+
+ var x2: U = .{ .a = 42 };
+ const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
+ var arr2: [3]U = t2;
+ expect(arr2[0].a == 42);
+ expect(arr2[1].b == true);
+ expect(mem.eql(u8, arr2[2].c, "hello"));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "type coercion of pointer to anon struct literal to pointer to array" {
+ const S = struct {
+ const U = union{
+ a: u32,
+ b: bool,
+ c: []const u8,
+ };
+
+ fn doTheTest() void {
+ var x1: u8 = 42;
+ const t1 = &.{ x1, 56, 54 };
+ var arr1: *const[3]u8 = t1;
+ expect(arr1[0] == 42);
+ expect(arr1[1] == 56);
+ expect(arr1[2] == 54);
+
+ var x2: U = .{ .a = 42 };
+ const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } };
+ var arr2: *const [3]U = t2;
+ expect(arr2[0].a == 42);
+ expect(arr2[1].b == true);
+ expect(mem.eql(u8, arr2[2].c, "hello"));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig
new file mode 100644
index 0000000000..170ad3325d
--- /dev/null
+++ b/test/behavior/asm.zig
@@ -0,0 +1,94 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const is_x86_64_linux = std.Target.current.cpu.arch == .x86_64 and std.Target.current.os.tag == .linux;
+
+comptime {
+ if (is_x86_64_linux) {
+ asm (
+ \\.globl this_is_my_alias;
+ \\.type this_is_my_alias, @function;
+ \\.set this_is_my_alias, derp;
+ );
+ }
+}
+
+test "module level assembly" {
+ if (is_x86_64_linux) {
+ expect(this_is_my_alias() == 1234);
+ }
+}
+
+test "output constraint modifiers" {
+ // This is only testing compilation.
+ var a: u32 = 3;
+ asm volatile (""
+ : [_] "=m,r" (a)
+ :
+ : ""
+ );
+ asm volatile (""
+ : [_] "=r,m" (a)
+ :
+ : ""
+ );
+}
+
+test "alternative constraints" {
+ // Make sure we allow commas as a separator for alternative constraints.
+ var a: u32 = 3;
+ asm volatile (""
+ : [_] "=r,m" (a)
+ : [_] "r,m" (a)
+ : ""
+ );
+}
+
+test "sized integer/float in asm input" {
+ asm volatile (""
+ :
+ : [_] "m" (@as(usize, 3))
+ : ""
+ );
+ asm volatile (""
+ :
+ : [_] "m" (@as(i15, -3))
+ : ""
+ );
+ asm volatile (""
+ :
+ : [_] "m" (@as(u3, 3))
+ : ""
+ );
+ asm volatile (""
+ :
+ : [_] "m" (@as(i3, 3))
+ : ""
+ );
+ asm volatile (""
+ :
+ : [_] "m" (@as(u121, 3))
+ : ""
+ );
+ asm volatile (""
+ :
+ : [_] "m" (@as(i121, 3))
+ : ""
+ );
+ asm volatile (""
+ :
+ : [_] "m" (@as(f32, 3.17))
+ : ""
+ );
+ asm volatile (""
+ :
+ : [_] "m" (@as(f64, 3.17))
+ : ""
+ );
+}
+
+extern fn this_is_my_alias() i32;
+
+export fn derp() i32 {
+ return 1234;
+}
diff --git a/test/behavior/async_fn.zig b/test/behavior/async_fn.zig
new file mode 100644
index 0000000000..0765eac7e8
--- /dev/null
+++ b/test/behavior/async_fn.zig
@@ -0,0 +1,1673 @@
+const std = @import("std");
+const builtin = std.builtin;
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectEqualStrings = std.testing.expectEqualStrings;
+const expectError = std.testing.expectError;
+
+var global_x: i32 = 1;
+
+test "simple coroutine suspend and resume" {
+ var frame = async simpleAsyncFn();
+ expect(global_x == 2);
+ resume frame;
+ expect(global_x == 3);
+ const af: anyframe->void = &frame;
+ resume frame;
+ expect(global_x == 4);
+}
+fn simpleAsyncFn() void {
+ global_x += 1;
+ suspend {}
+ global_x += 1;
+ suspend {}
+ global_x += 1;
+}
+
+var global_y: i32 = 1;
+
+test "pass parameter to coroutine" {
+ var p = async simpleAsyncFnWithArg(2);
+ expect(global_y == 3);
+ resume p;
+ expect(global_y == 5);
+}
+fn simpleAsyncFnWithArg(delta: i32) void {
+ global_y += delta;
+ suspend {}
+ global_y += delta;
+}
+
+test "suspend at end of function" {
+ const S = struct {
+ var x: i32 = 1;
+
+ fn doTheTest() void {
+ expect(x == 1);
+ const p = async suspendAtEnd();
+ expect(x == 2);
+ }
+
+ fn suspendAtEnd() void {
+ x += 1;
+ suspend {}
+ }
+ };
+ S.doTheTest();
+}
+
+test "local variable in async function" {
+ const S = struct {
+ var x: i32 = 0;
+
+ fn doTheTest() void {
+ expect(x == 0);
+ var p = async add(1, 2);
+ expect(x == 0);
+ resume p;
+ expect(x == 0);
+ resume p;
+ expect(x == 0);
+ resume p;
+ expect(x == 3);
+ }
+
+ fn add(a: i32, b: i32) void {
+ var accum: i32 = 0;
+ suspend {}
+ accum += a;
+ suspend {}
+ accum += b;
+ suspend {}
+ x = accum;
+ }
+ };
+ S.doTheTest();
+}
+
+test "calling an inferred async function" {
+ const S = struct {
+ var x: i32 = 1;
+ var other_frame: *@Frame(other) = undefined;
+
+ fn doTheTest() void {
+ _ = async first();
+ expect(x == 1);
+ resume other_frame.*;
+ expect(x == 2);
+ }
+
+ fn first() void {
+ other();
+ }
+ fn other() void {
+ other_frame = @frame();
+ suspend {}
+ x += 1;
+ }
+ };
+ S.doTheTest();
+}
+
+test "@frameSize" {
+ const S = struct {
+ fn doTheTest() void {
+ {
+ var ptr = @ptrCast(fn (i32) callconv(.Async) void, other);
+ const size = @frameSize(ptr);
+ expect(size == @sizeOf(@Frame(other)));
+ }
+ {
+ var ptr = @ptrCast(fn () callconv(.Async) void, first);
+ const size = @frameSize(ptr);
+ expect(size == @sizeOf(@Frame(first)));
+ }
+ }
+
+ fn first() void {
+ other(1);
+ }
+ fn other(param: i32) void {
+ var local: i32 = undefined;
+ suspend {}
+ }
+ };
+ S.doTheTest();
+}
+
+test "coroutine suspend, resume" {
+ const S = struct {
+ var frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ _ = async amain();
+ seq('d');
+ resume frame;
+ seq('h');
+
+ expect(std.mem.eql(u8, &points, "abcdefgh"));
+ }
+
+ fn amain() void {
+ seq('a');
+ var f = async testAsyncSeq();
+ seq('c');
+ await f;
+ seq('g');
+ }
+
+ fn testAsyncSeq() void {
+ defer seq('f');
+
+ seq('b');
+ suspend {
+ frame = @frame();
+ }
+ seq('e');
+ }
+ var points = [_]u8{'x'} ** "abcdefgh".len;
+ var index: usize = 0;
+
+ fn seq(c: u8) void {
+ points[index] = c;
+ index += 1;
+ }
+ };
+ S.doTheTest();
+}
+
+test "coroutine suspend with block" {
+ const p = async testSuspendBlock();
+ expect(!global_result);
+ resume a_promise;
+ expect(global_result);
+}
+
+var a_promise: anyframe = undefined;
+var global_result = false;
+fn testSuspendBlock() callconv(.Async) void {
+ suspend {
+ comptime expect(@TypeOf(@frame()) == *@Frame(testSuspendBlock));
+ a_promise = @frame();
+ }
+
+ // Test to make sure that @frame() works as advertised (issue #1296)
+ // var our_handle: anyframe = @frame();
+ expect(a_promise == @as(anyframe, @frame()));
+
+ global_result = true;
+}
+
+var await_a_promise: anyframe = undefined;
+var await_final_result: i32 = 0;
+
+test "coroutine await" {
+ await_seq('a');
+ var p = async await_amain();
+ await_seq('f');
+ resume await_a_promise;
+ await_seq('i');
+ expect(await_final_result == 1234);
+ expect(std.mem.eql(u8, &await_points, "abcdefghi"));
+}
+fn await_amain() callconv(.Async) void {
+ await_seq('b');
+ var p = async await_another();
+ await_seq('e');
+ await_final_result = await p;
+ await_seq('h');
+}
+fn await_another() callconv(.Async) i32 {
+ await_seq('c');
+ suspend {
+ await_seq('d');
+ await_a_promise = @frame();
+ }
+ await_seq('g');
+ return 1234;
+}
+
+var await_points = [_]u8{0} ** "abcdefghi".len;
+var await_seq_index: usize = 0;
+
+fn await_seq(c: u8) void {
+ await_points[await_seq_index] = c;
+ await_seq_index += 1;
+}
+
+var early_final_result: i32 = 0;
+
+test "coroutine await early return" {
+ early_seq('a');
+ var p = async early_amain();
+ early_seq('f');
+ expect(early_final_result == 1234);
+ expect(std.mem.eql(u8, &early_points, "abcdef"));
+}
+fn early_amain() callconv(.Async) void {
+ early_seq('b');
+ var p = async early_another();
+ early_seq('d');
+ early_final_result = await p;
+ early_seq('e');
+}
+fn early_another() callconv(.Async) i32 {
+ early_seq('c');
+ return 1234;
+}
+
+var early_points = [_]u8{0} ** "abcdef".len;
+var early_seq_index: usize = 0;
+
+fn early_seq(c: u8) void {
+ early_points[early_seq_index] = c;
+ early_seq_index += 1;
+}
+
+test "async function with dot syntax" {
+ const S = struct {
+ var y: i32 = 1;
+ fn foo() callconv(.Async) void {
+ y += 1;
+ suspend {}
+ }
+ };
+ const p = async S.foo();
+ expect(S.y == 2);
+}
+
+test "async fn pointer in a struct field" {
+ var data: i32 = 1;
+ const Foo = struct {
+ bar: fn (*i32) callconv(.Async) void,
+ };
+ var foo = Foo{ .bar = simpleAsyncFn2 };
+ var bytes: [64]u8 align(16) = undefined;
+ const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
+ comptime expect(@TypeOf(f) == anyframe->void);
+ expect(data == 2);
+ resume f;
+ expect(data == 4);
+ _ = async doTheAwait(f);
+ expect(data == 4);
+}
+
+fn doTheAwait(f: anyframe->void) void {
+ await f;
+}
+fn simpleAsyncFn2(y: *i32) callconv(.Async) void {
+ defer y.* += 2;
+ y.* += 1;
+ suspend {}
+}
+
+test "@asyncCall with return type" {
+ const Foo = struct {
+ bar: fn () callconv(.Async) i32,
+
+ var global_frame: anyframe = undefined;
+ fn middle() callconv(.Async) i32 {
+ return afunc();
+ }
+
+ fn afunc() i32 {
+ global_frame = @frame();
+ suspend {}
+ return 1234;
+ }
+ };
+ var foo = Foo{ .bar = Foo.middle };
+ var bytes: [150]u8 align(16) = undefined;
+ var aresult: i32 = 0;
+ _ = @asyncCall(&bytes, &aresult, foo.bar, .{});
+ expect(aresult == 0);
+ resume Foo.global_frame;
+ expect(aresult == 1234);
+}
+
+test "async fn with inferred error set" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ var frame: [1]@Frame(middle) = undefined;
+ var fn_ptr = middle;
+ var result: @typeInfo(@typeInfo(@TypeOf(fn_ptr)).Fn.return_type.?).ErrorUnion.error_set!void = undefined;
+ _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});
+ resume global_frame;
+ std.testing.expectError(error.Fail, result);
+ }
+ fn middle() callconv(.Async) !void {
+ var f = async middle2();
+ return await f;
+ }
+
+ fn middle2() !void {
+ return failing();
+ }
+
+ fn failing() !void {
+ global_frame = @frame();
+ suspend {}
+ return error.Fail;
+ }
+ };
+ S.doTheTest();
+}
+
+test "error return trace across suspend points - early return" {
+ const p = nonFailing();
+ resume p;
+ const p2 = async printTrace(p);
+}
+
+test "error return trace across suspend points - async return" {
+ const p = nonFailing();
+ const p2 = async printTrace(p);
+ resume p;
+}
+
+fn nonFailing() (anyframe->anyerror!void) {
+ const Static = struct {
+ var frame: @Frame(suspendThenFail) = undefined;
+ };
+ Static.frame = async suspendThenFail();
+ return &Static.frame;
+}
+fn suspendThenFail() callconv(.Async) anyerror!void {
+ suspend {}
+ return error.Fail;
+}
+fn printTrace(p: anyframe->(anyerror!void)) callconv(.Async) void {
+ (await p) catch |e| {
+ std.testing.expect(e == error.Fail);
+ if (@errorReturnTrace()) |trace| {
+ expect(trace.index == 1);
+ } else switch (builtin.mode) {
+ .Debug, .ReleaseSafe => @panic("expected return trace"),
+ .ReleaseFast, .ReleaseSmall => {},
+ }
+ };
+}
+
+test "break from suspend" {
+ var my_result: i32 = 1;
+ const p = async testBreakFromSuspend(&my_result);
+ std.testing.expect(my_result == 2);
+}
+fn testBreakFromSuspend(my_result: *i32) callconv(.Async) void {
+ suspend {
+ resume @frame();
+ }
+ my_result.* += 1;
+ suspend {}
+ my_result.* += 1;
+}
+
+test "heap allocated async function frame" {
+ const S = struct {
+ var x: i32 = 42;
+
+ fn doTheTest() !void {
+ const frame = try std.testing.allocator.create(@Frame(someFunc));
+ defer std.testing.allocator.destroy(frame);
+
+ expect(x == 42);
+ frame.* = async someFunc();
+ expect(x == 43);
+ resume frame;
+ expect(x == 44);
+ }
+
+ fn someFunc() void {
+ x += 1;
+ suspend {}
+ x += 1;
+ }
+ };
+ try S.doTheTest();
+}
+
+test "async function call return value" {
+ const S = struct {
+ var frame: anyframe = undefined;
+ var pt = Point{ .x = 10, .y = 11 };
+
+ fn doTheTest() void {
+ expectEqual(pt.x, 10);
+ expectEqual(pt.y, 11);
+ _ = async first();
+ expectEqual(pt.x, 10);
+ expectEqual(pt.y, 11);
+ resume frame;
+ expectEqual(pt.x, 1);
+ expectEqual(pt.y, 2);
+ }
+
+ fn first() void {
+ pt = second(1, 2);
+ }
+
+ fn second(x: i32, y: i32) Point {
+ return other(x, y);
+ }
+
+ fn other(x: i32, y: i32) Point {
+ frame = @frame();
+ suspend {}
+ return Point{
+ .x = x,
+ .y = y,
+ };
+ }
+
+ const Point = struct {
+ x: i32,
+ y: i32,
+ };
+ };
+ S.doTheTest();
+}
+
+test "suspension points inside branching control flow" {
+ const S = struct {
+ var result: i32 = 10;
+
+ fn doTheTest() void {
+ expect(10 == result);
+ var frame = async func(true);
+ expect(10 == result);
+ resume frame;
+ expect(11 == result);
+ resume frame;
+ expect(12 == result);
+ resume frame;
+ expect(13 == result);
+ }
+
+ fn func(b: bool) void {
+ while (b) {
+ suspend {}
+ result += 1;
+ }
+ }
+ };
+ S.doTheTest();
+}
+
+test "call async function which has struct return type" {
+ const S = struct {
+ var frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ _ = async atest();
+ resume frame;
+ }
+
+ fn atest() void {
+ const result = func();
+ expect(result.x == 5);
+ expect(result.y == 6);
+ }
+
+ const Point = struct {
+ x: usize,
+ y: usize,
+ };
+
+ fn func() Point {
+ suspend {
+ frame = @frame();
+ }
+ return Point{
+ .x = 5,
+ .y = 6,
+ };
+ }
+ };
+ S.doTheTest();
+}
+
+test "pass string literal to async function" {
+ const S = struct {
+ var frame: anyframe = undefined;
+ var ok: bool = false;
+
+ fn doTheTest() void {
+ _ = async hello("hello");
+ resume frame;
+ expect(ok);
+ }
+
+ fn hello(msg: []const u8) void {
+ frame = @frame();
+ suspend {}
+ expectEqualStrings("hello", msg);
+ ok = true;
+ }
+ };
+ S.doTheTest();
+}
+
+test "await inside an errdefer" {
+ const S = struct {
+ var frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ _ = async amainWrap();
+ resume frame;
+ }
+
+ fn amainWrap() !void {
+ var foo = async func();
+ errdefer await foo;
+ return error.Bad;
+ }
+
+ fn func() void {
+ frame = @frame();
+ suspend {}
+ }
+ };
+ S.doTheTest();
+}
+
+test "try in an async function with error union and non-zero-bit payload" {
+ const S = struct {
+ var frame: anyframe = undefined;
+ var ok = false;
+
+ fn doTheTest() void {
+ _ = async amain();
+ resume frame;
+ expect(ok);
+ }
+
+ fn amain() void {
+ std.testing.expectError(error.Bad, theProblem());
+ ok = true;
+ }
+
+ fn theProblem() ![]u8 {
+ frame = @frame();
+ suspend {}
+ const result = try other();
+ return result;
+ }
+
+ fn other() ![]u8 {
+ return error.Bad;
+ }
+ };
+ S.doTheTest();
+}
+
+test "returning a const error from async function" {
+ const S = struct {
+ var frame: anyframe = undefined;
+ var ok = false;
+
+ fn doTheTest() void {
+ _ = async amain();
+ resume frame;
+ expect(ok);
+ }
+
+ fn amain() !void {
+ var download_frame = async fetchUrl(10, "a string");
+ const download_text = try await download_frame;
+
+ @panic("should not get here");
+ }
+
+ fn fetchUrl(unused: i32, url: []const u8) ![]u8 {
+ frame = @frame();
+ suspend {}
+ ok = true;
+ return error.OutOfMemory;
+ }
+ };
+ S.doTheTest();
+}
+
+test "async/await typical usage" {
+ inline for ([_]bool{ false, true }) |b1| {
+ inline for ([_]bool{ false, true }) |b2| {
+ inline for ([_]bool{ false, true }) |b3| {
+ inline for ([_]bool{ false, true }) |b4| {
+ testAsyncAwaitTypicalUsage(b1, b2, b3, b4).doTheTest();
+ }
+ }
+ }
+ }
+}
+
+fn testAsyncAwaitTypicalUsage(
+ comptime simulate_fail_download: bool,
+ comptime simulate_fail_file: bool,
+ comptime suspend_download: bool,
+ comptime suspend_file: bool,
+) type {
+ return struct {
+ fn doTheTest() void {
+ _ = async amainWrap();
+ if (suspend_file) {
+ resume global_file_frame;
+ }
+ if (suspend_download) {
+ resume global_download_frame;
+ }
+ }
+ fn amainWrap() void {
+ if (amain()) |_| {
+ expect(!simulate_fail_download);
+ expect(!simulate_fail_file);
+ } else |e| switch (e) {
+ error.NoResponse => expect(simulate_fail_download),
+ error.FileNotFound => expect(simulate_fail_file),
+ else => @panic("test failure"),
+ }
+ }
+
+ fn amain() !void {
+ const allocator = std.testing.allocator;
+ var download_frame = async fetchUrl(allocator, "https://example.com/");
+ var download_awaited = false;
+ errdefer if (!download_awaited) {
+ if (await download_frame) |x| allocator.free(x) else |_| {}
+ };
+
+ var file_frame = async readFile(allocator, "something.txt");
+ var file_awaited = false;
+ errdefer if (!file_awaited) {
+ if (await file_frame) |x| allocator.free(x) else |_| {}
+ };
+
+ download_awaited = true;
+ const download_text = try await download_frame;
+ defer allocator.free(download_text);
+
+ file_awaited = true;
+ const file_text = try await file_frame;
+ defer allocator.free(file_text);
+
+ expect(std.mem.eql(u8, "expected download text", download_text));
+ expect(std.mem.eql(u8, "expected file text", file_text));
+ }
+
+ var global_download_frame: anyframe = undefined;
+ fn fetchUrl(allocator: *std.mem.Allocator, url: []const u8) anyerror![]u8 {
+ const result = try std.mem.dupe(allocator, u8, "expected download text");
+ errdefer allocator.free(result);
+ if (suspend_download) {
+ suspend {
+ global_download_frame = @frame();
+ }
+ }
+ if (simulate_fail_download) return error.NoResponse;
+ return result;
+ }
+
+ var global_file_frame: anyframe = undefined;
+ fn readFile(allocator: *std.mem.Allocator, filename: []const u8) anyerror![]u8 {
+ const result = try std.mem.dupe(allocator, u8, "expected file text");
+ errdefer allocator.free(result);
+ if (suspend_file) {
+ suspend {
+ global_file_frame = @frame();
+ }
+ }
+ if (simulate_fail_file) return error.FileNotFound;
+ return result;
+ }
+ };
+}
+
+test "alignment of local variables in async functions" {
+ const S = struct {
+ fn doTheTest() void {
+ var y: u8 = 123;
+ var x: u8 align(128) = 1;
+ expect(@ptrToInt(&x) % 128 == 0);
+ }
+ };
+ S.doTheTest();
+}
+
+test "no reason to resolve frame still works" {
+ _ = async simpleNothing();
+}
+fn simpleNothing() void {
+ var x: i32 = 1234;
+}
+
+test "async call a generic function" {
+ const S = struct {
+ fn doTheTest() void {
+ var f = async func(i32, 2);
+ const result = await f;
+ expect(result == 3);
+ }
+
+ fn func(comptime T: type, inc: T) T {
+ var x: T = 1;
+ suspend {
+ resume @frame();
+ }
+ x += inc;
+ return x;
+ }
+ };
+ _ = async S.doTheTest();
+}
+
+test "return from suspend block" {
+ const S = struct {
+ fn doTheTest() void {
+ expect(func() == 1234);
+ }
+ fn func() i32 {
+ suspend {
+ return 1234;
+ }
+ }
+ };
+ _ = async S.doTheTest();
+}
+
+test "struct parameter to async function is copied to the frame" {
+ const S = struct {
+ const Point = struct {
+ x: i32,
+ y: i32,
+ };
+
+ var frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ _ = async atest();
+ resume frame;
+ }
+
+ fn atest() void {
+ var f: @Frame(foo) = undefined;
+ bar(&f);
+ clobberStack(10);
+ }
+
+ fn clobberStack(x: i32) void {
+ if (x == 0) return;
+ clobberStack(x - 1);
+ var y: i32 = x;
+ }
+
+ fn bar(f: *@Frame(foo)) void {
+ var pt = Point{ .x = 1, .y = 2 };
+ f.* = async foo(pt);
+ var result = await f;
+ expect(result == 1);
+ }
+
+ fn foo(point: Point) i32 {
+ suspend {
+ frame = @frame();
+ }
+ return point.x;
+ }
+ };
+ S.doTheTest();
+}
+
+test "cast fn to async fn when it is inferred to be async" {
+ const S = struct {
+ var frame: anyframe = undefined;
+ var ok = false;
+
+ fn doTheTest() void {
+ var ptr: fn () callconv(.Async) i32 = undefined;
+ ptr = func;
+ var buf: [100]u8 align(16) = undefined;
+ var result: i32 = undefined;
+ const f = @asyncCall(&buf, &result, ptr, .{});
+ _ = await f;
+ expect(result == 1234);
+ ok = true;
+ }
+
+ fn func() i32 {
+ suspend {
+ frame = @frame();
+ }
+ return 1234;
+ }
+ };
+ _ = async S.doTheTest();
+ resume S.frame;
+ expect(S.ok);
+}
+
+test "cast fn to async fn when it is inferred to be async, awaited directly" {
+ const S = struct {
+ var frame: anyframe = undefined;
+ var ok = false;
+
+ fn doTheTest() void {
+ var ptr: fn () callconv(.Async) i32 = undefined;
+ ptr = func;
+ var buf: [100]u8 align(16) = undefined;
+ var result: i32 = undefined;
+ _ = await @asyncCall(&buf, &result, ptr, .{});
+ expect(result == 1234);
+ ok = true;
+ }
+
+ fn func() i32 {
+ suspend {
+ frame = @frame();
+ }
+ return 1234;
+ }
+ };
+ _ = async S.doTheTest();
+ resume S.frame;
+ expect(S.ok);
+}
+
+test "await does not force async if callee is blocking" {
+ const S = struct {
+ fn simple() i32 {
+ return 1234;
+ }
+ };
+ var x = async S.simple();
+ expect(await x == 1234);
+}
+
+test "recursive async function" {
+ expect(recursiveAsyncFunctionTest(false).doTheTest() == 55);
+ expect(recursiveAsyncFunctionTest(true).doTheTest() == 55);
+}
+
+fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type {
+ return struct {
+ fn fib(allocator: *std.mem.Allocator, x: u32) error{OutOfMemory}!u32 {
+ if (x <= 1) return x;
+
+ if (suspending_implementation) {
+ suspend {
+ resume @frame();
+ }
+ }
+
+ const f1 = try allocator.create(@Frame(fib));
+ defer allocator.destroy(f1);
+
+ const f2 = try allocator.create(@Frame(fib));
+ defer allocator.destroy(f2);
+
+ f1.* = async fib(allocator, x - 1);
+ var f1_awaited = false;
+ errdefer if (!f1_awaited) {
+ _ = await f1;
+ };
+
+ f2.* = async fib(allocator, x - 2);
+ var f2_awaited = false;
+ errdefer if (!f2_awaited) {
+ _ = await f2;
+ };
+
+ var sum: u32 = 0;
+
+ f1_awaited = true;
+ sum += try await f1;
+
+ f2_awaited = true;
+ sum += try await f2;
+
+ return sum;
+ }
+
+ fn doTheTest() u32 {
+ if (suspending_implementation) {
+ var result: u32 = undefined;
+ _ = async amain(&result);
+ return result;
+ } else {
+ return fib(std.testing.allocator, 10) catch unreachable;
+ }
+ }
+
+ fn amain(result: *u32) void {
+ var x = async fib(std.testing.allocator, 10);
+ result.* = (await x) catch unreachable;
+ }
+ };
+}
+
+test "@asyncCall with comptime-known function, but not awaited directly" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ var frame: [1]@Frame(middle) = undefined;
+ var result: @typeInfo(@typeInfo(@TypeOf(middle)).Fn.return_type.?).ErrorUnion.error_set!void = undefined;
+ _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});
+ resume global_frame;
+ std.testing.expectError(error.Fail, result);
+ }
+ fn middle() callconv(.Async) !void {
+ var f = async middle2();
+ return await f;
+ }
+
+ fn middle2() !void {
+ return failing();
+ }
+
+ fn failing() !void {
+ global_frame = @frame();
+ suspend {}
+ return error.Fail;
+ }
+ };
+ S.doTheTest();
+}
+
+test "@asyncCall with actual frame instead of byte buffer" {
+ const S = struct {
+ fn func() i32 {
+ suspend {}
+ return 1234;
+ }
+ };
+ var frame: @Frame(S.func) = undefined;
+ var result: i32 = undefined;
+ const ptr = @asyncCall(&frame, &result, S.func, .{});
+ resume ptr;
+ expect(result == 1234);
+}
+
+test "@asyncCall using the result location inside the frame" {
+ const S = struct {
+ fn simple2(y: *i32) callconv(.Async) i32 {
+ defer y.* += 2;
+ y.* += 1;
+ suspend {}
+ return 1234;
+ }
+ fn getAnswer(f: anyframe->i32, out: *i32) void {
+ out.* = await f;
+ }
+ };
+ var data: i32 = 1;
+ const Foo = struct {
+ bar: fn (*i32) callconv(.Async) i32,
+ };
+ var foo = Foo{ .bar = S.simple2 };
+ var bytes: [64]u8 align(16) = undefined;
+ const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
+ comptime expect(@TypeOf(f) == anyframe->i32);
+ expect(data == 2);
+ resume f;
+ expect(data == 4);
+ _ = async S.getAnswer(f, &data);
+ expect(data == 1234);
+}
+
+test "@TypeOf an async function call of generic fn with error union type" {
+ const S = struct {
+ fn func(comptime x: anytype) anyerror!i32 {
+ const T = @TypeOf(async func(x));
+ comptime expect(T == @typeInfo(@TypeOf(@frame())).Pointer.child);
+ return undefined;
+ }
+ };
+ _ = async S.func(i32);
+}
+
+test "using @TypeOf on a generic function call" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var global_ok = false;
+
+ var buf: [100]u8 align(16) = undefined;
+
+ fn amain(x: anytype) void {
+ if (x == 0) {
+ global_ok = true;
+ return;
+ }
+ suspend {
+ global_frame = @frame();
+ }
+ const F = @TypeOf(async amain(x - 1));
+ const frame = @intToPtr(*F, @ptrToInt(&buf));
+ return await @asyncCall(frame, {}, amain, .{x - 1});
+ }
+ };
+ _ = async S.amain(@as(u32, 1));
+ resume S.global_frame;
+ expect(S.global_ok);
+}
+
+test "recursive call of await @asyncCall with struct return type" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var global_ok = false;
+
+ var buf: [100]u8 align(16) = undefined;
+
+ fn amain(x: anytype) Foo {
+ if (x == 0) {
+ global_ok = true;
+ return Foo{ .x = 1, .y = 2, .z = 3 };
+ }
+ suspend {
+ global_frame = @frame();
+ }
+ const F = @TypeOf(async amain(x - 1));
+ const frame = @intToPtr(*F, @ptrToInt(&buf));
+ return await @asyncCall(frame, {}, amain, .{x - 1});
+ }
+
+ const Foo = struct {
+ x: u64,
+ y: u64,
+ z: u64,
+ };
+ };
+ var res: S.Foo = undefined;
+ var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined;
+ _ = @asyncCall(&frame, &res, S.amain, .{@as(u32, 1)});
+ resume S.global_frame;
+ expect(S.global_ok);
+ expect(res.x == 1);
+ expect(res.y == 2);
+ expect(res.z == 3);
+}
+
+test "nosuspend function call" {
+ const S = struct {
+ fn doTheTest() void {
+ const result = nosuspend add(50, 100);
+ expect(result == 150);
+ }
+ fn add(a: i32, b: i32) i32 {
+ if (a > 100) {
+ suspend {}
+ }
+ return a + b;
+ }
+ };
+ S.doTheTest();
+}
+
+test "await used in expression and awaiting fn with no suspend but async calling convention" {
+ const S = struct {
+ fn atest() void {
+ var f1 = async add(1, 2);
+ var f2 = async add(3, 4);
+
+ const sum = (await f1) + (await f2);
+ expect(sum == 10);
+ }
+ fn add(a: i32, b: i32) callconv(.Async) i32 {
+ return a + b;
+ }
+ };
+ _ = async S.atest();
+}
+
+test "await used in expression after a fn call" {
+ const S = struct {
+ fn atest() void {
+ var f1 = async add(3, 4);
+ var sum: i32 = 0;
+ sum = foo() + await f1;
+ expect(sum == 8);
+ }
+ fn add(a: i32, b: i32) callconv(.Async) i32 {
+ return a + b;
+ }
+ fn foo() i32 {
+ return 1;
+ }
+ };
+ _ = async S.atest();
+}
+
+test "async fn call used in expression after a fn call" {
+ const S = struct {
+ fn atest() void {
+ var sum: i32 = 0;
+ sum = foo() + add(3, 4);
+ expect(sum == 8);
+ }
+ fn add(a: i32, b: i32) callconv(.Async) i32 {
+ return a + b;
+ }
+ fn foo() i32 {
+ return 1;
+ }
+ };
+ _ = async S.atest();
+}
+
+test "suspend in for loop" {
+ const S = struct {
+ var global_frame: ?anyframe = null;
+
+ fn doTheTest() void {
+ _ = async atest();
+ while (global_frame) |f| resume f;
+ }
+
+ fn atest() void {
+ expect(func(&[_]u8{ 1, 2, 3 }) == 6);
+ }
+ fn func(stuff: []const u8) u32 {
+ global_frame = @frame();
+ var sum: u32 = 0;
+ for (stuff) |x| {
+ suspend {}
+ sum += x;
+ }
+ global_frame = null;
+ return sum;
+ }
+ };
+ S.doTheTest();
+}
+
+test "suspend in while loop" {
+ const S = struct {
+ var global_frame: ?anyframe = null;
+
+ fn doTheTest() void {
+ _ = async atest();
+ while (global_frame) |f| resume f;
+ }
+
+ fn atest() void {
+ expect(optional(6) == 6);
+ expect(errunion(6) == 6);
+ }
+ fn optional(stuff: ?u32) u32 {
+ global_frame = @frame();
+ defer global_frame = null;
+ while (stuff) |val| {
+ suspend {}
+ return val;
+ }
+ return 0;
+ }
+ fn errunion(stuff: anyerror!u32) u32 {
+ global_frame = @frame();
+ defer global_frame = null;
+ while (stuff) |val| {
+ suspend {}
+ return val;
+ } else |err| {
+ return 0;
+ }
+ }
+ };
+ S.doTheTest();
+}
+
+test "correctly spill when returning the error union result of another async fn" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ expect((atest() catch unreachable) == 1234);
+ }
+
+ fn atest() !i32 {
+ return fallible1();
+ }
+
+ fn fallible1() anyerror!i32 {
+ suspend {
+ global_frame = @frame();
+ }
+ return 1234;
+ }
+ };
+ _ = async S.doTheTest();
+ resume S.global_frame;
+}
+
+test "spill target expr in a for loop" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ var foo = Foo{
+ .slice = &[_]i32{ 1, 2 },
+ };
+ expect(atest(&foo) == 3);
+ }
+
+ const Foo = struct {
+ slice: []const i32,
+ };
+
+ fn atest(foo: *Foo) i32 {
+ var sum: i32 = 0;
+ for (foo.slice) |x| {
+ suspend {
+ global_frame = @frame();
+ }
+ sum += x;
+ }
+ return sum;
+ }
+ };
+ _ = async S.doTheTest();
+ resume S.global_frame;
+ resume S.global_frame;
+}
+
+test "spill target expr in a for loop, with a var decl in the loop body" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+
+ fn doTheTest() void {
+ var foo = Foo{
+ .slice = &[_]i32{ 1, 2 },
+ };
+ expect(atest(&foo) == 3);
+ }
+
+ const Foo = struct {
+ slice: []const i32,
+ };
+
+ fn atest(foo: *Foo) i32 {
+ var sum: i32 = 0;
+ for (foo.slice) |x| {
+ // Previously this var decl would prevent spills. This test makes sure
+ // the for loop spills still happen even though there is a VarDecl in scope
+ // before the suspend.
+ var anything = true;
+ _ = anything;
+ suspend {
+ global_frame = @frame();
+ }
+ sum += x;
+ }
+ return sum;
+ }
+ };
+ _ = async S.doTheTest();
+ resume S.global_frame;
+ resume S.global_frame;
+}
+
+test "async call with @call" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ fn doTheTest() void {
+ _ = @call(.{ .modifier = .async_kw }, atest, .{});
+ resume global_frame;
+ }
+ fn atest() void {
+ var frame = @call(.{ .modifier = .async_kw }, afoo, .{});
+ const res = await frame;
+ expect(res == 42);
+ }
+ fn afoo() i32 {
+ suspend {
+ global_frame = @frame();
+ }
+ return 42;
+ }
+ };
+ S.doTheTest();
+}
+
+test "async function passed 0-bit arg after non-0-bit arg" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var global_int: i32 = 0;
+
+ fn foo() void {
+ bar(1, .{}) catch unreachable;
+ }
+
+ fn bar(x: i32, args: anytype) anyerror!void {
+ global_frame = @frame();
+ suspend {}
+ global_int = x;
+ }
+ };
+ _ = async S.foo();
+ resume S.global_frame;
+ expect(S.global_int == 1);
+}
+
+test "async function passed align(16) arg after align(8) arg" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var global_int: u128 = 0;
+
+ fn foo() void {
+ var a: u128 = 99;
+ bar(10, .{a}) catch unreachable;
+ }
+
+ fn bar(x: u64, args: anytype) anyerror!void {
+ expect(x == 10);
+ global_frame = @frame();
+ suspend {}
+ global_int = args[0];
+ }
+ };
+ _ = async S.foo();
+ resume S.global_frame;
+ expect(S.global_int == 99);
+}
+
+test "async function call resolves target fn frame, comptime func" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var global_int: i32 = 9;
+
+ fn foo() anyerror!void {
+ const stack_size = 1000;
+ var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
+ return await @asyncCall(&stack_frame, {}, bar, .{});
+ }
+
+ fn bar() anyerror!void {
+ global_frame = @frame();
+ suspend {}
+ global_int += 1;
+ }
+ };
+ _ = async S.foo();
+ resume S.global_frame;
+ expect(S.global_int == 10);
+}
+
+test "async function call resolves target fn frame, runtime func" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var global_int: i32 = 9;
+
+ fn foo() anyerror!void {
+ const stack_size = 1000;
+ var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
+ var func: fn () callconv(.Async) anyerror!void = bar;
+ return await @asyncCall(&stack_frame, {}, func, .{});
+ }
+
+ fn bar() anyerror!void {
+ global_frame = @frame();
+ suspend {}
+ global_int += 1;
+ }
+ };
+ _ = async S.foo();
+ resume S.global_frame;
+ expect(S.global_int == 10);
+}
+
+test "properly spill optional payload capture value" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var global_int: usize = 2;
+
+ fn foo() void {
+ var opt: ?usize = 1234;
+ if (opt) |x| {
+ bar();
+ global_int += x;
+ }
+ }
+
+ fn bar() void {
+ global_frame = @frame();
+ suspend {}
+ global_int += 1;
+ }
+ };
+ _ = async S.foo();
+ resume S.global_frame;
+ expect(S.global_int == 1237);
+}
+
+test "handle defer interfering with return value spill" {
+ const S = struct {
+ var global_frame1: anyframe = undefined;
+ var global_frame2: anyframe = undefined;
+ var finished = false;
+ var baz_happened = false;
+
+ fn doTheTest() void {
+ _ = async testFoo();
+ resume global_frame1;
+ resume global_frame2;
+ expect(baz_happened);
+ expect(finished);
+ }
+
+ fn testFoo() void {
+ expectError(error.Bad, foo());
+ finished = true;
+ }
+
+ fn foo() anyerror!void {
+ defer baz();
+ return bar() catch |err| return err;
+ }
+
+ fn bar() anyerror!void {
+ global_frame1 = @frame();
+ suspend {}
+ return error.Bad;
+ }
+
+ fn baz() void {
+ global_frame2 = @frame();
+ suspend {}
+ baz_happened = true;
+ }
+ };
+ S.doTheTest();
+}
+
+test "take address of temporary async frame" {
+ const S = struct {
+ var global_frame: anyframe = undefined;
+ var finished = false;
+
+ fn doTheTest() void {
+ _ = async asyncDoTheTest();
+ resume global_frame;
+ expect(finished);
+ }
+
+ fn asyncDoTheTest() void {
+ expect(finishIt(&async foo(10)) == 1245);
+ finished = true;
+ }
+
+ fn foo(arg: i32) i32 {
+ global_frame = @frame();
+ suspend {}
+ return arg + 1234;
+ }
+
+ fn finishIt(frame: anyframe->i32) i32 {
+ return (await frame) + 1;
+ }
+ };
+ S.doTheTest();
+}
+
+test "nosuspend await" {
+ const S = struct {
+ var finished = false;
+
+ fn doTheTest() void {
+ var frame = async foo(false);
+ expect(nosuspend await frame == 42);
+ finished = true;
+ }
+
+ fn foo(want_suspend: bool) i32 {
+ if (want_suspend) {
+ suspend {}
+ }
+ return 42;
+ }
+ };
+ S.doTheTest();
+ expect(S.finished);
+}
+
+test "nosuspend on function calls" {
+ const S0 = struct {
+ b: i32 = 42,
+ };
+ const S1 = struct {
+ fn c() S0 {
+ return S0{};
+ }
+ fn d() !S0 {
+ return S0{};
+ }
+ };
+ expectEqual(@as(i32, 42), nosuspend S1.c().b);
+ expectEqual(@as(i32, 42), (try nosuspend S1.d()).b);
+}
+
+test "nosuspend on async function calls" {
+ const S0 = struct {
+ b: i32 = 42,
+ };
+ const S1 = struct {
+ fn c() S0 {
+ return S0{};
+ }
+ fn d() !S0 {
+ return S0{};
+ }
+ };
+ var frame_c = nosuspend async S1.c();
+ expectEqual(@as(i32, 42), (await frame_c).b);
+ var frame_d = nosuspend async S1.d();
+ expectEqual(@as(i32, 42), (try await frame_d).b);
+}
+
+// test "resume nosuspend async function calls" {
+// const S0 = struct {
+// b: i32 = 42,
+// };
+// const S1 = struct {
+// fn c() S0 {
+// suspend {}
+// return S0{};
+// }
+// fn d() !S0 {
+// suspend {}
+// return S0{};
+// }
+// };
+// var frame_c = nosuspend async S1.c();
+// resume frame_c;
+// expectEqual(@as(i32, 42), (await frame_c).b);
+// var frame_d = nosuspend async S1.d();
+// resume frame_d;
+// expectEqual(@as(i32, 42), (try await frame_d).b);
+// }
+
+test "nosuspend resume async function calls" {
+ const S0 = struct {
+ b: i32 = 42,
+ };
+ const S1 = struct {
+ fn c() S0 {
+ suspend {}
+ return S0{};
+ }
+ fn d() !S0 {
+ suspend {}
+ return S0{};
+ }
+ };
+ var frame_c = async S1.c();
+ nosuspend resume frame_c;
+ expectEqual(@as(i32, 42), (await frame_c).b);
+ var frame_d = async S1.d();
+ nosuspend resume frame_d;
+ expectEqual(@as(i32, 42), (try await frame_d).b);
+}
+
+test "avoid forcing frame alignment resolution implicit cast to *c_void" {
+ const S = struct {
+ var x: ?*c_void = null;
+
+ fn foo() bool {
+ suspend {
+ x = @frame();
+ }
+ return true;
+ }
+ };
+ var frame = async S.foo();
+ resume @ptrCast(anyframe->bool, @alignCast(@alignOf(@Frame(S.foo)), S.x));
+ expect(nosuspend await frame);
+}
+
+test "@asyncCall with pass-by-value arguments" {
+ const F0: u64 = 0xbeefbeefbeefbeef;
+ const F1: u64 = 0xf00df00df00df00d;
+ const F2: u64 = 0xcafecafecafecafe;
+
+ const S = struct {
+ pub const ST = struct { f0: usize, f1: usize };
+ pub const AT = [5]u8;
+
+ pub fn f(_fill0: u64, s: ST, _fill1: u64, a: AT, _fill2: u64) callconv(.Async) void {
+ // Check that the array and struct arguments passed by value don't
+ // end up overflowing the adjacent fields in the frame structure.
+ expectEqual(F0, _fill0);
+ expectEqual(F1, _fill1);
+ expectEqual(F2, _fill2);
+ }
+ };
+
+ var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined;
+ // The function pointer must not be comptime-known.
+ var t = S.f;
+ var frame_ptr = @asyncCall(&buffer, {}, t, .{
+ F0,
+ .{ .f0 = 1, .f1 = 2 },
+ F1,
+ [_]u8{ 1, 2, 3, 4, 5 },
+ F2,
+ });
+}
+
+test "@asyncCall with arguments having non-standard alignment" {
+ const F0: u64 = 0xbeefbeef;
+ const F1: u64 = 0xf00df00df00df00d;
+
+ const S = struct {
+ pub fn f(_fill0: u32, s: struct { x: u64 align(16) }, _fill1: u64) callconv(.Async) void {
+ // The compiler inserts extra alignment for s, check that the
+ // generated code picks the right slot for fill1.
+ expectEqual(F0, _fill0);
+ expectEqual(F1, _fill1);
+ }
+ };
+
+ var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined;
+ // The function pointer must not be comptime-known.
+ var t = S.f;
+ var frame_ptr = @asyncCall(&buffer, {}, t, .{ F0, undefined, F1 });
+}
diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig
new file mode 100644
index 0000000000..b1fd2b3001
--- /dev/null
+++ b/test/behavior/atomics.zig
@@ -0,0 +1,221 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const builtin = @import("builtin");
+
+test "cmpxchg" {
+ testCmpxchg();
+ comptime testCmpxchg();
+}
+
+fn testCmpxchg() void {
+ var x: i32 = 1234;
+ if (@cmpxchgWeak(i32, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| {
+ expect(x1 == 1234);
+ } else {
+ @panic("cmpxchg should have failed");
+ }
+
+ while (@cmpxchgWeak(i32, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| {
+ expect(x1 == 1234);
+ }
+ expect(x == 5678);
+
+ expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);
+ expect(x == 42);
+}
+
+test "fence" {
+ var x: i32 = 1234;
+ @fence(.SeqCst);
+ x = 5678;
+}
+
+test "atomicrmw and atomicload" {
+ var data: u8 = 200;
+ testAtomicRmw(&data);
+ expect(data == 42);
+ testAtomicLoad(&data);
+}
+
+fn testAtomicRmw(ptr: *u8) void {
+ const prev_value = @atomicRmw(u8, ptr, .Xchg, 42, .SeqCst);
+ expect(prev_value == 200);
+ comptime {
+ var x: i32 = 1234;
+ const y: i32 = 12345;
+ expect(@atomicLoad(i32, &x, .SeqCst) == 1234);
+ expect(@atomicLoad(i32, &y, .SeqCst) == 12345);
+ }
+}
+
+fn testAtomicLoad(ptr: *u8) void {
+ const x = @atomicLoad(u8, ptr, .SeqCst);
+ expect(x == 42);
+}
+
+test "cmpxchg with ptr" {
+ var data1: i32 = 1234;
+ var data2: i32 = 5678;
+ var data3: i32 = 9101;
+ var x: *i32 = &data1;
+ if (@cmpxchgWeak(*i32, &x, &data2, &data3, .SeqCst, .SeqCst)) |x1| {
+ expect(x1 == &data1);
+ } else {
+ @panic("cmpxchg should have failed");
+ }
+
+ while (@cmpxchgWeak(*i32, &x, &data1, &data3, .SeqCst, .SeqCst)) |x1| {
+ expect(x1 == &data1);
+ }
+ expect(x == &data3);
+
+ expect(@cmpxchgStrong(*i32, &x, &data3, &data2, .SeqCst, .SeqCst) == null);
+ expect(x == &data2);
+}
+
+// TODO this test is disabled until this issue is resolved:
+// https://github.com/ziglang/zig/issues/2883
+// otherwise cross compiling will result in:
+// lld: error: undefined symbol: __sync_val_compare_and_swap_16
+//test "128-bit cmpxchg" {
+// var x: u128 align(16) = 1234; // TODO: https://github.com/ziglang/zig/issues/2987
+// if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| {
+// expect(x1 == 1234);
+// } else {
+// @panic("cmpxchg should have failed");
+// }
+//
+// while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| {
+// expect(x1 == 1234);
+// }
+// expect(x == 5678);
+//
+// expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null);
+// expect(x == 42);
+//}
+
+test "cmpxchg with ignored result" {
+ var x: i32 = 1234;
+ var ptr = &x;
+
+ _ = @cmpxchgStrong(i32, &x, 1234, 5678, .Monotonic, .Monotonic);
+
+ expectEqual(@as(i32, 5678), x);
+}
+
+var a_global_variable = @as(u32, 1234);
+
+test "cmpxchg on a global variable" {
+ _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic);
+ expectEqual(@as(u32, 42), a_global_variable);
+}
+
+test "atomic load and rmw with enum" {
+ const Value = enum(u8) {
+ a,
+ b,
+ c,
+ };
+ var x = Value.a;
+
+ expect(@atomicLoad(Value, &x, .SeqCst) != .b);
+
+ _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst);
+ expect(@atomicLoad(Value, &x, .SeqCst) == .c);
+ expect(@atomicLoad(Value, &x, .SeqCst) != .a);
+ expect(@atomicLoad(Value, &x, .SeqCst) != .b);
+}
+
+test "atomic store" {
+ var x: u32 = 0;
+ @atomicStore(u32, &x, 1, .SeqCst);
+ expect(@atomicLoad(u32, &x, .SeqCst) == 1);
+ @atomicStore(u32, &x, 12345678, .SeqCst);
+ expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
+}
+
+test "atomic store comptime" {
+ comptime testAtomicStore();
+ testAtomicStore();
+}
+
+fn testAtomicStore() void {
+ var x: u32 = 0;
+ @atomicStore(u32, &x, 1, .SeqCst);
+ expect(@atomicLoad(u32, &x, .SeqCst) == 1);
+ @atomicStore(u32, &x, 12345678, .SeqCst);
+ expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
+}
+
+test "atomicrmw with floats" {
+ if (builtin.target.cpu.arch == .aarch64 or
+ builtin.target.cpu.arch == .arm or
+ builtin.target.cpu.arch == .riscv64)
+ {
+ // https://github.com/ziglang/zig/issues/4457
+ return error.SkipZigTest;
+ }
+ testAtomicRmwFloat();
+ comptime testAtomicRmwFloat();
+}
+
+fn testAtomicRmwFloat() void {
+ var x: f32 = 0;
+ expect(x == 0);
+ _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
+ expect(x == 1);
+ _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
+ expect(x == 6);
+ _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
+ expect(x == 4);
+}
+
+test "atomicrmw with ints" {
+ testAtomicRmwInt();
+ comptime testAtomicRmwInt();
+}
+
+fn testAtomicRmwInt() void {
+ var x: u8 = 1;
+ var res = @atomicRmw(u8, &x, .Xchg, 3, .SeqCst);
+ expect(x == 3 and res == 1);
+ _ = @atomicRmw(u8, &x, .Add, 3, .SeqCst);
+ expect(x == 6);
+ _ = @atomicRmw(u8, &x, .Sub, 1, .SeqCst);
+ expect(x == 5);
+ _ = @atomicRmw(u8, &x, .And, 4, .SeqCst);
+ expect(x == 4);
+ _ = @atomicRmw(u8, &x, .Nand, 4, .SeqCst);
+ expect(x == 0xfb);
+ _ = @atomicRmw(u8, &x, .Or, 6, .SeqCst);
+ expect(x == 0xff);
+ _ = @atomicRmw(u8, &x, .Xor, 2, .SeqCst);
+ expect(x == 0xfd);
+
+ _ = @atomicRmw(u8, &x, .Max, 1, .SeqCst);
+ expect(x == 0xfd);
+ _ = @atomicRmw(u8, &x, .Min, 1, .SeqCst);
+ expect(x == 1);
+}
+
+test "atomics with different types" {
+ testAtomicsWithType(bool, true, false);
+ inline for (.{ u1, i5, u15 }) |T| {
+ var x: T = 0;
+ testAtomicsWithType(T, 0, 1);
+ }
+ testAtomicsWithType(u0, 0, 0);
+ testAtomicsWithType(i0, 0, 0);
+}
+
+fn testAtomicsWithType(comptime T: type, a: T, b: T) void {
+ var x: T = b;
+ @atomicStore(T, &x, a, .SeqCst);
+ expect(x == a);
+ expect(@atomicLoad(T, &x, .SeqCst) == a);
+ expect(@atomicRmw(T, &x, .Xchg, b, .SeqCst) == a);
+ expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst) == null);
+ if (@sizeOf(T) != 0)
+ expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst).? == a);
+}
diff --git a/test/behavior/await_struct.zig b/test/behavior/await_struct.zig
new file mode 100644
index 0000000000..328f8f87fd
--- /dev/null
+++ b/test/behavior/await_struct.zig
@@ -0,0 +1,44 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const expect = std.testing.expect;
+
+const Foo = struct {
+ x: i32,
+};
+
+var await_a_promise: anyframe = undefined;
+var await_final_result = Foo{ .x = 0 };
+
+test "coroutine await struct" {
+ await_seq('a');
+ var p = async await_amain();
+ await_seq('f');
+ resume await_a_promise;
+ await_seq('i');
+ expect(await_final_result.x == 1234);
+ expect(std.mem.eql(u8, &await_points, "abcdefghi"));
+}
+fn await_amain() callconv(.Async) void {
+ await_seq('b');
+ var p = async await_another();
+ await_seq('e');
+ await_final_result = await p;
+ await_seq('h');
+}
+fn await_another() callconv(.Async) Foo {
+ await_seq('c');
+ suspend {
+ await_seq('d');
+ await_a_promise = @frame();
+ }
+ await_seq('g');
+ return Foo{ .x = 1234 };
+}
+
+var await_points = [_]u8{0} ** "abcdefghi".len;
+var await_seq_index: usize = 0;
+
+fn await_seq(c: u8) void {
+ await_points[await_seq_index] = c;
+ await_seq_index += 1;
+}
diff --git a/test/behavior/bit_shifting.zig b/test/behavior/bit_shifting.zig
new file mode 100644
index 0000000000..746d598237
--- /dev/null
+++ b/test/behavior/bit_shifting.zig
@@ -0,0 +1,104 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type {
+ const key_bits = @typeInfo(Key).Int.bits;
+ expect(Key == std.meta.Int(.unsigned, key_bits));
+ expect(key_bits >= mask_bit_count);
+ const shard_key_bits = mask_bit_count;
+ const ShardKey = std.meta.Int(.unsigned, mask_bit_count);
+ const shift_amount = key_bits - shard_key_bits;
+ return struct {
+ const Self = @This();
+ shards: [1 << shard_key_bits]?*Node,
+
+ pub fn create() Self {
+ return Self{ .shards = [_]?*Node{null} ** (1 << shard_key_bits) };
+ }
+
+ fn getShardKey(key: Key) ShardKey {
+ // https://github.com/ziglang/zig/issues/1544
+ // this special case is needed because you can't u32 >> 32.
+ if (ShardKey == u0) return 0;
+
+ // this can be u1 >> u0
+ const shard_key = key >> shift_amount;
+
+ // TODO: https://github.com/ziglang/zig/issues/1544
+ // This cast could be implicit if we teach the compiler that
+ // u32 >> 30 -> u2
+ return @intCast(ShardKey, shard_key);
+ }
+
+ pub fn put(self: *Self, node: *Node) void {
+ const shard_key = Self.getShardKey(node.key);
+ node.next = self.shards[shard_key];
+ self.shards[shard_key] = node;
+ }
+
+ pub fn get(self: *Self, key: Key) ?*Node {
+ const shard_key = Self.getShardKey(key);
+ var maybe_node = self.shards[shard_key];
+ while (maybe_node) |node| : (maybe_node = node.next) {
+ if (node.key == key) return node;
+ }
+ return null;
+ }
+
+ pub const Node = struct {
+ key: Key,
+ value: V,
+ next: ?*Node,
+
+ pub fn init(self: *Node, key: Key, value: V) void {
+ self.key = key;
+ self.value = value;
+ self.next = null;
+ }
+ };
+ };
+}
+
+test "sharded table" {
+ // realistic 16-way sharding
+ testShardedTable(u32, 4, 8);
+
+ testShardedTable(u5, 0, 32); // ShardKey == u0
+ testShardedTable(u5, 2, 32);
+ testShardedTable(u5, 5, 32);
+
+ testShardedTable(u1, 0, 2);
+ testShardedTable(u1, 1, 2); // this does u1 >> u0
+
+ testShardedTable(u0, 0, 1);
+}
+fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime node_count: comptime_int) void {
+ const Table = ShardedTable(Key, mask_bit_count, void);
+
+ var table = Table.create();
+ var node_buffer: [node_count]Table.Node = undefined;
+ for (node_buffer) |*node, i| {
+ const key = @intCast(Key, i);
+ expect(table.get(key) == null);
+ node.init(key, {});
+ table.put(node);
+ }
+
+ for (node_buffer) |*node, i| {
+ expect(table.get(@intCast(Key, i)) == node);
+ }
+}
+
+// #2225
+test "comptime shr of BigInt" {
+ comptime {
+ var n0 = 0xdeadbeef0000000000000000;
+ std.debug.assert(n0 >> 64 == 0xdeadbeef);
+ var n1 = 17908056155735594659;
+ std.debug.assert(n1 >> 64 == 0);
+ }
+}
+
+test "comptime shift safety check" {
+ const x = @as(usize, 42) << @sizeOf(usize);
+}
diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig
new file mode 100644
index 0000000000..abac1cc80a
--- /dev/null
+++ b/test/behavior/bitcast.zig
@@ -0,0 +1,197 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const maxInt = std.math.maxInt;
+const native_endian = builtin.target.cpu.arch.endian();
+
+test "@bitCast i32 -> u32" {
+ testBitCast_i32_u32();
+ comptime testBitCast_i32_u32();
+}
+
+fn testBitCast_i32_u32() void {
+ expect(conv(-1) == maxInt(u32));
+ expect(conv2(maxInt(u32)) == -1);
+}
+
+fn conv(x: i32) u32 {
+ return @bitCast(u32, x);
+}
+fn conv2(x: u32) i32 {
+ return @bitCast(i32, x);
+}
+
+test "@bitCast extern enum to its integer type" {
+ const SOCK = extern enum {
+ A,
+ B,
+
+ fn testBitCastExternEnum() void {
+ var SOCK_DGRAM = @This().B;
+ var sock_dgram = @bitCast(c_int, SOCK_DGRAM);
+ expect(sock_dgram == 1);
+ }
+ };
+
+ SOCK.testBitCastExternEnum();
+ comptime SOCK.testBitCastExternEnum();
+}
+
+test "@bitCast packed structs at runtime and comptime" {
+ const Full = packed struct {
+ number: u16,
+ };
+ const Divided = packed struct {
+ half1: u8,
+ quarter3: u4,
+ quarter4: u4,
+ };
+ const S = struct {
+ fn doTheTest() void {
+ var full = Full{ .number = 0x1234 };
+ var two_halves = @bitCast(Divided, full);
+ switch (native_endian) {
+ .Big => {
+ expect(two_halves.half1 == 0x12);
+ expect(two_halves.quarter3 == 0x3);
+ expect(two_halves.quarter4 == 0x4);
+ },
+ .Little => {
+ expect(two_halves.half1 == 0x34);
+ expect(two_halves.quarter3 == 0x2);
+ expect(two_halves.quarter4 == 0x1);
+ },
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "@bitCast extern structs at runtime and comptime" {
+ const Full = extern struct {
+ number: u16,
+ };
+ const TwoHalves = extern struct {
+ half1: u8,
+ half2: u8,
+ };
+ const S = struct {
+ fn doTheTest() void {
+ var full = Full{ .number = 0x1234 };
+ var two_halves = @bitCast(TwoHalves, full);
+ switch (native_endian) {
+ .Big => {
+ expect(two_halves.half1 == 0x12);
+ expect(two_halves.half2 == 0x34);
+ },
+ .Little => {
+ expect(two_halves.half1 == 0x34);
+ expect(two_halves.half2 == 0x12);
+ },
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "bitcast packed struct to integer and back" {
+ const LevelUpMove = packed struct {
+ move_id: u9,
+ level: u7,
+ };
+ const S = struct {
+ fn doTheTest() void {
+ var move = LevelUpMove{ .move_id = 1, .level = 2 };
+ var v = @bitCast(u16, move);
+ var back_to_a_move = @bitCast(LevelUpMove, v);
+ expect(back_to_a_move.move_id == 1);
+ expect(back_to_a_move.level == 2);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "implicit cast to error union by returning" {
+ const S = struct {
+ fn entry() void {
+ expect((func(-1) catch unreachable) == maxInt(u64));
+ }
+ pub fn func(sz: i64) anyerror!u64 {
+ return @bitCast(u64, sz);
+ }
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+// issue #3010: compiler segfault
+test "bitcast literal [4]u8 param to u32" {
+ const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 });
+ expect(ip == maxInt(u32));
+}
+
+test "bitcast packed struct literal to byte" {
+ const Foo = packed struct {
+ value: u8,
+ };
+ const casted = @bitCast(u8, Foo{ .value = 0xF });
+ expect(casted == 0xf);
+}
+
+test "comptime bitcast used in expression has the correct type" {
+ const Foo = packed struct {
+ value: u8,
+ };
+ expect(@bitCast(u8, Foo{ .value = 0xF }) == 0xf);
+}
+
+test "bitcast result to _" {
+ _ = @bitCast(u8, @as(i8, 1));
+}
+
+test "nested bitcast" {
+ const S = struct {
+ fn moo(x: isize) void {
+ @import("std").testing.expectEqual(@intCast(isize, 42), x);
+ }
+
+ fn foo(x: isize) void {
+ @This().moo(
+ @bitCast(isize, if (x != 0) @bitCast(usize, x) else @bitCast(usize, x)),
+ );
+ }
+ };
+
+ S.foo(42);
+ comptime S.foo(42);
+}
+
+test "bitcast passed as tuple element" {
+ const S = struct {
+ fn foo(args: anytype) void {
+ comptime expect(@TypeOf(args[0]) == f32);
+ expect(args[0] == 12.34);
+ }
+ };
+ S.foo(.{@bitCast(f32, @as(u32, 0x414570A4))});
+}
+
+test "triple level result location with bitcast sandwich passed as tuple element" {
+ const S = struct {
+ fn foo(args: anytype) void {
+ comptime expect(@TypeOf(args[0]) == f64);
+ expect(args[0] > 12.33 and args[0] < 12.35);
+ }
+ };
+ S.foo(.{@as(f64, @bitCast(f32, @as(u32, 0x414570A4)))});
+}
+
+test "bitcast generates a temporary value" {
+ var y = @as(u16, 0x55AA);
+ const x = @bitCast(u16, @bitCast([2]u8, y));
+ expectEqual(y, x);
+}
diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig
new file mode 100644
index 0000000000..8de2d5d2ca
--- /dev/null
+++ b/test/behavior/bitreverse.zig
@@ -0,0 +1,69 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const minInt = std.math.minInt;
+
+test "@bitReverse" {
+ comptime testBitReverse();
+ testBitReverse();
+}
+
+fn testBitReverse() void {
+ // using comptime_ints, unsigned
+ expect(@bitReverse(u0, 0) == 0);
+ expect(@bitReverse(u5, 0x12) == 0x9);
+ expect(@bitReverse(u8, 0x12) == 0x48);
+ expect(@bitReverse(u16, 0x1234) == 0x2c48);
+ expect(@bitReverse(u24, 0x123456) == 0x6a2c48);
+ expect(@bitReverse(u32, 0x12345678) == 0x1e6a2c48);
+ expect(@bitReverse(u40, 0x123456789a) == 0x591e6a2c48);
+ expect(@bitReverse(u48, 0x123456789abc) == 0x3d591e6a2c48);
+ expect(@bitReverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48);
+ expect(@bitReverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48);
+ expect(@bitReverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48);
+
+ // using runtime uints, unsigned
+ var num0: u0 = 0;
+ expect(@bitReverse(u0, num0) == 0);
+ var num5: u5 = 0x12;
+ expect(@bitReverse(u5, num5) == 0x9);
+ var num8: u8 = 0x12;
+ expect(@bitReverse(u8, num8) == 0x48);
+ var num16: u16 = 0x1234;
+ expect(@bitReverse(u16, num16) == 0x2c48);
+ var num24: u24 = 0x123456;
+ expect(@bitReverse(u24, num24) == 0x6a2c48);
+ var num32: u32 = 0x12345678;
+ expect(@bitReverse(u32, num32) == 0x1e6a2c48);
+ var num40: u40 = 0x123456789a;
+ expect(@bitReverse(u40, num40) == 0x591e6a2c48);
+ var num48: u48 = 0x123456789abc;
+ expect(@bitReverse(u48, num48) == 0x3d591e6a2c48);
+ var num56: u56 = 0x123456789abcde;
+ expect(@bitReverse(u56, num56) == 0x7b3d591e6a2c48);
+ var num64: u64 = 0x123456789abcdef1;
+ expect(@bitReverse(u64, num64) == 0x8f7b3d591e6a2c48);
+ var num128: u128 = 0x123456789abcdef11121314151617181;
+ expect(@bitReverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48);
+
+ // using comptime_ints, signed, positive
+ expect(@bitReverse(u8, @as(u8, 0)) == 0);
+ expect(@bitReverse(i8, @bitCast(i8, @as(u8, 0x92))) == @bitCast(i8, @as(u8, 0x49)));
+ expect(@bitReverse(i16, @bitCast(i16, @as(u16, 0x1234))) == @bitCast(i16, @as(u16, 0x2c48)));
+ expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0x123456))) == @bitCast(i24, @as(u24, 0x6a2c48)));
+ expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0x12345678))) == @bitCast(i32, @as(u32, 0x1e6a2c48)));
+ expect(@bitReverse(i40, @bitCast(i40, @as(u40, 0x123456789a))) == @bitCast(i40, @as(u40, 0x591e6a2c48)));
+ expect(@bitReverse(i48, @bitCast(i48, @as(u48, 0x123456789abc))) == @bitCast(i48, @as(u48, 0x3d591e6a2c48)));
+ expect(@bitReverse(i56, @bitCast(i56, @as(u56, 0x123456789abcde))) == @bitCast(i56, @as(u56, 0x7b3d591e6a2c48)));
+ expect(@bitReverse(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1))) == @bitCast(i64, @as(u64, 0x8f7b3d591e6a2c48)));
+ expect(@bitReverse(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181))) == @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48)));
+
+ // using signed, negative. Compare to runtime ints returned from llvm.
+ var neg8: i8 = -18;
+ expect(@bitReverse(i8, @as(i8, -18)) == @bitReverse(i8, neg8));
+ var neg16: i16 = -32694;
+ expect(@bitReverse(i16, @as(i16, -32694)) == @bitReverse(i16, neg16));
+ var neg24: i24 = -6773785;
+ expect(@bitReverse(i24, @as(i24, -6773785)) == @bitReverse(i24, neg24));
+ var neg32: i32 = -16773785;
+ expect(@bitReverse(i32, @as(i32, -16773785)) == @bitReverse(i32, neg32));
+}
diff --git a/test/behavior/bool.zig b/test/behavior/bool.zig
new file mode 100644
index 0000000000..ef9383244e
--- /dev/null
+++ b/test/behavior/bool.zig
@@ -0,0 +1,35 @@
+const expect = @import("std").testing.expect;
+
+test "bool literals" {
+ expect(true);
+ expect(!false);
+}
+
+test "cast bool to int" {
+ const t = true;
+ const f = false;
+ expect(@boolToInt(t) == @as(u32, 1));
+ expect(@boolToInt(f) == @as(u32, 0));
+ nonConstCastBoolToInt(t, f);
+}
+
+fn nonConstCastBoolToInt(t: bool, f: bool) void {
+ expect(@boolToInt(t) == @as(u32, 1));
+ expect(@boolToInt(f) == @as(u32, 0));
+}
+
+test "bool cmp" {
+ expect(testBoolCmp(true, false) == false);
+}
+fn testBoolCmp(a: bool, b: bool) bool {
+ return a == b;
+}
+
+const global_f = false;
+const global_t = true;
+const not_global_f = !global_f;
+const not_global_t = !global_t;
+test "compile time bool not" {
+ expect(not_global_f);
+ expect(!not_global_t);
+}
diff --git a/test/behavior/bugs/1025.zig b/test/behavior/bugs/1025.zig
new file mode 100644
index 0000000000..66e1a2be54
--- /dev/null
+++ b/test/behavior/bugs/1025.zig
@@ -0,0 +1,12 @@
+const A = struct {
+ B: type,
+};
+
+fn getA() A {
+ return A{ .B = u8 };
+}
+
+test "bug 1025" {
+ const a = getA();
+ @import("std").testing.expect(a.B == u8);
+}
diff --git a/test/behavior/bugs/1076.zig b/test/behavior/bugs/1076.zig
new file mode 100644
index 0000000000..fa3caf0df8
--- /dev/null
+++ b/test/behavior/bugs/1076.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+const mem = std.mem;
+const expect = std.testing.expect;
+
+test "comptime code should not modify constant data" {
+ testCastPtrOfArrayToSliceAndPtr();
+ comptime testCastPtrOfArrayToSliceAndPtr();
+}
+
+fn testCastPtrOfArrayToSliceAndPtr() void {
+ {
+ var array = "aoeu".*;
+ const x: [*]u8 = &array;
+ x[0] += 1;
+ expect(mem.eql(u8, array[0..], "boeu"));
+ }
+ {
+ var array: [4]u8 = "aoeu".*;
+ const x: [*]u8 = &array;
+ x[0] += 1;
+ expect(mem.eql(u8, array[0..], "boeu"));
+ }
+}
diff --git a/test/behavior/bugs/1111.zig b/test/behavior/bugs/1111.zig
new file mode 100644
index 0000000000..607bc33666
--- /dev/null
+++ b/test/behavior/bugs/1111.zig
@@ -0,0 +1,11 @@
+const Foo = extern enum {
+ Bar = -1,
+};
+
+test "issue 1111 fixed" {
+ const v = Foo.Bar;
+
+ switch (v) {
+ Foo.Bar => return,
+ }
+}
diff --git a/test/behavior/bugs/1120.zig b/test/behavior/bugs/1120.zig
new file mode 100644
index 0000000000..dda46e8e1c
--- /dev/null
+++ b/test/behavior/bugs/1120.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const A = packed struct {
+ a: u2,
+ b: u6,
+};
+const B = packed struct {
+ q: u8,
+ a: u2,
+ b: u6,
+};
+test "bug 1120" {
+ var a = A{ .a = 2, .b = 2 };
+ var b = B{ .q = 22, .a = 3, .b = 2 };
+ var t: usize = 0;
+ const ptr = switch (t) {
+ 0 => &a.a,
+ 1 => &b.a,
+ else => unreachable,
+ };
+ expect(ptr.* == 2);
+}
diff --git a/test/behavior/bugs/1277.zig b/test/behavior/bugs/1277.zig
new file mode 100644
index 0000000000..3aa1db2ea0
--- /dev/null
+++ b/test/behavior/bugs/1277.zig
@@ -0,0 +1,15 @@
+const std = @import("std");
+
+const S = struct {
+ f: ?fn () i32,
+};
+
+const s = S{ .f = f };
+
+fn f() i32 {
+ return 1234;
+}
+
+test "don't emit an LLVM global for a const function when it's in an optional in a struct" {
+ std.testing.expect(s.f.?() == 1234);
+}
diff --git a/test/behavior/bugs/1310.zig b/test/behavior/bugs/1310.zig
new file mode 100644
index 0000000000..788cba5756
--- /dev/null
+++ b/test/behavior/bugs/1310.zig
@@ -0,0 +1,24 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+pub const VM = ?[*]const struct_InvocationTable_;
+pub const struct_InvocationTable_ = extern struct {
+ GetVM: ?fn (?[*]VM) callconv(.C) c_int,
+};
+
+pub const struct_VM_ = extern struct {
+ functions: ?[*]const struct_InvocationTable_,
+};
+
+//excised output from stdlib.h etc
+
+pub const InvocationTable_ = struct_InvocationTable_;
+pub const VM_ = struct_VM_;
+
+fn agent_callback(_vm: [*]VM, options: [*]u8) callconv(.C) i32 {
+ return 11;
+}
+
+test "fixed" {
+ expect(agent_callback(undefined, undefined) == 11);
+}
diff --git a/test/behavior/bugs/1322.zig b/test/behavior/bugs/1322.zig
new file mode 100644
index 0000000000..02ead6afff
--- /dev/null
+++ b/test/behavior/bugs/1322.zig
@@ -0,0 +1,19 @@
+const std = @import("std");
+
+const B = union(enum) {
+ c: C,
+ None,
+};
+
+const A = struct {
+ b: B,
+};
+
+const C = struct {};
+
+test "tagged union with all void fields but a meaningful tag" {
+ var a: A = A{ .b = B{ .c = C{} } };
+ std.testing.expect(@as(std.meta.Tag(B), a.b) == std.meta.Tag(B).c);
+ a = A{ .b = B.None };
+ std.testing.expect(@as(std.meta.Tag(B), a.b) == std.meta.Tag(B).None);
+}
diff --git a/test/behavior/bugs/1381.zig b/test/behavior/bugs/1381.zig
new file mode 100644
index 0000000000..1d44593990
--- /dev/null
+++ b/test/behavior/bugs/1381.zig
@@ -0,0 +1,21 @@
+const std = @import("std");
+
+const B = union(enum) {
+ D: u8,
+ E: u16,
+};
+
+const A = union(enum) {
+ B: B,
+ C: u8,
+};
+
+test "union that needs padding bytes inside an array" {
+ var as = [_]A{
+ A{ .B = B{ .D = 1 } },
+ A{ .B = B{ .D = 1 } },
+ };
+
+ const a = as[0].B;
+ std.testing.expect(a.D == 1);
+}
diff --git a/test/behavior/bugs/1421.zig b/test/behavior/bugs/1421.zig
new file mode 100644
index 0000000000..4cc3a90b29
--- /dev/null
+++ b/test/behavior/bugs/1421.zig
@@ -0,0 +1,13 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const S = struct {
+ fn method() std.builtin.TypeInfo {
+ return @typeInfo(S);
+ }
+};
+
+test "functions with return type required to be comptime are generic" {
+ const ti = S.method();
+ expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct);
+}
diff --git a/test/behavior/bugs/1442.zig b/test/behavior/bugs/1442.zig
new file mode 100644
index 0000000000..d5ea3f66fe
--- /dev/null
+++ b/test/behavior/bugs/1442.zig
@@ -0,0 +1,11 @@
+const std = @import("std");
+
+const Union = union(enum) {
+ Text: []const u8,
+ Color: u32,
+};
+
+test "const error union field alignment" {
+ var union_or_err: anyerror!Union = Union{ .Color = 1234 };
+ std.testing.expect((union_or_err catch unreachable).Color == 1234);
+}
diff --git a/test/behavior/bugs/1467.zig b/test/behavior/bugs/1467.zig
new file mode 100644
index 0000000000..71c55dc59c
--- /dev/null
+++ b/test/behavior/bugs/1467.zig
@@ -0,0 +1,7 @@
+pub const E = enum(u32) { A, B, C };
+pub const S = extern struct {
+ e: E,
+};
+test "bug 1467" {
+ const s: S = undefined;
+}
diff --git a/test/behavior/bugs/1486.zig b/test/behavior/bugs/1486.zig
new file mode 100644
index 0000000000..1b8e5ca4a1
--- /dev/null
+++ b/test/behavior/bugs/1486.zig
@@ -0,0 +1,10 @@
+const expect = @import("std").testing.expect;
+
+const ptr = &global;
+var global: u64 = 123;
+
+test "constant pointer to global variable causes runtime load" {
+ global = 1234;
+ expect(&global == ptr);
+ expect(ptr.* == 1234);
+}
diff --git a/test/behavior/bugs/1500.zig b/test/behavior/bugs/1500.zig
new file mode 100644
index 0000000000..e24cfe5a9c
--- /dev/null
+++ b/test/behavior/bugs/1500.zig
@@ -0,0 +1,10 @@
+const A = struct {
+ b: B,
+};
+
+const B = fn (A) void;
+
+test "allow these dependencies" {
+ var a: A = undefined;
+ var b: B = undefined;
+}
diff --git a/test/behavior/bugs/1607.zig b/test/behavior/bugs/1607.zig
new file mode 100644
index 0000000000..ffc1aa85dc
--- /dev/null
+++ b/test/behavior/bugs/1607.zig
@@ -0,0 +1,15 @@
+const std = @import("std");
+const testing = std.testing;
+
+const a = [_]u8{ 1, 2, 3 };
+
+fn checkAddress(s: []const u8) void {
+ for (s) |*i, j| {
+ testing.expect(i == &a[j]);
+ }
+}
+
+test "slices pointing at the same address as global array." {
+ checkAddress(&a);
+ comptime checkAddress(&a);
+}
diff --git a/test/behavior/bugs/1735.zig b/test/behavior/bugs/1735.zig
new file mode 100644
index 0000000000..e757b5157c
--- /dev/null
+++ b/test/behavior/bugs/1735.zig
@@ -0,0 +1,46 @@
+const std = @import("std");
+
+const mystruct = struct {
+ pending: ?listofstructs,
+};
+pub fn TailQueue(comptime T: type) type {
+ return struct {
+ const Self = @This();
+
+ pub const Node = struct {
+ prev: ?*Node,
+ next: ?*Node,
+ data: T,
+ };
+
+ first: ?*Node,
+ last: ?*Node,
+ len: usize,
+
+ pub fn init() Self {
+ return Self{
+ .first = null,
+ .last = null,
+ .len = 0,
+ };
+ }
+ };
+}
+const listofstructs = TailQueue(mystruct);
+
+const a = struct {
+ const Self = @This();
+
+ foo: listofstructs,
+
+ pub fn init() Self {
+ return Self{
+ .foo = listofstructs.init(),
+ };
+ }
+};
+
+test "intialization" {
+ var t = a.init();
+ std.testing.expect(t.foo.len == 0);
+}
diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig
new file mode 100644
index 0000000000..9882536d38
--- /dev/null
+++ b/test/behavior/bugs/1741.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+
+test "fixed" {
+ const x: f32 align(128) = 12.34;
+ std.testing.expect(@ptrToInt(&x) % 128 == 0);
+}
diff --git a/test/behavior/bugs/1851.zig b/test/behavior/bugs/1851.zig
new file mode 100644
index 0000000000..d6cf17651d
--- /dev/null
+++ b/test/behavior/bugs/1851.zig
@@ -0,0 +1,26 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "allocation and looping over 3-byte integer" {
+ expect(@sizeOf(u24) == 4);
+ expect(@sizeOf([1]u24) == 4);
+ expect(@alignOf(u24) == 4);
+ expect(@alignOf([1]u24) == 4);
+
+ var x = try std.testing.allocator.alloc(u24, 2);
+ defer std.testing.allocator.free(x);
+ expect(x.len == 2);
+ x[0] = 0xFFFFFF;
+ x[1] = 0xFFFFFF;
+
+ const bytes = std.mem.sliceAsBytes(x);
+ expect(@TypeOf(bytes) == []align(4) u8);
+ expect(bytes.len == 8);
+
+ for (bytes) |*b| {
+ b.* = 0x00;
+ }
+
+ expect(x[0] == 0x00);
+ expect(x[1] == 0x00);
+}
diff --git a/test/behavior/bugs/1914.zig b/test/behavior/bugs/1914.zig
new file mode 100644
index 0000000000..2c9e836e6a
--- /dev/null
+++ b/test/behavior/bugs/1914.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+
+const A = struct {
+ b_list_pointer: *const []B,
+};
+const B = struct {
+ a_pointer: *const A,
+};
+
+const b_list: []B = &[_]B{};
+const a = A{ .b_list_pointer = &b_list };
+
+test "segfault bug" {
+ const assert = std.debug.assert;
+ const obj = B{ .a_pointer = &a };
+ assert(obj.a_pointer == &a); // this makes zig crash
+}
+
+const A2 = struct {
+ pointer: *B,
+};
+
+pub const B2 = struct {
+ pointer_array: []*A2,
+};
+
+var b_value = B2{ .pointer_array = &[_]*A2{} };
+
+test "basic stuff" {
+ std.debug.assert(&b_value == &b_value);
+}
diff --git a/test/behavior/bugs/2006.zig b/test/behavior/bugs/2006.zig
new file mode 100644
index 0000000000..dad78a9ba0
--- /dev/null
+++ b/test/behavior/bugs/2006.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const S = struct {
+ p: *S,
+};
+test "bug 2006" {
+ var a: S = undefined;
+ a = S{ .p = undefined };
+ expect(@sizeOf(S) != 0);
+ expect(@sizeOf(*void) == 0);
+}
diff --git a/test/behavior/bugs/2114.zig b/test/behavior/bugs/2114.zig
new file mode 100644
index 0000000000..1034a256d3
--- /dev/null
+++ b/test/behavior/bugs/2114.zig
@@ -0,0 +1,19 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const math = std.math;
+
+fn ctz(x: anytype) usize {
+ return @ctz(@TypeOf(x), x);
+}
+
+test "fixed" {
+ testClz();
+ comptime testClz();
+}
+
+fn testClz() void {
+ expect(ctz(@as(u128, 0x40000000000000000000000000000000)) == 126);
+ expect(math.rotl(u128, @as(u128, 0x40000000000000000000000000000000), @as(u8, 1)) == @as(u128, 0x80000000000000000000000000000000));
+ expect(ctz(@as(u128, 0x80000000000000000000000000000000)) == 127);
+ expect(ctz(math.rotl(u128, @as(u128, 0x40000000000000000000000000000000), @as(u8, 1))) == 127);
+}
diff --git a/test/behavior/bugs/2346.zig b/test/behavior/bugs/2346.zig
new file mode 100644
index 0000000000..c8cea34813
--- /dev/null
+++ b/test/behavior/bugs/2346.zig
@@ -0,0 +1,6 @@
+test "fixed" {
+ const a: *void = undefined;
+ const b: *[1]void = a;
+ const c: *[0]u8 = undefined;
+ const d: []u8 = c;
+}
diff --git a/test/behavior/bugs/2578.zig b/test/behavior/bugs/2578.zig
new file mode 100644
index 0000000000..cf7d941562
--- /dev/null
+++ b/test/behavior/bugs/2578.zig
@@ -0,0 +1,12 @@
+const Foo = struct {
+ y: u8,
+};
+
+var foo: Foo = undefined;
+const t = &foo;
+
+fn bar(pointer: ?*c_void) void {}
+
+test "fixed" {
+ bar(t);
+}
diff --git a/test/behavior/bugs/2692.zig b/test/behavior/bugs/2692.zig
new file mode 100644
index 0000000000..267c3a131a
--- /dev/null
+++ b/test/behavior/bugs/2692.zig
@@ -0,0 +1,6 @@
+fn foo(a: []u8) void {}
+
+test "address of 0 length array" {
+ var pt: [0]u8 = undefined;
+ foo(&pt);
+}
diff --git a/test/behavior/bugs/2889.zig b/test/behavior/bugs/2889.zig
new file mode 100644
index 0000000000..4991b56510
--- /dev/null
+++ b/test/behavior/bugs/2889.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+
+const source = "A-";
+
+fn parseNote() ?i32 {
+ const letter = source[0];
+ const modifier = source[1];
+
+ const semitone = blk: {
+ if (letter == 'C' and modifier == '-') break :blk @as(i32, 0);
+ if (letter == 'C' and modifier == '#') break :blk @as(i32, 1);
+ if (letter == 'D' and modifier == '-') break :blk @as(i32, 2);
+ if (letter == 'D' and modifier == '#') break :blk @as(i32, 3);
+ if (letter == 'E' and modifier == '-') break :blk @as(i32, 4);
+ if (letter == 'F' and modifier == '-') break :blk @as(i32, 5);
+ if (letter == 'F' and modifier == '#') break :blk @as(i32, 6);
+ if (letter == 'G' and modifier == '-') break :blk @as(i32, 7);
+ if (letter == 'G' and modifier == '#') break :blk @as(i32, 8);
+ if (letter == 'A' and modifier == '-') break :blk @as(i32, 9);
+ if (letter == 'A' and modifier == '#') break :blk @as(i32, 10);
+ if (letter == 'B' and modifier == '-') break :blk @as(i32, 11);
+ return null;
+ };
+
+ return semitone;
+}
+
+test "fixed" {
+ const result = parseNote();
+ std.testing.expect(result.? == 9);
+}
diff --git a/test/behavior/bugs/3007.zig b/test/behavior/bugs/3007.zig
new file mode 100644
index 0000000000..b723ebc097
--- /dev/null
+++ b/test/behavior/bugs/3007.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+
+const Foo = struct {
+ free: bool,
+
+ pub const FooError = error{NotFree};
+};
+
+var foo = Foo{ .free = true };
+var default_foo: ?*Foo = null;
+
+fn get_foo() Foo.FooError!*Foo {
+ if (foo.free) {
+ foo.free = false;
+ return &foo;
+ }
+ return error.NotFree;
+}
+
+test "fixed" {
+ default_foo = get_foo() catch null; // This Line
+ std.testing.expect(!default_foo.?.free);
+}
diff --git a/test/behavior/bugs/3046.zig b/test/behavior/bugs/3046.zig
new file mode 100644
index 0000000000..b62474f9ba
--- /dev/null
+++ b/test/behavior/bugs/3046.zig
@@ -0,0 +1,19 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const SomeStruct = struct {
+ field: i32,
+};
+
+fn couldFail() anyerror!i32 {
+ return 1;
+}
+
+var some_struct: SomeStruct = undefined;
+
+test "fixed" {
+ some_struct = SomeStruct{
+ .field = couldFail() catch |_| @as(i32, 0),
+ };
+ expect(some_struct.field == 1);
+}
diff --git a/test/behavior/bugs/3112.zig b/test/behavior/bugs/3112.zig
new file mode 100644
index 0000000000..1dfe359d4a
--- /dev/null
+++ b/test/behavior/bugs/3112.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const State = struct {
+ const Self = @This();
+ enter: fn (previous: ?Self) void,
+};
+
+fn prev(p: ?State) void {
+ expect(p == null);
+}
+
+test "zig test crash" {
+ var global: State = undefined;
+ global.enter = prev;
+ global.enter(null);
+}
diff --git a/test/behavior/bugs/3367.zig b/test/behavior/bugs/3367.zig
new file mode 100644
index 0000000000..3df3adbff7
--- /dev/null
+++ b/test/behavior/bugs/3367.zig
@@ -0,0 +1,12 @@
+const Foo = struct {
+ usingnamespace Mixin;
+};
+
+const Mixin = struct {
+ pub fn two(self: Foo) void {}
+};
+
+test "container member access usingnamespace decls" {
+ var foo = Foo{};
+ foo.two();
+}
diff --git a/test/behavior/bugs/3384.zig b/test/behavior/bugs/3384.zig
new file mode 100644
index 0000000000..789b0be516
--- /dev/null
+++ b/test/behavior/bugs/3384.zig
@@ -0,0 +1,11 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "resolve array slice using builtin" {
+ expect(@hasDecl(@This(), "std") == true);
+ expect(@hasDecl(@This(), "std"[0..0]) == false);
+ expect(@hasDecl(@This(), "std"[0..1]) == false);
+ expect(@hasDecl(@This(), "std"[0..2]) == false);
+ expect(@hasDecl(@This(), "std"[0..3]) == true);
+ expect(@hasDecl(@This(), "std"[0..]) == true);
+}
diff --git a/test/behavior/bugs/3468.zig b/test/behavior/bugs/3468.zig
new file mode 100644
index 0000000000..adf3db3306
--- /dev/null
+++ b/test/behavior/bugs/3468.zig
@@ -0,0 +1,6 @@
+// zig fmt: off
+test "pointer deref next to assignment" {
+ var a:i32=2;
+ var b=&a;
+ b.*=3;
+}
diff --git a/test/behavior/bugs/3586.zig b/test/behavior/bugs/3586.zig
new file mode 100644
index 0000000000..7bb1ba5d31
--- /dev/null
+++ b/test/behavior/bugs/3586.zig
@@ -0,0 +1,11 @@
+const NoteParams = struct {};
+
+const Container = struct {
+ params: ?NoteParams,
+};
+
+test "fixed" {
+ var ctr = Container{
+ .params = NoteParams{},
+ };
+}
diff --git a/test/behavior/bugs/3742.zig b/test/behavior/bugs/3742.zig
new file mode 100644
index 0000000000..bf6e1f5207
--- /dev/null
+++ b/test/behavior/bugs/3742.zig
@@ -0,0 +1,38 @@
+const std = @import("std");
+
+pub const GET = struct {
+ key: []const u8,
+
+ pub fn init(key: []const u8) GET {
+ return .{ .key = key };
+ }
+
+ pub const Redis = struct {
+ pub const Command = struct {
+ pub fn serialize(self: GET, comptime rootSerializer: type) void {
+ return rootSerializer.serializeCommand(.{ "GET", self.key });
+ }
+ };
+ };
+};
+
+pub fn isCommand(comptime T: type) bool {
+ const tid = @typeInfo(T);
+ return (tid == .Struct or tid == .Enum or tid == .Union) and
+ @hasDecl(T, "Redis") and @hasDecl(T.Redis, "Command");
+}
+
+pub const ArgSerializer = struct {
+ pub fn serializeCommand(command: anytype) void {
+ const CmdT = @TypeOf(command);
+
+ if (comptime isCommand(CmdT)) {
+ // COMMENTING THE NEXT LINE REMOVES THE ERROR
+ return CmdT.Redis.Command.serialize(command, ArgSerializer);
+ }
+ }
+};
+
+test "fixed" {
+ ArgSerializer.serializeCommand(GET.init("banana"));
+}
diff --git a/test/behavior/bugs/394.zig b/test/behavior/bugs/394.zig
new file mode 100644
index 0000000000..b1f0b6b605
--- /dev/null
+++ b/test/behavior/bugs/394.zig
@@ -0,0 +1,18 @@
+const E = union(enum) {
+ A: [9]u8,
+ B: u64,
+};
+const S = struct {
+ x: u8,
+ y: E,
+};
+
+const expect = @import("std").testing.expect;
+
+test "bug 394 fixed" {
+ const x = S{
+ .x = 3,
+ .y = E{ .B = 1 },
+ };
+ expect(x.x == 3);
+}
diff --git a/test/behavior/bugs/421.zig b/test/behavior/bugs/421.zig
new file mode 100644
index 0000000000..748cb3a62d
--- /dev/null
+++ b/test/behavior/bugs/421.zig
@@ -0,0 +1,15 @@
+const expect = @import("std").testing.expect;
+
+test "bitCast to array" {
+ comptime testBitCastArray();
+ testBitCastArray();
+}
+
+fn testBitCastArray() void {
+ expect(extractOne64(0x0123456789abcdef0123456789abcdef) == 0x0123456789abcdef);
+}
+
+fn extractOne64(a: u128) u64 {
+ const x = @bitCast([2]u64, a);
+ return x[1];
+}
diff --git a/test/behavior/bugs/4328.zig b/test/behavior/bugs/4328.zig
new file mode 100644
index 0000000000..98ab7bd155
--- /dev/null
+++ b/test/behavior/bugs/4328.zig
@@ -0,0 +1,71 @@
+const expectEqual = @import("std").testing.expectEqual;
+
+const FILE = extern struct {
+ dummy_field: u8,
+};
+
+extern fn printf([*c]const u8, ...) c_int;
+extern fn fputs([*c]const u8, noalias [*c]FILE) c_int;
+extern fn ftell([*c]FILE) c_long;
+extern fn fopen([*c]const u8, [*c]const u8) [*c]FILE;
+
+const S = extern struct {
+ state: c_short,
+
+ extern fn s_do_thing([*c]S, b: c_int) c_short;
+};
+
+test "Extern function calls in @TypeOf" {
+ const Test = struct {
+ fn test_fn_1(a: anytype, b: anytype) @TypeOf(printf("%d %s\n", a, b)) {
+ return 0;
+ }
+
+ fn test_fn_2(a: anytype) @TypeOf((S{ .state = 0 }).s_do_thing(a)) {
+ return 1;
+ }
+
+ fn doTheTest() void {
+ expectEqual(c_int, @TypeOf(test_fn_1(0, 42)));
+ expectEqual(c_short, @TypeOf(test_fn_2(0)));
+ }
+ };
+
+ Test.doTheTest();
+ comptime Test.doTheTest();
+}
+
+test "Peer resolution of extern function calls in @TypeOf" {
+ const Test = struct {
+ fn test_fn() @TypeOf(ftell(null), fputs(null, null)) {
+ return 0;
+ }
+
+ fn doTheTest() void {
+ expectEqual(c_long, @TypeOf(test_fn()));
+ }
+ };
+
+ Test.doTheTest();
+ comptime Test.doTheTest();
+}
+
+test "Extern function calls, dereferences and field access in @TypeOf" {
+ const Test = struct {
+ fn test_fn_1(a: c_long) @TypeOf(fopen("test", "r").*) {
+ return .{ .dummy_field = 0 };
+ }
+
+ fn test_fn_2(a: anytype) @TypeOf(fopen("test", "r").*.dummy_field) {
+ return 255;
+ }
+
+ fn doTheTest() void {
+ expectEqual(FILE, @TypeOf(test_fn_1(0)));
+ expectEqual(u8, @TypeOf(test_fn_2(0)));
+ }
+ };
+
+ Test.doTheTest();
+ comptime Test.doTheTest();
+}
diff --git a/test/behavior/bugs/4560.zig b/test/behavior/bugs/4560.zig
new file mode 100644
index 0000000000..6821527894
--- /dev/null
+++ b/test/behavior/bugs/4560.zig
@@ -0,0 +1,32 @@
+const std = @import("std");
+
+test "fixed" {
+ var s: S = .{
+ .a = 1,
+ .b = .{
+ .size = 123,
+ .max_distance_from_start_index = 456,
+ },
+ };
+ std.testing.expect(s.a == 1);
+ std.testing.expect(s.b.size == 123);
+ std.testing.expect(s.b.max_distance_from_start_index == 456);
+}
+
+const S = struct {
+ a: u32,
+ b: Map,
+
+ const Map = StringHashMap(*S);
+};
+
+pub fn StringHashMap(comptime V: type) type {
+ return HashMap([]const u8, V);
+}
+
+pub fn HashMap(comptime K: type, comptime V: type) type {
+ return struct {
+ size: usize,
+ max_distance_from_start_index: usize,
+ };
+}
diff --git a/test/behavior/bugs/4769_a.zig b/test/behavior/bugs/4769_a.zig
new file mode 100644
index 0000000000..ab0c01417a
--- /dev/null
+++ b/test/behavior/bugs/4769_a.zig
@@ -0,0 +1 @@
+// \ No newline at end of file
diff --git a/test/behavior/bugs/4769_b.zig b/test/behavior/bugs/4769_b.zig
new file mode 100644
index 0000000000..23b2513f17
--- /dev/null
+++ b/test/behavior/bugs/4769_b.zig
@@ -0,0 +1 @@
+//! \ No newline at end of file
diff --git a/test/behavior/bugs/4769_c.zig b/test/behavior/bugs/4769_c.zig
new file mode 100644
index 0000000000..4894ddf7e8
--- /dev/null
+++ b/test/behavior/bugs/4769_c.zig
@@ -0,0 +1 @@
+/// \ No newline at end of file
diff --git a/test/behavior/bugs/4954.zig b/test/behavior/bugs/4954.zig
new file mode 100644
index 0000000000..b5a9bdf851
--- /dev/null
+++ b/test/behavior/bugs/4954.zig
@@ -0,0 +1,8 @@
+fn f(buf: []u8) void {
+ var ptr = &buf[@sizeOf(u32)];
+}
+
+test "crash" {
+ var buf: [4096]u8 = undefined;
+ f(&buf);
+}
diff --git a/test/behavior/bugs/529.zig b/test/behavior/bugs/529.zig
new file mode 100644
index 0000000000..6aeb73c331
--- /dev/null
+++ b/test/behavior/bugs/529.zig
@@ -0,0 +1,14 @@
+const A = extern struct {
+ field: c_int,
+};
+
+extern fn issue529(?*A) void;
+
+comptime {
+ _ = @import("529_other_file_2.zig");
+}
+
+test "issue 529 fixed" {
+ @import("529_other_file.zig").issue529(null);
+ issue529(null);
+}
diff --git a/test/behavior/bugs/529_other_file.zig b/test/behavior/bugs/529_other_file.zig
new file mode 100644
index 0000000000..4ee4e31a10
--- /dev/null
+++ b/test/behavior/bugs/529_other_file.zig
@@ -0,0 +1,5 @@
+pub const A = extern struct {
+ field: c_int,
+};
+
+pub extern fn issue529(?*A) void;
diff --git a/test/behavior/bugs/529_other_file_2.zig b/test/behavior/bugs/529_other_file_2.zig
new file mode 100644
index 0000000000..b493959e50
--- /dev/null
+++ b/test/behavior/bugs/529_other_file_2.zig
@@ -0,0 +1,4 @@
+pub const A = extern struct {
+ field: c_int,
+};
+export fn issue529(a: ?*A) void {}
diff --git a/test/behavior/bugs/5398.zig b/test/behavior/bugs/5398.zig
new file mode 100644
index 0000000000..fdfd0b3698
--- /dev/null
+++ b/test/behavior/bugs/5398.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const testing = std.testing;
+
+pub const Mesh = struct {
+ id: u32,
+};
+pub const Material = struct {
+ transparent: bool = true,
+ emits_shadows: bool = true,
+ render_color: bool = true,
+};
+pub const Renderable = struct {
+ material: Material,
+ // The compiler inserts some padding here to ensure Mesh is correctly aligned.
+ mesh: Mesh,
+};
+
+var renderable: Renderable = undefined;
+
+test "assignment of field with padding" {
+ renderable = Renderable{
+ .mesh = Mesh{ .id = 0 },
+ .material = Material{
+ .transparent = false,
+ .emits_shadows = false,
+ },
+ };
+ testing.expectEqual(false, renderable.material.transparent);
+ testing.expectEqual(false, renderable.material.emits_shadows);
+ testing.expectEqual(true, renderable.material.render_color);
+}
diff --git a/test/behavior/bugs/5413.zig b/test/behavior/bugs/5413.zig
new file mode 100644
index 0000000000..5ef533a761
--- /dev/null
+++ b/test/behavior/bugs/5413.zig
@@ -0,0 +1,6 @@
+const expect = @import("std").testing.expect;
+
+test "Peer type resolution with string literals and unknown length u8 pointers" {
+ expect(@TypeOf("", "a", @as([*:0]const u8, "")) == [*:0]const u8);
+ expect(@TypeOf(@as([*:0]const u8, "baz"), "foo", "bar") == [*:0]const u8);
+}
diff --git a/test/behavior/bugs/5474.zig b/test/behavior/bugs/5474.zig
new file mode 100644
index 0000000000..88ce96095e
--- /dev/null
+++ b/test/behavior/bugs/5474.zig
@@ -0,0 +1,57 @@
+const std = @import("std");
+
+// baseline (control) struct with array of scalar
+const Box0 = struct {
+ items: [4]Item,
+
+ const Item = struct {
+ num: u32,
+ };
+};
+
+// struct with array of empty struct
+const Box1 = struct {
+ items: [4]Item,
+
+ const Item = struct {};
+};
+
+// struct with array of zero-size struct
+const Box2 = struct {
+ items: [4]Item,
+
+ const Item = struct {
+ nothing: void,
+ };
+};
+
+fn doTest() void {
+ // var
+ {
+ var box0: Box0 = .{ .items = undefined };
+ std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == false);
+
+ var box1: Box1 = .{ .items = undefined };
+ std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == false);
+
+ var box2: Box2 = .{ .items = undefined };
+ std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == false);
+ }
+
+ // const
+ {
+ const box0: Box0 = .{ .items = undefined };
+ std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == true);
+
+ const box1: Box1 = .{ .items = undefined };
+ std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == true);
+
+ const box2: Box2 = .{ .items = undefined };
+ std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == true);
+ }
+}
+
+test "pointer-to-array constness for zero-size elements" {
+ doTest();
+ comptime doTest();
+}
diff --git a/test/behavior/bugs/5487.zig b/test/behavior/bugs/5487.zig
new file mode 100644
index 0000000000..bf530a8a02
--- /dev/null
+++ b/test/behavior/bugs/5487.zig
@@ -0,0 +1,12 @@
+const io = @import("std").io;
+
+pub fn write(_: void, bytes: []const u8) !usize {
+ return 0;
+}
+pub fn writer() io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write) {
+ return io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write){ .context = {} };
+}
+
+test "crash" {
+ _ = io.multiWriter(.{writer()});
+}
diff --git a/test/behavior/bugs/624.zig b/test/behavior/bugs/624.zig
new file mode 100644
index 0000000000..1eee889a0d
--- /dev/null
+++ b/test/behavior/bugs/624.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const TestContext = struct {
+ server_context: *ListenerContext,
+};
+
+const ListenerContext = struct {
+ context_alloc: *ContextAllocator,
+};
+
+const ContextAllocator = MemoryPool(TestContext);
+
+fn MemoryPool(comptime T: type) type {
+ return struct {
+ n: usize,
+ };
+}
+
+test "foo" {
+ var allocator = ContextAllocator{ .n = 10 };
+ expect(allocator.n == 10);
+}
diff --git a/test/behavior/bugs/6456.zig b/test/behavior/bugs/6456.zig
new file mode 100644
index 0000000000..4b59792174
--- /dev/null
+++ b/test/behavior/bugs/6456.zig
@@ -0,0 +1,42 @@
+const std = @import("std");
+const testing = std.testing;
+const StructField = std.builtin.TypeInfo.StructField;
+const Declaration = std.builtin.TypeInfo.Declaration;
+
+const text =
+ \\f1
+ \\f2
+ \\f3
+;
+
+test "issue 6456" {
+ comptime {
+ var fields: []const StructField = &[0]StructField{};
+
+ var it = std.mem.tokenize(text, "\n");
+ while (it.next()) |name| {
+ fields = fields ++ &[_]StructField{StructField{
+ .alignment = 0,
+ .name = name,
+ .field_type = usize,
+ .default_value = @as(?usize, null),
+ .is_comptime = false,
+ }};
+ }
+
+ const T = @Type(.{
+ .Struct = .{
+ .layout = .Auto,
+ .is_tuple = false,
+ .fields = fields,
+ .decls = &[_]Declaration{},
+ },
+ });
+
+ const gen_fields = @typeInfo(T).Struct.fields;
+ testing.expectEqual(3, gen_fields.len);
+ testing.expectEqualStrings("f1", gen_fields[0].name);
+ testing.expectEqualStrings("f2", gen_fields[1].name);
+ testing.expectEqualStrings("f3", gen_fields[2].name);
+ }
+}
diff --git a/test/behavior/bugs/655.zig b/test/behavior/bugs/655.zig
new file mode 100644
index 0000000000..3d1bccb183
--- /dev/null
+++ b/test/behavior/bugs/655.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+const other_file = @import("655_other_file.zig");
+
+test "function with *const parameter with type dereferenced by namespace" {
+ const x: other_file.Integer = 1234;
+ comptime std.testing.expect(@TypeOf(&x) == *const other_file.Integer);
+ foo(&x);
+}
+
+fn foo(x: *const other_file.Integer) void {
+ std.testing.expect(x.* == 1234);
+}
diff --git a/test/behavior/bugs/655_other_file.zig b/test/behavior/bugs/655_other_file.zig
new file mode 100644
index 0000000000..df1df44955
--- /dev/null
+++ b/test/behavior/bugs/655_other_file.zig
@@ -0,0 +1 @@
+pub const Integer = u32;
diff --git a/test/behavior/bugs/656.zig b/test/behavior/bugs/656.zig
new file mode 100644
index 0000000000..159ec52d43
--- /dev/null
+++ b/test/behavior/bugs/656.zig
@@ -0,0 +1,31 @@
+const expect = @import("std").testing.expect;
+
+const PrefixOp = union(enum) {
+ Return,
+ AddrOf: Value,
+};
+
+const Value = struct {
+ align_expr: ?u32,
+};
+
+test "optional if after an if in a switch prong of a switch with 2 prongs in an else" {
+ foo(false, true);
+}
+
+fn foo(a: bool, b: bool) void {
+ var prefix_op = PrefixOp{
+ .AddrOf = Value{ .align_expr = 1234 },
+ };
+ if (a) {} else {
+ switch (prefix_op) {
+ PrefixOp.AddrOf => |addr_of_info| {
+ if (b) {}
+ if (addr_of_info.align_expr) |align_expr| {
+ expect(align_expr == 1234);
+ }
+ },
+ PrefixOp.Return => {},
+ }
+ }
+}
diff --git a/test/behavior/bugs/6781.zig b/test/behavior/bugs/6781.zig
new file mode 100644
index 0000000000..beadd97f1d
--- /dev/null
+++ b/test/behavior/bugs/6781.zig
@@ -0,0 +1,74 @@
+const std = @import("std");
+const assert = std.debug.assert;
+
+const segfault = true;
+
+pub const JournalHeader = packed struct {
+ hash_chain_root: u128 = undefined,
+ prev_hash_chain_root: u128,
+ checksum: u128 = undefined,
+ magic: u64,
+ command: u32,
+ size: u32,
+
+ pub fn calculate_checksum(self: *const JournalHeader, entry: []const u8) u128 {
+ assert(entry.len >= @sizeOf(JournalHeader));
+ assert(entry.len == self.size);
+
+ const checksum_offset = @byteOffsetOf(JournalHeader, "checksum");
+ const checksum_size = @sizeOf(@TypeOf(self.checksum));
+ assert(checksum_offset == 0 + 16 + 16);
+ assert(checksum_size == 16);
+
+ var target: [32]u8 = undefined;
+ std.crypto.hash.Blake3.hash(entry[checksum_offset + checksum_size ..], target[0..], .{});
+ return @bitCast(u128, target[0..checksum_size].*);
+ }
+
+ pub fn calculate_hash_chain_root(self: *const JournalHeader) u128 {
+ const hash_chain_root_size = @sizeOf(@TypeOf(self.hash_chain_root));
+ assert(hash_chain_root_size == 16);
+
+ const prev_hash_chain_root_offset = @byteOffsetOf(JournalHeader, "prev_hash_chain_root");
+ const prev_hash_chain_root_size = @sizeOf(@TypeOf(self.prev_hash_chain_root));
+ assert(prev_hash_chain_root_offset == 0 + 16);
+ assert(prev_hash_chain_root_size == 16);
+
+ const checksum_offset = @byteOffsetOf(JournalHeader, "checksum");
+ const checksum_size = @sizeOf(@TypeOf(self.checksum));
+ assert(checksum_offset == 0 + 16 + 16);
+ assert(checksum_size == 16);
+
+ assert(prev_hash_chain_root_offset + prev_hash_chain_root_size == checksum_offset);
+
+ const header = @bitCast([@sizeOf(JournalHeader)]u8, self.*);
+ const source = header[prev_hash_chain_root_offset .. checksum_offset + checksum_size];
+ assert(source.len == prev_hash_chain_root_size + checksum_size);
+ var target: [32]u8 = undefined;
+ std.crypto.hash.Blake3.hash(source, target[0..], .{});
+ if (segfault) {
+ return @bitCast(u128, target[0..hash_chain_root_size].*);
+ } else {
+ var array = target[0..hash_chain_root_size].*;
+ return @bitCast(u128, array);
+ }
+ }
+
+ pub fn set_checksum_and_hash_chain_root(self: *JournalHeader, entry: []const u8) void {
+ self.checksum = self.calculate_checksum(entry);
+ self.hash_chain_root = self.calculate_hash_chain_root();
+ }
+};
+
+test "fixed" {
+ var buffer = [_]u8{0} ** 65536;
+ var entry = std.mem.bytesAsValue(JournalHeader, buffer[0..@sizeOf(JournalHeader)]);
+ entry.* = .{
+ .prev_hash_chain_root = 0,
+ .magic = 0,
+ .command = 0,
+ .size = 64 + 128,
+ };
+ entry.set_checksum_and_hash_chain_root(buffer[0..entry.size]);
+ try std.io.null_writer.print("{}\n", .{entry});
+}
diff --git a/test/behavior/bugs/679.zig b/test/behavior/bugs/679.zig
new file mode 100644
index 0000000000..20b2b7b02a
--- /dev/null
+++ b/test/behavior/bugs/679.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+pub fn List(comptime T: type) type {
+ return u32;
+}
+
+const ElementList = List(Element);
+const Element = struct {
+ link: ElementList,
+};
+
+test "false dependency loop in struct definition" {
+ const listType = ElementList;
+ var x: listType = 42;
+ expect(x == 42);
+}
diff --git a/test/behavior/bugs/6850.zig b/test/behavior/bugs/6850.zig
new file mode 100644
index 0000000000..6759ba79ca
--- /dev/null
+++ b/test/behavior/bugs/6850.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+
+test "lazy sizeof comparison with zero" {
+ const Empty = struct {};
+ const T = *Empty;
+
+ std.testing.expect(hasNoBits(T));
+}
+
+fn hasNoBits(comptime T: type) bool {
+ return @sizeOf(T) == 0;
+}
diff --git a/test/behavior/bugs/7003.zig b/test/behavior/bugs/7003.zig
new file mode 100644
index 0000000000..75fc9068b1
--- /dev/null
+++ b/test/behavior/bugs/7003.zig
@@ -0,0 +1,8 @@
+test "@Type should resolve its children types" {
+ const sparse = enum(u2) { a, b, c };
+ const dense = enum(u2) { a, b, c, d };
+
+ comptime var sparse_info = @typeInfo(anyerror!sparse);
+ sparse_info.ErrorUnion.payload = dense;
+ const B = @Type(sparse_info);
+}
diff --git a/test/behavior/bugs/7027.zig b/test/behavior/bugs/7027.zig
new file mode 100644
index 0000000000..782eebbfc9
--- /dev/null
+++ b/test/behavior/bugs/7027.zig
@@ -0,0 +1,17 @@
+const Foobar = struct {
+ myTypes: [128]type,
+ str: [1024]u8,
+
+ fn foo() @This() {
+ comptime var foobar: Foobar = undefined;
+ foobar.str = [_]u8{'a'} ** 1024;
+ return foobar;
+ }
+};
+
+fn foo(arg: anytype) void {}
+
+test "" {
+ comptime var foobar = Foobar.foo();
+ foo(foobar.str[0..10]);
+}
diff --git a/test/behavior/bugs/704.zig b/test/behavior/bugs/704.zig
new file mode 100644
index 0000000000..765336c95f
--- /dev/null
+++ b/test/behavior/bugs/704.zig
@@ -0,0 +1,7 @@
+const xxx = struct {
+ pub fn bar(self: *xxx) void {}
+};
+test "bug 704" {
+ var x: xxx = undefined;
+ x.bar();
+}
diff --git a/test/behavior/bugs/7047.zig b/test/behavior/bugs/7047.zig
new file mode 100644
index 0000000000..0704e97b48
--- /dev/null
+++ b/test/behavior/bugs/7047.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+
+const U = union(enum) {
+ T: type,
+ N: void,
+};
+
+fn S(comptime query: U) type {
+ return struct {
+ fn tag() type {
+ return query.T;
+ }
+ };
+}
+
+test "compiler doesn't consider equal unions with different 'type' payload" {
+ const s1 = S(U{ .T = u32 }).tag();
+ std.testing.expectEqual(u32, s1);
+
+ const s2 = S(U{ .T = u64 }).tag();
+ std.testing.expectEqual(u64, s2);
+}
diff --git a/test/behavior/bugs/718.zig b/test/behavior/bugs/718.zig
new file mode 100644
index 0000000000..b5a57b8944
--- /dev/null
+++ b/test/behavior/bugs/718.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+const mem = std.mem;
+const expect = std.testing.expect;
+const Keys = struct {
+ up: bool,
+ down: bool,
+ left: bool,
+ right: bool,
+};
+var keys: Keys = undefined;
+test "zero keys with @memset" {
+ @memset(@ptrCast([*]u8, &keys), 0, @sizeOf(@TypeOf(keys)));
+ expect(!keys.up);
+ expect(!keys.down);
+ expect(!keys.left);
+ expect(!keys.right);
+}
diff --git a/test/behavior/bugs/7250.zig b/test/behavior/bugs/7250.zig
new file mode 100644
index 0000000000..b0f1aa15df
--- /dev/null
+++ b/test/behavior/bugs/7250.zig
@@ -0,0 +1,15 @@
+const nrfx_uart_t = extern struct {
+ p_reg: [*c]u32,
+ drv_inst_idx: u8,
+};
+
+pub fn nrfx_uart_rx(p_instance: [*c]const nrfx_uart_t) void {}
+
+threadlocal var g_uart0 = nrfx_uart_t{
+ .p_reg = 0,
+ .drv_inst_idx = 0,
+};
+
+test "reference a global threadlocal variable" {
+ _ = nrfx_uart_rx(&g_uart0);
+}
diff --git a/test/behavior/bugs/726.zig b/test/behavior/bugs/726.zig
new file mode 100644
index 0000000000..632d3b1511
--- /dev/null
+++ b/test/behavior/bugs/726.zig
@@ -0,0 +1,15 @@
+const expect = @import("std").testing.expect;
+
+test "@ptrCast from const to nullable" {
+ const c: u8 = 4;
+ var x: ?*const u8 = @ptrCast(?*const u8, &c);
+ expect(x.?.* == 4);
+}
+
+test "@ptrCast from var in empty struct to nullable" {
+ const container = struct {
+ var c: u8 = 4;
+ };
+ var x: ?*const u8 = @ptrCast(?*const u8, &container.c);
+ expect(x.?.* == 4);
+}
diff --git a/test/behavior/bugs/828.zig b/test/behavior/bugs/828.zig
new file mode 100644
index 0000000000..50ae0fd279
--- /dev/null
+++ b/test/behavior/bugs/828.zig
@@ -0,0 +1,33 @@
+const CountBy = struct {
+ a: usize,
+
+ const One = CountBy{ .a = 1 };
+
+ pub fn counter(self: *const CountBy) Counter {
+ return Counter{ .i = 0 };
+ }
+};
+
+const Counter = struct {
+ i: usize,
+
+ pub fn count(self: *Counter) bool {
+ self.i += 1;
+ return self.i <= 10;
+ }
+};
+
+fn constCount(comptime cb: *const CountBy, comptime unused: u32) void {
+ comptime {
+ var cnt = cb.counter();
+ if (cnt.i != 0) @compileError("Counter instance reused!");
+ while (cnt.count()) {}
+ }
+}
+
+test "comptime struct return should not return the same instance" {
+ //the first parameter must be passed by reference to trigger the bug
+ //a second parameter is required to trigger the bug
+ const ValA = constCount(&CountBy.One, 12);
+ const ValB = constCount(&CountBy.One, 15);
+}
diff --git a/test/behavior/bugs/920.zig b/test/behavior/bugs/920.zig
new file mode 100644
index 0000000000..72854956a1
--- /dev/null
+++ b/test/behavior/bugs/920.zig
@@ -0,0 +1,65 @@
+const std = @import("std");
+const math = std.math;
+const Random = std.rand.Random;
+
+const ZigTable = struct {
+ r: f64,
+ x: [257]f64,
+ f: [257]f64,
+
+ pdf: fn (f64) f64,
+ is_symmetric: bool,
+ zero_case: fn (*Random, f64) f64,
+};
+
+fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable {
+ var tables: ZigTable = undefined;
+
+ tables.is_symmetric = is_symmetric;
+ tables.r = r;
+ tables.pdf = f;
+ tables.zero_case = zero_case;
+
+ tables.x[0] = v / f(r);
+ tables.x[1] = r;
+
+ for (tables.x[2..256]) |*entry, i| {
+ const last = tables.x[2 + i - 1];
+ entry.* = f_inv(v / last + f(last));
+ }
+ tables.x[256] = 0;
+
+ for (tables.f[0..]) |*entry, i| {
+ entry.* = f(tables.x[i]);
+ }
+
+ return tables;
+}
+
+const norm_r = 3.6541528853610088;
+const norm_v = 0.00492867323399;
+
+fn norm_f(x: f64) f64 {
+ return math.exp(-x * x / 2.0);
+}
+fn norm_f_inv(y: f64) f64 {
+ return math.sqrt(-2.0 * math.ln(y));
+}
+fn norm_zero_case(random: *Random, u: f64) f64 {
+ return 0.0;
+}
+
+const NormalDist = blk: {
+ @setEvalBranchQuota(30000);
+ break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
+};
+
+test "bug 920 fixed" {
+ const NormalDist1 = blk: {
+ break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
+ };
+
+ for (NormalDist1.f) |_, i| {
+ std.testing.expectEqual(NormalDist1.f[i], NormalDist.f[i]);
+ }
+}
diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig
new file mode 100644
index 0000000000..c6d658a8a2
--- /dev/null
+++ b/test/behavior/byteswap.zig
@@ -0,0 +1,68 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "@byteSwap integers" {
+ const ByteSwapIntTest = struct {
+ fn run() void {
+ t(u0, 0, 0);
+ t(u8, 0x12, 0x12);
+ t(u16, 0x1234, 0x3412);
+ t(u24, 0x123456, 0x563412);
+ t(u32, 0x12345678, 0x78563412);
+ t(u40, 0x123456789a, 0x9a78563412);
+ t(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412)));
+ t(u56, 0x123456789abcde, 0xdebc9a78563412);
+ t(u64, 0x123456789abcdef1, 0xf1debc9a78563412);
+ t(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412);
+
+ t(u0, @as(u0, 0), 0);
+ t(i8, @as(i8, -50), -50);
+ t(i16, @bitCast(i16, @as(u16, 0x1234)), @bitCast(i16, @as(u16, 0x3412)));
+ t(i24, @bitCast(i24, @as(u24, 0x123456)), @bitCast(i24, @as(u24, 0x563412)));
+ t(i32, @bitCast(i32, @as(u32, 0x12345678)), @bitCast(i32, @as(u32, 0x78563412)));
+ t(u40, @bitCast(i40, @as(u40, 0x123456789a)), @as(u40, 0x9a78563412));
+ t(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412)));
+ t(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412)));
+ t(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0xf1debc9a78563412)));
+ t(
+ i128,
+ @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)),
+ @bitCast(i128, @as(u128, 0x8171615141312111f1debc9a78563412)),
+ );
+ }
+ fn t(comptime I: type, input: I, expected_output: I) void {
+ std.testing.expectEqual(expected_output, @byteSwap(I, input));
+ }
+ };
+ comptime ByteSwapIntTest.run();
+ ByteSwapIntTest.run();
+}
+
+test "@byteSwap vectors" {
+ // https://github.com/ziglang/zig/issues/3563
+ if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest;
+
+ // https://github.com/ziglang/zig/issues/3317
+ if (std.Target.current.cpu.arch == .mipsel or std.Target.current.cpu.arch == .mips) return error.SkipZigTest;
+
+ const ByteSwapVectorTest = struct {
+ fn run() void {
+ t(u8, 2, [_]u8{ 0x12, 0x13 }, [_]u8{ 0x12, 0x13 });
+ t(u16, 2, [_]u16{ 0x1234, 0x2345 }, [_]u16{ 0x3412, 0x4523 });
+ t(u24, 2, [_]u24{ 0x123456, 0x234567 }, [_]u24{ 0x563412, 0x674523 });
+ }
+
+ fn t(
+ comptime I: type,
+ comptime n: comptime_int,
+ input: std.meta.Vector(n, I),
+ expected_vector: std.meta.Vector(n, I),
+ ) void {
+ const actual_output: [n]I = @byteSwap(I, input);
+ const expected_output: [n]I = expected_vector;
+ std.testing.expectEqual(expected_output, actual_output);
+ }
+ };
+ comptime ByteSwapVectorTest.run();
+ ByteSwapVectorTest.run();
+}
diff --git a/test/behavior/byval_arg_var.zig b/test/behavior/byval_arg_var.zig
new file mode 100644
index 0000000000..ec3d18a532
--- /dev/null
+++ b/test/behavior/byval_arg_var.zig
@@ -0,0 +1,27 @@
+const std = @import("std");
+
+var result: []const u8 = "wrong";
+
+test "pass string literal byvalue to a generic var param" {
+ start();
+ blowUpStack(10);
+
+ std.testing.expect(std.mem.eql(u8, result, "string literal"));
+}
+
+fn start() void {
+ foo("string literal");
+}
+
+fn foo(x: anytype) void {
+ bar(x);
+}
+
+fn bar(x: anytype) void {
+ result = x;
+}
+
+fn blowUpStack(x: u32) void {
+ if (x == 0) return;
+ blowUpStack(x - 1);
+}
diff --git a/test/behavior/call.zig b/test/behavior/call.zig
new file mode 100644
index 0000000000..4d05a83a39
--- /dev/null
+++ b/test/behavior/call.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;
+ expect(@call(.{}, foo, .{}) == 1234);
+ comptime {
+ // modifiers that allow comptime calls
+ expect(@call(.{}, foo, .{}) == 1234);
+ expect(@call(.{ .modifier = .no_async }, foo, .{}) == 1234);
+ expect(@call(.{ .modifier = .always_tail }, foo, .{}) == 1234);
+ expect(@call(.{ .modifier = .always_inline }, foo, .{}) == 1234);
+ }
+ {
+ // comptime call without comptime keyword
+ const result = @call(.{ .modifier = .compile_time }, foo, .{}) == 1234;
+ comptime expect(result);
+ }
+ {
+ // call of non comptime-known function
+ var alias_foo = foo;
+ expect(@call(.{ .modifier = .no_async }, alias_foo, .{}) == 1234);
+ expect(@call(.{ .modifier = .never_tail }, alias_foo, .{}) == 1234);
+ 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;
+ expect(@call(.{}, add, .{ a, 34 }) == 46);
+ expect(@call(.{}, add, .{ 12, b }) == 46);
+ expect(@call(.{}, add, .{ a, b }) == 46);
+ expect(@call(.{}, add, .{ 12, 34 }) == 46);
+ comptime 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 };
+ expect(@call(.{ .modifier = .always_inline }, add, separate_args0) == 46);
+ expect(@call(.{ .modifier = .always_inline }, add, separate_args1) == 46);
+ expect(@call(.{ .modifier = .always_inline }, add, separate_args2) == 46);
+ 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;
+ expectEqual(?i32, S.ReturnType(inst.call_me_maybe));
+}
diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig
new file mode 100644
index 0000000000..0852a151a9
--- /dev/null
+++ b/test/behavior/cast.zig
@@ -0,0 +1,927 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const mem = std.mem;
+const maxInt = std.math.maxInt;
+const Vector = std.meta.Vector;
+const native_endian = @import("builtin").target.cpu.arch.endian();
+
+test "int to ptr cast" {
+ const x = @as(usize, 13);
+ const y = @intToPtr(*u8, x);
+ const z = @ptrToInt(y);
+ expect(z == 13);
+}
+
+test "integer literal to pointer cast" {
+ const vga_mem = @intToPtr(*u16, 0xB8000);
+ expect(@ptrToInt(vga_mem) == 0xB8000);
+}
+
+test "pointer reinterpret const float to int" {
+ // The hex representation is 0x3fe3333333333303.
+ const float: f64 = 5.99999999999994648725e-01;
+ const float_ptr = &float;
+ const int_ptr = @ptrCast(*const i32, float_ptr);
+ const int_val = int_ptr.*;
+ if (native_endian == .Little)
+ expect(int_val == 0x33333303)
+ else
+ expect(int_val == 0x3fe33333);
+}
+
+test "implicitly cast indirect pointer to maybe-indirect pointer" {
+ const S = struct {
+ const Self = @This();
+ x: u8,
+ fn constConst(p: *const *const Self) u8 {
+ return p.*.x;
+ }
+ fn maybeConstConst(p: ?*const *const Self) u8 {
+ return p.?.*.x;
+ }
+ fn constConstConst(p: *const *const *const Self) u8 {
+ return p.*.*.x;
+ }
+ fn maybeConstConstConst(p: ?*const *const *const Self) u8 {
+ return p.?.*.*.x;
+ }
+ };
+ const s = S{ .x = 42 };
+ const p = &s;
+ const q = &p;
+ const r = &q;
+ expect(42 == S.constConst(q));
+ expect(42 == S.maybeConstConst(q));
+ expect(42 == S.constConstConst(r));
+ expect(42 == S.maybeConstConstConst(r));
+}
+
+test "explicit cast from integer to error type" {
+ testCastIntToErr(error.ItBroke);
+ comptime testCastIntToErr(error.ItBroke);
+}
+fn testCastIntToErr(err: anyerror) void {
+ const x = @errorToInt(err);
+ const y = @intToError(x);
+ expect(error.ItBroke == y);
+}
+
+test "peer resolve arrays of different size to const slice" {
+ expect(mem.eql(u8, boolToStr(true), "true"));
+ expect(mem.eql(u8, boolToStr(false), "false"));
+ comptime expect(mem.eql(u8, boolToStr(true), "true"));
+ comptime expect(mem.eql(u8, boolToStr(false), "false"));
+}
+fn boolToStr(b: bool) []const u8 {
+ return if (b) "true" else "false";
+}
+
+test "peer resolve array and const slice" {
+ testPeerResolveArrayConstSlice(true);
+ comptime testPeerResolveArrayConstSlice(true);
+}
+fn testPeerResolveArrayConstSlice(b: bool) void {
+ const value1 = if (b) "aoeu" else @as([]const u8, "zz");
+ const value2 = if (b) @as([]const u8, "zz") else "aoeu";
+ expect(mem.eql(u8, value1, "aoeu"));
+ expect(mem.eql(u8, value2, "zz"));
+}
+
+test "implicitly cast from T to anyerror!?T" {
+ castToOptionalTypeError(1);
+ comptime castToOptionalTypeError(1);
+}
+
+const A = struct {
+ a: i32,
+};
+fn castToOptionalTypeError(z: i32) void {
+ const x = @as(i32, 1);
+ const y: anyerror!?i32 = x;
+ expect((try y).? == 1);
+
+ const f = z;
+ const g: anyerror!?i32 = f;
+
+ const a = A{ .a = z };
+ const b: anyerror!?A = a;
+ expect((b catch unreachable).?.a == 1);
+}
+
+test "implicitly cast from int to anyerror!?T" {
+ implicitIntLitToOptional();
+ comptime implicitIntLitToOptional();
+}
+fn implicitIntLitToOptional() void {
+ const f: ?i32 = 1;
+ const g: anyerror!?i32 = 1;
+}
+
+test "return null from fn() anyerror!?&T" {
+ const a = returnNullFromOptionalTypeErrorRef();
+ const b = returnNullLitFromOptionalTypeErrorRef();
+ expect((try a) == null and (try b) == null);
+}
+fn returnNullFromOptionalTypeErrorRef() anyerror!?*A {
+ const a: ?*A = null;
+ return a;
+}
+fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A {
+ return null;
+}
+
+test "peer type resolution: ?T and T" {
+ expect(peerTypeTAndOptionalT(true, false).? == 0);
+ expect(peerTypeTAndOptionalT(false, false).? == 3);
+ comptime {
+ expect(peerTypeTAndOptionalT(true, false).? == 0);
+ 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" {
+ expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
+ expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
+ comptime {
+ expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
+ expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
+ }
+}
+fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
+ if (a) {
+ return &[_]u8{};
+ }
+
+ return slice[0..1];
+}
+
+test "implicitly cast from [N]T to ?[]const T" {
+ expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
+ comptime expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
+}
+
+fn castToOptionalSlice() ?[]const u8 {
+ return "hi";
+}
+
+test "implicitly cast from [0]T to anyerror![]T" {
+ testCastZeroArrayToErrSliceMut();
+ comptime testCastZeroArrayToErrSliceMut();
+}
+
+fn testCastZeroArrayToErrSliceMut() void {
+ expect((gimmeErrOrSlice() catch unreachable).len == 0);
+}
+
+fn gimmeErrOrSlice() anyerror![]u8 {
+ return &[_]u8{};
+}
+
+test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" {
+ const S = struct {
+ fn doTheTest() anyerror!void {
+ {
+ var data = "hi".*;
+ const slice = data[0..];
+ expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
+ expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
+ }
+ {
+ var data: [2]u8 = "hi".*;
+ const slice = data[0..];
+ expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
+ expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
+ }
+ }
+ };
+ try S.doTheTest();
+ try comptime S.doTheTest();
+}
+fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
+ if (a) {
+ return &[_]u8{};
+ }
+
+ return slice[0..1];
+}
+
+test "resolve undefined with integer" {
+ testResolveUndefWithInt(true, 1234);
+ comptime testResolveUndefWithInt(true, 1234);
+}
+fn testResolveUndefWithInt(b: bool, x: i32) void {
+ const value = if (b) x else undefined;
+ if (b) {
+ expect(value == x);
+ }
+}
+
+test "implicit cast from &const [N]T to []const T" {
+ testCastConstArrayRefToConstSlice();
+ comptime testCastConstArrayRefToConstSlice();
+}
+
+fn testCastConstArrayRefToConstSlice() void {
+ {
+ const blah = "aoeu".*;
+ const const_array_ref = &blah;
+ expect(@TypeOf(const_array_ref) == *const [4:0]u8);
+ const slice: []const u8 = const_array_ref;
+ expect(mem.eql(u8, slice, "aoeu"));
+ }
+ {
+ const blah: [4]u8 = "aoeu".*;
+ const const_array_ref = &blah;
+ expect(@TypeOf(const_array_ref) == *const [4]u8);
+ const slice: []const u8 = const_array_ref;
+ expect(mem.eql(u8, slice, "aoeu"));
+ }
+}
+
+test "peer type resolution: error and [N]T" {
+ expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
+ comptime expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
+ expect(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK"));
+ comptime expect(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK"));
+}
+
+fn testPeerErrorAndArray(x: u8) anyerror![]const u8 {
+ return switch (x) {
+ 0x00 => "OK",
+ else => error.BadValue,
+ };
+}
+fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 {
+ return switch (x) {
+ 0x00 => "OK",
+ 0x01 => "OKK",
+ else => error.BadValue,
+ };
+}
+
+test "@floatToInt" {
+ testFloatToInts();
+ comptime testFloatToInts();
+}
+
+fn testFloatToInts() void {
+ const x = @as(i32, 1e4);
+ expect(x == 10000);
+ const y = @floatToInt(i32, @as(f32, 1e4));
+ expect(y == 10000);
+ expectFloatToInt(f16, 255.1, u8, 255);
+ expectFloatToInt(f16, 127.2, i8, 127);
+ expectFloatToInt(f16, -128.2, i8, -128);
+ expectFloatToInt(f32, 255.1, u8, 255);
+ expectFloatToInt(f32, 127.2, i8, 127);
+ expectFloatToInt(f32, -128.2, i8, -128);
+ expectFloatToInt(comptime_int, 1234, i16, 1234);
+}
+
+fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void {
+ expect(@floatToInt(I, f) == i);
+}
+
+test "cast u128 to f128 and back" {
+ comptime testCast128();
+ testCast128();
+}
+
+fn testCast128() void {
+ expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000);
+}
+
+fn cast128Int(x: f128) u128 {
+ return @bitCast(u128, x);
+}
+
+fn cast128Float(x: u128) f128 {
+ return @bitCast(f128, x);
+}
+
+test "single-item pointer of array to slice and to unknown length pointer" {
+ testCastPtrOfArrayToSliceAndPtr();
+ comptime testCastPtrOfArrayToSliceAndPtr();
+}
+
+fn testCastPtrOfArrayToSliceAndPtr() void {
+ {
+ var array = "aoeu".*;
+ const x: [*]u8 = &array;
+ x[0] += 1;
+ expect(mem.eql(u8, array[0..], "boeu"));
+ const y: []u8 = &array;
+ y[0] += 1;
+ expect(mem.eql(u8, array[0..], "coeu"));
+ }
+ {
+ var array: [4]u8 = "aoeu".*;
+ const x: [*]u8 = &array;
+ x[0] += 1;
+ expect(mem.eql(u8, array[0..], "boeu"));
+ const y: []u8 = &array;
+ y[0] += 1;
+ expect(mem.eql(u8, array[0..], "coeu"));
+ }
+}
+
+test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
+ const window_name = [1][*]const u8{"window name"};
+ const x: [*]const ?[*]const u8 = &window_name;
+ expect(mem.eql(u8, std.mem.spanZ(@ptrCast([*:0]const u8, x[0].?)), "window name"));
+}
+
+test "@intCast comptime_int" {
+ const result = @intCast(i32, 1234);
+ expect(@TypeOf(result) == i32);
+ expect(result == 1234);
+}
+
+test "@floatCast comptime_int and comptime_float" {
+ {
+ const result = @floatCast(f16, 1234);
+ expect(@TypeOf(result) == f16);
+ expect(result == 1234.0);
+ }
+ {
+ const result = @floatCast(f16, 1234.0);
+ expect(@TypeOf(result) == f16);
+ expect(result == 1234.0);
+ }
+ {
+ const result = @floatCast(f32, 1234);
+ expect(@TypeOf(result) == f32);
+ expect(result == 1234.0);
+ }
+ {
+ const result = @floatCast(f32, 1234.0);
+ expect(@TypeOf(result) == f32);
+ expect(result == 1234.0);
+ }
+}
+
+test "vector casts" {
+ const S = struct {
+ fn doTheTest() void {
+ // Upcast (implicit, equivalent to @intCast)
+ var up0: Vector(2, u8) = [_]u8{ 0x55, 0xaa };
+ var up1 = @as(Vector(2, u16), up0);
+ var up2 = @as(Vector(2, u32), up0);
+ var up3 = @as(Vector(2, u64), up0);
+ // Downcast (safety-checked)
+ var down0 = up3;
+ var down1 = @intCast(Vector(2, u32), down0);
+ var down2 = @intCast(Vector(2, u16), down0);
+ var down3 = @intCast(Vector(2, u8), down0);
+
+ expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa }));
+ expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa }));
+ expect(mem.eql(u64, &@as([2]u64, up3), &[2]u64{ 0x55, 0xaa }));
+
+ expect(mem.eql(u32, &@as([2]u32, down1), &[2]u32{ 0x55, 0xaa }));
+ expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa }));
+ expect(mem.eql(u8, &@as([2]u8, down3), &[2]u8{ 0x55, 0xaa }));
+ }
+
+ fn doTheTestFloat() void {
+ var vec = @splat(2, @as(f32, 1234.0));
+ var wider: Vector(2, f64) = vec;
+ expect(wider[0] == 1234.0);
+ expect(wider[1] == 1234.0);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+ S.doTheTestFloat();
+ comptime S.doTheTestFloat();
+}
+
+test "comptime_int @intToFloat" {
+ {
+ const result = @intToFloat(f16, 1234);
+ expect(@TypeOf(result) == f16);
+ expect(result == 1234.0);
+ }
+ {
+ const result = @intToFloat(f32, 1234);
+ expect(@TypeOf(result) == f32);
+ expect(result == 1234.0);
+ }
+ {
+ const result = @intToFloat(f64, 1234);
+ expect(@TypeOf(result) == f64);
+ expect(result == 1234.0);
+ }
+ {
+ const result = @intToFloat(f128, 1234);
+ expect(@TypeOf(result) == f128);
+ expect(result == 1234.0);
+ }
+ // big comptime_int (> 64 bits) to f128 conversion
+ {
+ const result = @intToFloat(f128, 0x1_0000_0000_0000_0000);
+ expect(@TypeOf(result) == f128);
+ expect(result == 0x1_0000_0000_0000_0000.0);
+ }
+}
+
+test "@intCast i32 to u7" {
+ var x: u128 = maxInt(u128);
+ var y: i32 = 120;
+ var z = x >> @intCast(u7, y);
+ expect(z == 0xff);
+}
+
+test "@floatCast cast down" {
+ {
+ var double: f64 = 0.001534;
+ var single = @floatCast(f32, double);
+ expect(single == 0.001534);
+ }
+ {
+ const double: f64 = 0.001534;
+ const single = @floatCast(f32, double);
+ expect(single == 0.001534);
+ }
+}
+
+test "implicit cast undefined to optional" {
+ expect(MakeType(void).getNull() == null);
+ expect(MakeType(void).getNonNull() != null);
+}
+
+fn MakeType(comptime T: type) type {
+ return struct {
+ fn getNull() ?T {
+ return null;
+ }
+
+ fn getNonNull() ?T {
+ return @as(T, undefined);
+ }
+ };
+}
+
+test "implicit cast from *[N]T to ?[*]T" {
+ var x: ?[*]u16 = null;
+ var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
+
+ x = &y;
+ expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
+ x.?[0] = 8;
+ y[3] = 6;
+ expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
+}
+
+test "implicit cast from *[N]T to [*c]T" {
+ var x: [4]u16 = [4]u16{ 0, 1, 2, 3 };
+ var y: [*c]u16 = &x;
+
+ expect(std.mem.eql(u16, x[0..4], y[0..4]));
+ x[0] = 8;
+ y[3] = 6;
+ expect(std.mem.eql(u16, x[0..4], y[0..4]));
+}
+
+test "implicit cast from *T to ?*c_void" {
+ var a: u8 = 1;
+ incrementVoidPtrValue(&a);
+ std.testing.expect(a == 2);
+}
+
+fn incrementVoidPtrValue(value: ?*c_void) void {
+ @ptrCast(*u8, value.?).* += 1;
+}
+
+test "implicit cast from [*]T to ?*c_void" {
+ var a = [_]u8{ 3, 2, 1 };
+ var runtime_zero: usize = 0;
+ incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
+ expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
+}
+
+fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
+ var n: usize = 0;
+ while (n < len) : (n += 1) {
+ @ptrCast([*]u8, array.?)[n] += 1;
+ }
+}
+
+test "*usize to *void" {
+ var i = @as(usize, 0);
+ var v = @ptrCast(*void, &i);
+ v.* = {};
+}
+
+test "compile time int to ptr of function" {
+ foobar(FUNCTION_CONSTANT);
+}
+
+pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
+pub const PFN_void = fn (*c_void) callconv(.C) void;
+
+fn foobar(func: PFN_void) void {
+ std.testing.expect(@ptrToInt(func) == maxInt(usize));
+}
+
+test "implicit ptr to *c_void" {
+ var a: u32 = 1;
+ var ptr: *align(@alignOf(u32)) c_void = &a;
+ var b: *u32 = @ptrCast(*u32, ptr);
+ expect(b.* == 1);
+ var ptr2: ?*align(@alignOf(u32)) c_void = &a;
+ var c: *u32 = @ptrCast(*u32, ptr2.?);
+ expect(c.* == 1);
+}
+
+test "@intCast to comptime_int" {
+ 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;
+ expect(b == 255);
+}
+
+test "@intToEnum passed a comptime_int to an enum with one item" {
+ const E = enum {
+ A,
+ };
+ const x = @intToEnum(E, 0);
+ expect(x == E.A);
+}
+
+test "@intToEnum runtime to an extern enum with duplicate values" {
+ const E = extern enum(u8) {
+ A = 1,
+ B = 1,
+ };
+ var a: u8 = 1;
+ var x = @intToEnum(E, a);
+ expect(x == E.A);
+ expect(x == E.B);
+}
+
+test "@intCast to u0 and use the result" {
+ const S = struct {
+ fn doTheTest(zero: u1, one: u1, bigzero: i32) void {
+ expect((one << @intCast(u0, bigzero)) == 1);
+ expect((zero << @intCast(u0, bigzero)) == 0);
+ }
+ };
+ S.doTheTest(0, 1, 0);
+ comptime S.doTheTest(0, 1, 0);
+}
+
+test "peer type resolution: unreachable, null, slice" {
+ const S = struct {
+ fn doTheTest(num: usize, word: []const u8) void {
+ const result = switch (num) {
+ 0 => null,
+ 1 => word,
+ else => unreachable,
+ };
+ expect(mem.eql(u8, result.?, "hi"));
+ }
+ };
+ S.doTheTest(1, "hi");
+}
+
+test "peer type resolution: unreachable, error set, unreachable" {
+ const Error = error{
+ FileDescriptorAlreadyPresentInSet,
+ OperationCausesCircularLoop,
+ FileDescriptorNotRegistered,
+ SystemResources,
+ UserResourceLimitReached,
+ FileDescriptorIncompatibleWithEpoll,
+ Unexpected,
+ };
+ var err = Error.SystemResources;
+ const transformed_err = switch (err) {
+ error.FileDescriptorAlreadyPresentInSet => unreachable,
+ error.OperationCausesCircularLoop => unreachable,
+ error.FileDescriptorNotRegistered => unreachable,
+ error.SystemResources => error.SystemResources,
+ error.UserResourceLimitReached => error.UserResourceLimitReached,
+ error.FileDescriptorIncompatibleWithEpoll => unreachable,
+ error.Unexpected => unreachable,
+ };
+ expect(transformed_err == error.SystemResources);
+}
+
+test "implicit cast comptime_int to comptime_float" {
+ comptime expect(@as(comptime_float, 10) == @as(f32, 10));
+ expect(2 == 2.0);
+}
+
+test "implicit cast *[0]T to E![]const u8" {
+ var x = @as(anyerror![]const u8, &[0]u8{});
+ expect((x catch unreachable).len == 0);
+}
+
+test "peer cast *[0]T to E![]const T" {
+ var buffer: [5]u8 = "abcde".*;
+ var buf: anyerror![]const u8 = buffer[0..];
+ var b = false;
+ var y = if (b) &[0]u8{} else buf;
+ expect(mem.eql(u8, "abcde", y catch unreachable));
+}
+
+test "peer cast *[0]T to []const T" {
+ var buffer: [5]u8 = "abcde".*;
+ var buf: []const u8 = buffer[0..];
+ var b = false;
+ var y = if (b) &[0]u8{} else buf;
+ expect(mem.eql(u8, "abcde", y));
+}
+
+var global_array: [4]u8 = undefined;
+test "cast from array reference to fn" {
+ const f = @ptrCast(fn () callconv(.C) void, &global_array);
+ expect(@ptrToInt(f) == @ptrToInt(&global_array));
+}
+
+test "*const [N]null u8 to ?[]const u8" {
+ const S = struct {
+ fn doTheTest() void {
+ var a = "Hello";
+ var b: ?[]const u8 = a;
+ expect(mem.eql(u8, b.?, "Hello"));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "peer resolution of string literals" {
+ const S = struct {
+ const E = extern enum {
+ a,
+ b,
+ c,
+ d,
+ };
+
+ fn doTheTest(e: E) void {
+ const cmd = switch (e) {
+ .a => "one",
+ .b => "two",
+ .c => "three",
+ .d => "four",
+ };
+ expect(mem.eql(u8, cmd, "two"));
+ }
+ };
+ S.doTheTest(.b);
+ comptime S.doTheTest(.b);
+}
+
+test "type coercion related to sentinel-termination" {
+ const S = struct {
+ fn doTheTest() void {
+ // [:x]T to []T
+ {
+ var array = [4:0]i32{ 1, 2, 3, 4 };
+ var slice: [:0]i32 = &array;
+ var dest: []i32 = slice;
+ expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 }));
+ }
+
+ // [*:x]T to [*]T
+ {
+ var array = [4:99]i32{ 1, 2, 3, 4 };
+ var dest: [*]i32 = &array;
+ expect(dest[0] == 1);
+ expect(dest[1] == 2);
+ expect(dest[2] == 3);
+ expect(dest[3] == 4);
+ expect(dest[4] == 99);
+ }
+
+ // [N:x]T to [N]T
+ {
+ var array = [4:0]i32{ 1, 2, 3, 4 };
+ var dest: [4]i32 = array;
+ expect(mem.eql(i32, &dest, &[_]i32{ 1, 2, 3, 4 }));
+ }
+
+ // *[N:x]T to *[N]T
+ {
+ var array = [4:0]i32{ 1, 2, 3, 4 };
+ var dest: *[4]i32 = &array;
+ expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 }));
+ }
+
+ // [:x]T to [*:x]T
+ {
+ var array = [4:0]i32{ 1, 2, 3, 4 };
+ var slice: [:0]i32 = &array;
+ var dest: [*:0]i32 = slice;
+ expect(dest[0] == 1);
+ expect(dest[1] == 2);
+ expect(dest[2] == 3);
+ expect(dest[3] == 4);
+ expect(dest[4] == 0);
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "cast i8 fn call peers to i32 result" {
+ const S = struct {
+ fn doTheTest() void {
+ var cond = true;
+ const value: i32 = if (cond) smallBoi() else bigBoi();
+ expect(value == 123);
+ }
+ fn smallBoi() i8 {
+ return 123;
+ }
+ fn bigBoi() i16 {
+ return 1234;
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "return u8 coercing into ?u32 return type" {
+ const S = struct {
+ fn doTheTest() void {
+ expect(foo(123).? == 123);
+ }
+ fn foo(arg: u8) ?u32 {
+ return arg;
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "peer result null and comptime_int" {
+ const S = struct {
+ fn blah(n: i32) ?i32 {
+ if (n == 0) {
+ return null;
+ } else if (n < 0) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ };
+
+ expect(S.blah(0) == null);
+ comptime expect(S.blah(0) == null);
+ expect(S.blah(10).? == 1);
+ comptime expect(S.blah(10).? == 1);
+ expect(S.blah(-10).? == -1);
+ comptime expect(S.blah(-10).? == -1);
+}
+
+test "peer type resolution implicit cast to return type" {
+ const S = struct {
+ fn doTheTest() void {
+ for ("hello") |c| _ = f(c);
+ }
+ fn f(c: u8) []const u8 {
+ return switch (c) {
+ 'h', 'e' => &[_]u8{c}, // should cast to slice
+ 'l', ' ' => &[_]u8{ c, '.' }, // should cast to slice
+ else => ([_]u8{c})[0..], // is a slice
+ };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "peer type resolution implicit cast to variable type" {
+ const S = struct {
+ fn doTheTest() void {
+ var x: []const u8 = undefined;
+ for ("hello") |c| x = switch (c) {
+ 'h', 'e' => &[_]u8{c}, // should cast to slice
+ 'l', ' ' => &[_]u8{ c, '.' }, // should cast to slice
+ else => ([_]u8{c})[0..], // is a slice
+ };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "variable initialization uses result locations properly with regards to the type" {
+ var b = true;
+ const x: i32 = if (b) 1 else 2;
+ expect(x == 1);
+}
+
+test "cast between [*c]T and ?[*:0]T on fn parameter" {
+ const S = struct {
+ const Handler = ?fn ([*c]const u8) callconv(.C) void;
+ fn addCallback(handler: Handler) void {}
+
+ fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void {}
+
+ fn doTheTest() void {
+ addCallback(myCallback);
+ }
+ };
+ S.doTheTest();
+}
+
+test "cast between C pointer with different but compatible types" {
+ const S = struct {
+ fn foo(arg: [*]c_ushort) u16 {
+ return arg[0];
+ }
+ fn doTheTest() void {
+ var x = [_]u16{ 4, 2, 1, 3 };
+ expect(foo(@ptrCast([*]u16, &x)) == 4);
+ }
+ };
+ S.doTheTest();
+}
+
+var global_struct: struct { f0: usize } = undefined;
+
+test "assignment to optional pointer result loc" {
+ var foo: struct { ptr: ?*c_void } = .{ .ptr = &global_struct };
+ expect(foo.ptr.? == @ptrCast(*c_void, &global_struct));
+}
+
+test "peer type resolve string lit with sentinel-terminated mutable slice" {
+ var array: [4:0]u8 = undefined;
+ array[4] = 0; // TODO remove this when #4372 is solved
+ var slice: [:0]u8 = array[0..4 :0];
+ comptime expect(@TypeOf(slice, "hi") == [:0]const u8);
+ comptime expect(@TypeOf("hi", slice) == [:0]const u8);
+}
+
+test "peer type unsigned int to signed" {
+ var w: u31 = 5;
+ var x: u8 = 7;
+ var y: i32 = -5;
+ var a = w + y + x;
+ comptime expect(@TypeOf(a) == i32);
+ expect(a == 7);
+}
+
+test "peer type resolve array pointers, one of them const" {
+ var array1: [4]u8 = undefined;
+ const array2: [5]u8 = undefined;
+ comptime expect(@TypeOf(&array1, &array2) == []const u8);
+ comptime expect(@TypeOf(&array2, &array1) == []const u8);
+}
+
+test "peer type resolve array pointer and unknown pointer" {
+ const const_array: [4]u8 = undefined;
+ var array: [4]u8 = undefined;
+ var const_ptr: [*]const u8 = undefined;
+ var ptr: [*]u8 = undefined;
+
+ comptime expect(@TypeOf(&array, ptr) == [*]u8);
+ comptime expect(@TypeOf(ptr, &array) == [*]u8);
+
+ comptime expect(@TypeOf(&const_array, ptr) == [*]const u8);
+ comptime expect(@TypeOf(ptr, &const_array) == [*]const u8);
+
+ comptime expect(@TypeOf(&array, const_ptr) == [*]const u8);
+ comptime expect(@TypeOf(const_ptr, &array) == [*]const u8);
+
+ comptime expect(@TypeOf(&const_array, const_ptr) == [*]const u8);
+ comptime expect(@TypeOf(const_ptr, &const_array) == [*]const u8);
+}
+
+test "comptime float casts" {
+ const a = @intToFloat(comptime_float, 1);
+ expect(a == 1);
+ expect(@TypeOf(a) == comptime_float);
+ const b = @floatToInt(comptime_int, 2);
+ expect(b == 2);
+ expect(@TypeOf(b) == comptime_int);
+}
+
+test "cast from ?[*]T to ??[*]T" {
+ const a: ??[*]u8 = @as(?[*]u8, null);
+ expect(a != null and a.? == null);
+}
+
+test "cast between *[N]void and []void" {
+ var a: [4]void = undefined;
+ var b: []void = &a;
+ expect(b.len == 4);
+}
diff --git a/test/behavior/const_slice_child.zig b/test/behavior/const_slice_child.zig
new file mode 100644
index 0000000000..92e5121026
--- /dev/null
+++ b/test/behavior/const_slice_child.zig
@@ -0,0 +1,47 @@
+const std = @import("std");
+const debug = std.debug;
+const testing = std.testing;
+const expect = testing.expect;
+
+var argv: [*]const [*]const u8 = undefined;
+
+test "const slice child" {
+ const strs = [_][*]const u8{
+ "one",
+ "two",
+ "three",
+ };
+ argv = &strs;
+ bar(strs.len);
+}
+
+fn foo(args: [][]const u8) void {
+ expect(args.len == 3);
+ expect(streql(args[0], "one"));
+ expect(streql(args[1], "two"));
+ expect(streql(args[2], "three"));
+}
+
+fn bar(argc: usize) void {
+ const args = testing.allocator.alloc([]const u8, argc) catch unreachable;
+ defer testing.allocator.free(args);
+ for (args) |_, i| {
+ const ptr = argv[i];
+ args[i] = ptr[0..strlen(ptr)];
+ }
+ foo(args);
+}
+
+fn strlen(ptr: [*]const u8) usize {
+ var count: usize = 0;
+ while (ptr[count] != 0) : (count += 1) {}
+ return count;
+}
+
+fn streql(a: []const u8, b: []const u8) bool {
+ if (a.len != b.len) return false;
+ for (a) |item, index| {
+ if (b[index] != item) return false;
+ }
+ return true;
+}
diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig
new file mode 100644
index 0000000000..6bfeb485cc
--- /dev/null
+++ b/test/behavior/defer.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" {
+ expect(runSomeErrorDefers(true) catch unreachable);
+ expect(result[0] == 'c');
+ expect(result[1] == 'a');
+
+ const ok = runSomeErrorDefers(false) catch |err| x: {
+ expect(err == error.FalseNotAllowed);
+ break :x true;
+ };
+ expect(ok);
+ expect(result[0] == 'c');
+ expect(result[1] == 'b');
+ 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);
+ }
+}
+
+test "defer and labeled break" {
+ var i = @as(usize, 0);
+
+ blk: {
+ defer i += 1;
+ break :blk;
+ }
+
+ expect(i == 1);
+}
+
+test "errdefer does not apply to fn inside fn" {
+ if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| 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 {
+ expect(notNull().? == 1);
+ }
+
+ fn notNull() ?u8 {
+ var res: ?u8 = 1;
+ defer res = null;
+ return res;
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "errdefer with payload" {
+ const S = struct {
+ fn foo() !i32 {
+ errdefer |a| {
+ expectEqual(error.One, a);
+ }
+ return error.One;
+ }
+ fn doTheTest() void {
+ expectError(error.One, foo());
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig
new file mode 100644
index 0000000000..ecb95be8f5
--- /dev/null
+++ b/test/behavior/enum.zig
@@ -0,0 +1,1204 @@
+const expect = @import("std").testing.expect;
+const mem = @import("std").mem;
+const Tag = @import("std").meta.Tag;
+
+test "extern enum" {
+ const S = struct {
+ const i = extern enum {
+ n = 0,
+ o = 2,
+ p = 4,
+ q = 4,
+ };
+ fn doTheTest(y: c_int) void {
+ var x = i.o;
+ switch (x) {
+ .n, .p => unreachable,
+ .o => {},
+ }
+ }
+ };
+ S.doTheTest(52);
+ comptime S.doTheTest(52);
+}
+
+test "non-exhaustive enum" {
+ const S = struct {
+ const E = enum(u8) {
+ a,
+ b,
+ _,
+ };
+ fn doTheTest(y: u8) void {
+ var e: E = .b;
+ expect(switch (e) {
+ .a => false,
+ .b => true,
+ _ => false,
+ });
+ e = @intToEnum(E, 12);
+ expect(switch (e) {
+ .a => false,
+ .b => false,
+ _ => true,
+ });
+
+ expect(switch (e) {
+ .a => false,
+ .b => false,
+ else => true,
+ });
+ e = .b;
+ expect(switch (e) {
+ .a => false,
+ else => true,
+ });
+
+ expect(@typeInfo(E).Enum.fields.len == 2);
+ e = @intToEnum(E, 12);
+ expect(@enumToInt(e) == 12);
+ e = @intToEnum(E, y);
+ expect(@enumToInt(e) == 52);
+ expect(@typeInfo(E).Enum.is_exhaustive == false);
+ }
+ };
+ S.doTheTest(52);
+ comptime 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);
+ expect(switch (e) {
+ _ => true,
+ });
+ expect(@enumToInt(e) == y);
+
+ expect(@typeInfo(E).Enum.fields.len == 0);
+ expect(@typeInfo(E).Enum.is_exhaustive == false);
+ }
+ };
+ S.doTheTest(42);
+ comptime 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;
+ expect(switch (e) {
+ .a => true,
+ _ => false,
+ });
+ e = @intToEnum(E, 12);
+ expect(switch (e) {
+ .a => false,
+ _ => true,
+ });
+
+ expect(switch (e) {
+ .a => false,
+ else => true,
+ });
+ e = .a;
+ expect(switch (e) {
+ .a => true,
+ else => false,
+ });
+
+ expect(@enumToInt(@intToEnum(E, y)) == y);
+ expect(@typeInfo(E).Enum.fields.len == 1);
+ expect(@typeInfo(E).Enum.is_exhaustive == false);
+ }
+ };
+ S.doTheTest(23);
+ comptime S.doTheTest(23);
+}
+
+test "enum type" {
+ const foo1 = Foo{ .One = 13 };
+ const foo2 = Foo{
+ .Two = Point{
+ .x = 1234,
+ .y = 5678,
+ },
+ };
+ const bar = Bar.B;
+
+ expect(bar == Bar.B);
+ expect(@typeInfo(Foo).Union.fields.len == 3);
+ expect(@typeInfo(Bar).Enum.fields.len == 4);
+ expect(@sizeOf(Foo) == @sizeOf(FooNoVoid));
+ expect(@sizeOf(Bar) == 1);
+}
+
+test "enum as return value" {
+ switch (returnAnInt(13)) {
+ Foo.One => |value| 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,
+};
+
+test "enum to int" {
+ shouldEqual(Number.Zero, 0);
+ shouldEqual(Number.One, 1);
+ shouldEqual(Number.Two, 2);
+ shouldEqual(Number.Three, 3);
+ shouldEqual(Number.Four, 4);
+}
+
+fn shouldEqual(n: Number, expected: u3) void {
+ expect(@enumToInt(n) == expected);
+}
+
+test "int to enum" {
+ testIntToEnumEval(3);
+}
+fn testIntToEnumEval(x: i32) void {
+ expect(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three);
+}
+const IntToEnumNumber = enum {
+ Zero,
+ One,
+ Two,
+ Three,
+ Four,
+};
+
+test "@tagName" {
+ expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
+ comptime expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
+}
+
+test "@tagName extern enum with duplicates" {
+ expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A"));
+ comptime expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A"));
+}
+
+test "@tagName non-exhaustive enum" {
+ expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+ comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+}
+
+fn testEnumTagNameBare(n: anytype) []const u8 {
+ return @tagName(n);
+}
+
+const BareNumber = enum {
+ One,
+ Two,
+ Three,
+};
+
+const ExternDuplicates = extern enum(u8) {
+ A = 1,
+ B = 1,
+};
+
+const NonExhaustive = enum(u8) {
+ A,
+ B,
+ _,
+};
+
+test "enum alignment" {
+ comptime {
+ expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8));
+ 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,
+};
+
+test "enum sizes" {
+ comptime {
+ expect(@sizeOf(ValueCount1) == 0);
+ expect(@sizeOf(ValueCount2) == 1);
+ expect(@sizeOf(ValueCount256) == 1);
+ expect(@sizeOf(ValueCount257) == 2);
+ }
+}
+
+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 expect(Tag(Small) == u2);
+ }
+ {
+ var x = Small2.One;
+ x = Small2.Two;
+ comptime 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;
+ expect(getA(&data) == A.Two);
+ expect(getB(&data) == B.Three3);
+ expect(getC(&data) == C.Four4);
+ comptime expect(@sizeOf(BitFieldOfEnums) == 1);
+
+ data.b = B.Four3;
+ expect(data.b == B.Four3);
+
+ data.a = A.Three;
+ expect(data.a == A.Three);
+ 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" {
+ testCastEnumTag(Small2.Two);
+ comptime testCastEnumTag(Small2.Two);
+}
+
+fn testCastEnumTag(value: Small2) void {
+ expect(@enumToInt(value) == 1);
+}
+
+const MultipleChoice = enum(u32) {
+ A = 20,
+ B = 40,
+ C = 60,
+ D = 1000,
+};
+
+test "enum with specified tag values" {
+ testEnumWithSpecifiedTagValues(MultipleChoice.C);
+ comptime testEnumWithSpecifiedTagValues(MultipleChoice.C);
+}
+
+fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void {
+ expect(@enumToInt(x) == 60);
+ 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" {
+ testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
+ comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
+}
+
+fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void {
+ expect(@enumToInt(x) == 1000);
+ 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" {
+ expect(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1);
+ 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" {
+ 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 @import("std").testing.expect(@sizeOf(Enum) == @sizeOf(u8));
+}
+
+test "empty extern enum with members" {
+ const E = extern enum {
+ A,
+ B,
+ C,
+ };
+ expect(@sizeOf(E) == @sizeOf(c_int));
+}
+
+test "tag name with assigned enum values" {
+ const LocalFoo = enum {
+ A = 1,
+ B = 0,
+ };
+ var b = LocalFoo.B;
+ expect(mem.eql(u8, @tagName(b), "B"));
+}
+
+test "enum literal equality" {
+ const x = .hi;
+ const y = .ok;
+ const z = .hi;
+
+ expect(x != y);
+ expect(x == z);
+}
+
+test "enum literal cast to enum" {
+ const Color = enum {
+ Auto,
+ Off,
+ On,
+ };
+
+ var color1: Color = .Auto;
+ var color2 = Color.Auto;
+ expect(color1 == color2);
+}
+
+test "peer type resolution with enum literal" {
+ const Items = enum {
+ one,
+ two,
+ };
+
+ expect(Items.two == .two);
+ expect(.two == Items.two);
+}
+
+test "enum literal in array literal" {
+ const Items = enum {
+ one,
+ two,
+ };
+
+ const array = [_]Items{
+ .one,
+ .two,
+ };
+
+ expect(array[0] == .one);
+ expect(array[1] == .two);
+}
+
+test "signed integer as enum tag" {
+ const SignedEnum = enum(i2) {
+ A0 = -1,
+ A1 = 0,
+ A2 = 1,
+ };
+
+ expect(@enumToInt(SignedEnum.A0) == -1);
+ expect(@enumToInt(SignedEnum.A1) == 0);
+ expect(@enumToInt(SignedEnum.A2) == 1);
+}
+
+test "enum value allocation" {
+ const LargeEnum = enum(u32) {
+ A0 = 0x80000000,
+ A1,
+ A2,
+ };
+
+ expect(@enumToInt(LargeEnum.A0) == 0x80000000);
+ expect(@enumToInt(LargeEnum.A1) == 0x80000001);
+ 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,
+ };
+ expect(@enumToInt(E.One) == 0);
+ const E2 = enum(u2) {
+ One = 2,
+ };
+ expect(@enumToInt(E2.One) == 2);
+}
+
+test "enum literal casting to optional" {
+ var bar: ?Bar = undefined;
+ bar = .B;
+
+ 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
+
+ expect((try bar) == Bar.B);
+}
+
+test "enum with one member and u1 tag type @enumToInt" {
+ const Enum = enum(u1) {
+ Test,
+ };
+ expect(@enumToInt(Enum.Test) == 0);
+}
+
+test "enum with comptime_int tag type" {
+ const Enum = enum(comptime_int) {
+ One = 3,
+ Two = 2,
+ Three = 1,
+ };
+ comptime expect(Tag(Enum) == comptime_int);
+}
+
+test "enum with one member default to u0 tag type" {
+ const E0 = enum {
+ X,
+ };
+ comptime expect(Tag(E0) == u0);
+}
+
+test "tagName on enum literals" {
+ expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
+ comptime 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;
+ expect(e.method());
+ expect(e.generic_method(bool));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/enum_with_members.zig b/test/behavior/enum_with_members.zig
new file mode 100644
index 0000000000..08b195494b
--- /dev/null
+++ b/test/behavior/enum_with_members.zig
@@ -0,0 +1,27 @@
+const expect = @import("std").testing.expect;
+const mem = @import("std").mem;
+const fmt = @import("std").fmt;
+
+const ET = union(enum) {
+ SINT: i32,
+ UINT: u32,
+
+ pub fn print(a: *const ET, buf: []u8) anyerror!usize {
+ return switch (a.*) {
+ ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}),
+ ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}),
+ };
+ }
+};
+
+test "enum with members" {
+ const a = ET{ .SINT = -42 };
+ const b = ET{ .UINT = 42 };
+ var buf: [20]u8 = undefined;
+
+ expect((a.print(buf[0..]) catch unreachable) == 3);
+ expect(mem.eql(u8, buf[0..3], "-42"));
+
+ expect((b.print(buf[0..]) catch unreachable) == 2);
+ expect(mem.eql(u8, buf[0..2], "42"));
+}
diff --git a/test/behavior/error.zig b/test/behavior/error.zig
new file mode 100644
index 0000000000..529b89d1e2
--- /dev/null
+++ b/test/behavior/error.zig
@@ -0,0 +1,452 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectError = std.testing.expectError;
+const expectEqual = std.testing.expectEqual;
+const mem = std.mem;
+
+pub fn foo() anyerror!i32 {
+ const x = try bar();
+ return x + 1;
+}
+
+pub fn bar() anyerror!i32 {
+ return 13;
+}
+
+pub fn baz() anyerror!i32 {
+ const y = foo() catch 1234;
+ return y + 1;
+}
+
+test "error wrapping" {
+ expect((baz() catch unreachable) == 15);
+}
+
+fn gimmeItBroke() []const u8 {
+ return @errorName(error.ItBroke);
+}
+
+test "@errorName" {
+ expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
+ expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
+}
+
+test "error values" {
+ const a = @errorToInt(error.err1);
+ const b = @errorToInt(error.err2);
+ expect(a != b);
+}
+
+test "redefinition of error values allowed" {
+ shouldBeNotEqual(error.AnError, error.SecondError);
+}
+fn shouldBeNotEqual(a: anyerror, b: anyerror) void {
+ if (a == b) unreachable;
+}
+
+test "error binary operator" {
+ const a = errBinaryOperatorG(true) catch 3;
+ const b = errBinaryOperatorG(false) catch 3;
+ expect(a == 3);
+ expect(b == 10);
+}
+fn errBinaryOperatorG(x: bool) anyerror!isize {
+ return if (x) error.ItBroke else @as(isize, 10);
+}
+
+test "unwrap simple value from error" {
+ const i = unwrapSimpleValueFromErrorDo() catch unreachable;
+ expect(i == 13);
+}
+fn unwrapSimpleValueFromErrorDo() anyerror!isize {
+ return 13;
+}
+
+test "error return in assignment" {
+ doErrReturnInAssignment() catch unreachable;
+}
+
+fn doErrReturnInAssignment() anyerror!void {
+ var x: i32 = undefined;
+ x = try makeANonErr();
+}
+
+fn makeANonErr() anyerror!i32 {
+ return 1;
+}
+
+test "error union type " {
+ testErrorUnionType();
+ comptime testErrorUnionType();
+}
+
+fn testErrorUnionType() void {
+ const x: anyerror!i32 = 1234;
+ if (x) |value| expect(value == 1234) else |_| unreachable;
+ expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);
+ expect(@typeInfo(@typeInfo(@TypeOf(x)).ErrorUnion.error_set) == .ErrorSet);
+ expect(@typeInfo(@TypeOf(x)).ErrorUnion.error_set == anyerror);
+}
+
+test "error set type" {
+ testErrorSetType();
+ comptime testErrorSetType();
+}
+
+const MyErrSet = error{
+ OutOfMemory,
+ FileNotFound,
+};
+
+fn testErrorSetType() void {
+ expect(@typeInfo(MyErrSet).ErrorSet.?.len == 2);
+
+ const a: MyErrSet!i32 = 5678;
+ const b: MyErrSet!i32 = MyErrSet.OutOfMemory;
+
+ if (a) |value| expect(value == 5678) else |err| switch (err) {
+ error.OutOfMemory => unreachable,
+ error.FileNotFound => unreachable,
+ }
+}
+
+test "explicit error set cast" {
+ testExplicitErrorSetCast(Set1.A);
+ comptime testExplicitErrorSetCast(Set1.A);
+}
+
+const Set1 = error{
+ A,
+ B,
+};
+const Set2 = error{
+ A,
+ C,
+};
+
+fn testExplicitErrorSetCast(set1: Set1) void {
+ var x = @errSetCast(Set2, set1);
+ var y = @errSetCast(Set1, x);
+ expect(y == error.A);
+}
+
+test "comptime test error for empty error set" {
+ testComptimeTestErrorEmptySet(1234);
+ comptime testComptimeTestErrorEmptySet(1234);
+}
+
+const EmptyErrorSet = error{};
+
+fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void {
+ if (x) |v| expect(v == 1234) else |err| @compileError("bad");
+}
+
+test "syntax: optional operator in front of error union operator" {
+ comptime {
+ expect(?(anyerror!i32) == ?(anyerror!i32));
+ }
+}
+
+test "comptime err to int of error set with only 1 possible value" {
+ testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
+ comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
+}
+fn testErrToIntWithOnePossibleValue(
+ x: error{A},
+ comptime value: u32,
+) void {
+ if (@errorToInt(x) != value) {
+ @compileError("bad");
+ }
+}
+
+test "empty error union" {
+ const x = error{} || error{};
+}
+
+test "error union peer type resolution" {
+ testErrorUnionPeerTypeResolution(1);
+}
+
+fn testErrorUnionPeerTypeResolution(x: i32) void {
+ const y = switch (x) {
+ 1 => bar_1(),
+ 2 => baz_1(),
+ else => quux_1(),
+ };
+ if (y) |_| {
+ @panic("expected error");
+ } else |e| {
+ expect(e == error.A);
+ }
+}
+
+fn bar_1() anyerror {
+ return error.A;
+}
+
+fn baz_1() !i32 {
+ return error.B;
+}
+
+fn quux_1() !i32 {
+ return error.C;
+}
+
+test "error: fn returning empty error set can be passed as fn returning any error" {
+ entry();
+ comptime entry();
+}
+
+fn entry() void {
+ foo2(bar2);
+}
+
+fn foo2(f: fn () anyerror!void) void {
+ const x = f();
+}
+
+fn bar2() (error{}!void) {}
+
+test "error: Zero sized error set returned with value payload crash" {
+ _ = foo3(0) catch {};
+ _ = comptime foo3(0) catch {};
+}
+
+const Error = error{};
+fn foo3(b: usize) Error!usize {
+ return b;
+}
+
+test "error: Infer error set from literals" {
+ _ = nullLiteral("n") catch |err| handleErrors(err);
+ _ = floatLiteral("n") catch |err| handleErrors(err);
+ _ = intLiteral("n") catch |err| handleErrors(err);
+ _ = comptime nullLiteral("n") catch |err| handleErrors(err);
+ _ = comptime floatLiteral("n") catch |err| handleErrors(err);
+ _ = comptime intLiteral("n") catch |err| handleErrors(err);
+}
+
+fn handleErrors(err: anytype) noreturn {
+ switch (err) {
+ error.T => {},
+ }
+
+ unreachable;
+}
+
+fn nullLiteral(str: []const u8) !?i64 {
+ if (str[0] == 'n') return null;
+
+ return error.T;
+}
+
+fn floatLiteral(str: []const u8) !?f64 {
+ if (str[0] == 'n') return 1.0;
+
+ return error.T;
+}
+
+fn intLiteral(str: []const u8) !?i64 {
+ if (str[0] == 'n') return 1;
+
+ return error.T;
+}
+
+test "nested error union function call in optional unwrap" {
+ const S = struct {
+ const Foo = struct {
+ a: i32,
+ };
+
+ fn errorable() !i32 {
+ var x: Foo = (try getFoo()) orelse return error.Other;
+ return x.a;
+ }
+
+ fn errorable2() !i32 {
+ var x: Foo = (try getFoo2()) orelse return error.Other;
+ return x.a;
+ }
+
+ fn errorable3() !i32 {
+ var x: Foo = (try getFoo3()) orelse return error.Other;
+ return x.a;
+ }
+
+ fn getFoo() anyerror!?Foo {
+ return Foo{ .a = 1234 };
+ }
+
+ fn getFoo2() anyerror!?Foo {
+ return error.Failure;
+ }
+
+ fn getFoo3() anyerror!?Foo {
+ return null;
+ }
+ };
+ expect((try S.errorable()) == 1234);
+ expectError(error.Failure, S.errorable2());
+ expectError(error.Other, S.errorable3());
+ comptime {
+ expect((try S.errorable()) == 1234);
+ expectError(error.Failure, S.errorable2());
+ expectError(error.Other, S.errorable3());
+ }
+}
+
+test "widen cast integer payload of error union function call" {
+ const S = struct {
+ fn errorable() !u64 {
+ var x = @as(u64, try number());
+ return x;
+ }
+
+ fn number() anyerror!u32 {
+ return 1234;
+ }
+ };
+ expect((try S.errorable()) == 1234);
+}
+
+test "return function call to error set from error union function" {
+ const S = struct {
+ fn errorable() anyerror!i32 {
+ return fail();
+ }
+
+ fn fail() anyerror {
+ return error.Failure;
+ }
+ };
+ expectError(error.Failure, S.errorable());
+ comptime expectError(error.Failure, S.errorable());
+}
+
+test "optional error set is the same size as error set" {
+ comptime expect(@sizeOf(?anyerror) == @sizeOf(anyerror));
+ const S = struct {
+ fn returnsOptErrSet() ?anyerror {
+ return null;
+ }
+ };
+ expect(S.returnsOptErrSet() == null);
+ comptime expect(S.returnsOptErrSet() == null);
+}
+
+test "debug info for optional error set" {
+ const SomeError = error{Hello};
+ var a_local_variable: ?SomeError = null;
+}
+
+test "nested catch" {
+ const S = struct {
+ fn entry() void {
+ expectError(error.Bad, func());
+ }
+ fn fail() anyerror!Foo {
+ return error.Wrong;
+ }
+ fn func() anyerror!Foo {
+ const x = fail() catch
+ fail() catch
+ return error.Bad;
+ unreachable;
+ }
+ const Foo = struct {
+ field: i32,
+ };
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+test "implicit cast to optional to error union to return result loc" {
+ const S = struct {
+ fn entry() void {
+ var x: Foo = undefined;
+ if (func(&x)) |opt| {
+ expect(opt != null);
+ } else |_| @panic("expected non error");
+ }
+ fn func(f: *Foo) anyerror!?*Foo {
+ return f;
+ }
+ const Foo = struct {
+ field: i32,
+ };
+ };
+ S.entry();
+ //comptime S.entry(); TODO
+}
+
+test "function pointer with return type that is error union with payload which is pointer of parent struct" {
+ const S = struct {
+ const Foo = struct {
+ fun: fn (a: i32) (anyerror!*Foo),
+ };
+
+ const Err = error{UnspecifiedErr};
+
+ fn bar(a: i32) anyerror!*Foo {
+ return Err.UnspecifiedErr;
+ }
+
+ fn doTheTest() void {
+ var x = Foo{ .fun = bar };
+ expectError(error.UnspecifiedErr, x.fun(1));
+ }
+ };
+ S.doTheTest();
+}
+
+test "return result loc as peer result loc in inferred error set function" {
+ const S = struct {
+ fn doTheTest() void {
+ if (foo(2)) |x| {
+ expect(x.Two);
+ } else |e| switch (e) {
+ error.Whatever => @panic("fail"),
+ }
+ expectError(error.Whatever, foo(99));
+ }
+ const FormValue = union(enum) {
+ One: void,
+ Two: bool,
+ };
+
+ fn foo(id: u64) !FormValue {
+ return switch (id) {
+ 2 => FormValue{ .Two = true },
+ 1 => FormValue{ .One = {} },
+ else => return error.Whatever,
+ };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "error payload type is correctly resolved" {
+ const MyIntWrapper = struct {
+ const Self = @This();
+
+ x: i32,
+
+ pub fn create() anyerror!Self {
+ return Self{ .x = 42 };
+ }
+ };
+
+ expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create());
+}
+
+test "error union comptime caching" {
+ const S = struct {
+ fn foo(comptime arg: anytype) void {}
+ };
+
+ S.foo(@as(anyerror!void, {}));
+ S.foo(@as(anyerror!void, {}));
+} \ No newline at end of file
diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig
new file mode 100644
index 0000000000..05e0a7742d
--- /dev/null
+++ b/test/behavior/eval.zig
@@ -0,0 +1,832 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+
+test "compile time recursion" {
+ expect(some_data.len == 21);
+}
+var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined;
+fn fibonacci(x: i32) i32 {
+ if (x <= 1) return 1;
+ return fibonacci(x - 1) + fibonacci(x - 2);
+}
+
+fn unwrapAndAddOne(blah: ?i32) i32 {
+ return blah.? + 1;
+}
+const should_be_1235 = unwrapAndAddOne(1234);
+test "static add one" {
+ expect(should_be_1235 == 1235);
+}
+
+test "inlined loop" {
+ comptime var i = 0;
+ comptime var sum = 0;
+ inline while (i <= 5) : (i += 1)
+ sum += i;
+ expect(sum == 15);
+}
+
+fn gimme1or2(comptime a: bool) i32 {
+ const x: i32 = 1;
+ const y: i32 = 2;
+ comptime var z: i32 = if (a) x else y;
+ return z;
+}
+test "inline variable gets result of const if" {
+ expect(gimme1or2(true) == 1);
+ expect(gimme1or2(false) == 2);
+}
+
+test "static function evaluation" {
+ expect(statically_added_number == 3);
+}
+const statically_added_number = staticAdd(1, 2);
+fn staticAdd(a: i32, b: i32) i32 {
+ return a + b;
+}
+
+test "const expr eval on single expr blocks" {
+ expect(constExprEvalOnSingleExprBlocksFn(1, true) == 3);
+ comptime expect(constExprEvalOnSingleExprBlocksFn(1, true) == 3);
+}
+
+fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 {
+ const literal = 3;
+
+ const result = if (b) b: {
+ break :b literal;
+ } else b: {
+ break :b x;
+ };
+
+ return result;
+}
+
+test "statically initialized list" {
+ expect(static_point_list[0].x == 1);
+ expect(static_point_list[0].y == 2);
+ expect(static_point_list[1].x == 3);
+ expect(static_point_list[1].y == 4);
+}
+const Point = struct {
+ x: i32,
+ y: i32,
+};
+const static_point_list = [_]Point{
+ makePoint(1, 2),
+ makePoint(3, 4),
+};
+fn makePoint(x: i32, y: i32) Point {
+ return Point{
+ .x = x,
+ .y = y,
+ };
+}
+
+test "static eval list init" {
+ expect(static_vec3.data[2] == 1.0);
+ expect(vec3(0.0, 0.0, 3.0).data[2] == 3.0);
+}
+const static_vec3 = vec3(0.0, 0.0, 1.0);
+pub const Vec3 = struct {
+ data: [3]f32,
+};
+pub fn vec3(x: f32, y: f32, z: f32) Vec3 {
+ return Vec3{
+ .data = [_]f32{
+ x,
+ y,
+ z,
+ },
+ };
+}
+
+test "constant expressions" {
+ var array: [array_size]u8 = undefined;
+ expect(@sizeOf(@TypeOf(array)) == 20);
+}
+const array_size: u8 = 20;
+
+test "constant struct with negation" {
+ expect(vertices[0].x == -0.6);
+}
+const Vertex = struct {
+ x: f32,
+ y: f32,
+ r: f32,
+ g: f32,
+ b: f32,
+};
+const vertices = [_]Vertex{
+ Vertex{
+ .x = -0.6,
+ .y = -0.4,
+ .r = 1.0,
+ .g = 0.0,
+ .b = 0.0,
+ },
+ Vertex{
+ .x = 0.6,
+ .y = -0.4,
+ .r = 0.0,
+ .g = 1.0,
+ .b = 0.0,
+ },
+ Vertex{
+ .x = 0.0,
+ .y = 0.6,
+ .r = 0.0,
+ .g = 0.0,
+ .b = 1.0,
+ },
+};
+
+test "statically initialized struct" {
+ st_init_str_foo.x += 1;
+ expect(st_init_str_foo.x == 14);
+}
+const StInitStrFoo = struct {
+ x: i32,
+ y: bool,
+};
+var st_init_str_foo = StInitStrFoo{
+ .x = 13,
+ .y = true,
+};
+
+test "statically initalized array literal" {
+ const y: [4]u8 = st_init_arr_lit_x;
+ expect(y[3] == 4);
+}
+const st_init_arr_lit_x = [_]u8{
+ 1,
+ 2,
+ 3,
+ 4,
+};
+
+test "const slice" {
+ comptime {
+ const a = "1234567890";
+ expect(a.len == 10);
+ const b = a[1..2];
+ expect(b.len == 1);
+ expect(b[0] == '2');
+ }
+}
+
+test "try to trick eval with runtime if" {
+ expect(testTryToTrickEvalWithRuntimeIf(true) == 10);
+}
+
+fn testTryToTrickEvalWithRuntimeIf(b: bool) usize {
+ comptime var i: usize = 0;
+ inline while (i < 10) : (i += 1) {
+ const result = if (b) false else true;
+ }
+ comptime {
+ return i;
+ }
+}
+
+test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {
+ var runtime = [1]i32{3};
+ comptime var i: usize = 0;
+ inline while (i < 2) : (i += 1) {
+ const result = if (i == 0) [1]i32{2} else runtime;
+ }
+ comptime {
+ expect(i == 2);
+ }
+}
+
+fn max(comptime T: type, a: T, b: T) T {
+ if (T == bool) {
+ return a or b;
+ } else if (a > b) {
+ return a;
+ } else {
+ return b;
+ }
+}
+fn letsTryToCompareBools(a: bool, b: bool) bool {
+ return max(bool, a, b);
+}
+test "inlined block and runtime block phi" {
+ expect(letsTryToCompareBools(true, true));
+ expect(letsTryToCompareBools(true, false));
+ expect(letsTryToCompareBools(false, true));
+ expect(!letsTryToCompareBools(false, false));
+
+ comptime {
+ expect(letsTryToCompareBools(true, true));
+ expect(letsTryToCompareBools(true, false));
+ expect(letsTryToCompareBools(false, true));
+ expect(!letsTryToCompareBools(false, false));
+ }
+}
+
+const CmdFn = struct {
+ name: []const u8,
+ func: fn (i32) i32,
+};
+
+const cmd_fns = [_]CmdFn{
+ CmdFn{
+ .name = "one",
+ .func = one,
+ },
+ CmdFn{
+ .name = "two",
+ .func = two,
+ },
+ CmdFn{
+ .name = "three",
+ .func = three,
+ },
+};
+fn one(value: i32) i32 {
+ return value + 1;
+}
+fn two(value: i32) i32 {
+ return value + 2;
+}
+fn three(value: i32) i32 {
+ return value + 3;
+}
+
+fn performFn(comptime prefix_char: u8, start_value: i32) i32 {
+ var result: i32 = start_value;
+ comptime var i = 0;
+ inline while (i < cmd_fns.len) : (i += 1) {
+ if (cmd_fns[i].name[0] == prefix_char) {
+ result = cmd_fns[i].func(result);
+ }
+ }
+ return result;
+}
+
+test "comptime iterate over fn ptr list" {
+ expect(performFn('t', 1) == 6);
+ expect(performFn('o', 0) == 1);
+ expect(performFn('w', 99) == 99);
+}
+
+test "eval @setRuntimeSafety at compile-time" {
+ const result = comptime fnWithSetRuntimeSafety();
+ expect(result == 1234);
+}
+
+fn fnWithSetRuntimeSafety() i32 {
+ @setRuntimeSafety(true);
+ return 1234;
+}
+
+test "eval @setFloatMode at compile-time" {
+ const result = comptime fnWithFloatMode();
+ expect(result == 1234.0);
+}
+
+fn fnWithFloatMode() f32 {
+ @setFloatMode(std.builtin.FloatMode.Strict);
+ return 1234.0;
+}
+
+const SimpleStruct = struct {
+ field: i32,
+
+ fn method(self: *const SimpleStruct) i32 {
+ return self.field + 3;
+ }
+};
+
+var simple_struct = SimpleStruct{ .field = 1234 };
+
+const bound_fn = simple_struct.method;
+
+test "call method on bound fn referring to var instance" {
+ expect(bound_fn() == 1237);
+}
+
+test "ptr to local array argument at comptime" {
+ comptime {
+ var bytes: [10]u8 = undefined;
+ modifySomeBytes(bytes[0..]);
+ expect(bytes[0] == 'a');
+ expect(bytes[9] == 'b');
+ }
+}
+
+fn modifySomeBytes(bytes: []u8) void {
+ bytes[0] = 'a';
+ bytes[9] = 'b';
+}
+
+test "comparisons 0 <= uint and 0 > uint should be comptime" {
+ testCompTimeUIntComparisons(1234);
+}
+fn testCompTimeUIntComparisons(x: u32) void {
+ if (!(0 <= x)) {
+ @compileError("this condition should be comptime known");
+ }
+ if (0 > x) {
+ @compileError("this condition should be comptime known");
+ }
+ if (!(x >= 0)) {
+ @compileError("this condition should be comptime known");
+ }
+ if (x < 0) {
+ @compileError("this condition should be comptime known");
+ }
+}
+
+test "const ptr to variable data changes at runtime" {
+ expect(foo_ref.name[0] == 'a');
+ foo_ref.name = "b";
+ expect(foo_ref.name[0] == 'b');
+}
+
+const Foo = struct {
+ name: []const u8,
+};
+
+var foo_contents = Foo{ .name = "a" };
+const foo_ref = &foo_contents;
+
+test "create global array with for loop" {
+ expect(global_array[5] == 5 * 5);
+ expect(global_array[9] == 9 * 9);
+}
+
+const global_array = x: {
+ var result: [10]usize = undefined;
+ for (result) |*item, index| {
+ item.* = index * index;
+ }
+ break :x result;
+};
+
+test "compile-time downcast when the bits fit" {
+ comptime {
+ const spartan_count: u16 = 255;
+ const byte = @intCast(u8, spartan_count);
+ expect(byte == 255);
+ }
+}
+
+const hi1 = "hi";
+const hi2 = hi1;
+test "const global shares pointer with other same one" {
+ assertEqualPtrs(&hi1[0], &hi2[0]);
+ comptime expect(&hi1[0] == &hi2[0]);
+}
+fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void {
+ expect(ptr1 == ptr2);
+}
+
+test "@setEvalBranchQuota" {
+ comptime {
+ // 1001 for the loop and then 1 more for the expect fn call
+ @setEvalBranchQuota(1002);
+ var i = 0;
+ var sum = 0;
+ while (i < 1001) : (i += 1) {
+ sum += i;
+ }
+ expect(sum == 500500);
+ }
+}
+
+test "float literal at compile time not lossy" {
+ expect(16777216.0 + 1.0 == 16777217.0);
+ expect(9007199254740992.0 + 1.0 == 9007199254740993.0);
+}
+
+test "f32 at compile time is lossy" {
+ expect(@as(f32, 1 << 24) + 1 == 1 << 24);
+}
+
+test "f64 at compile time is lossy" {
+ expect(@as(f64, 1 << 53) + 1 == 1 << 53);
+}
+
+test "f128 at compile time is lossy" {
+ expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0);
+}
+
+comptime {
+ expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192);
+}
+
+pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type {
+ return struct {
+ pub const Node = struct {};
+ };
+}
+
+test "string literal used as comptime slice is memoized" {
+ const a = "link";
+ const b = "link";
+ comptime expect(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node);
+ comptime expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node);
+}
+
+test "comptime slice of undefined pointer of length 0" {
+ const slice1 = @as([*]i32, undefined)[0..0];
+ expect(slice1.len == 0);
+ const slice2 = @as([*]i32, undefined)[100..100];
+ expect(slice2.len == 0);
+}
+
+fn copyWithPartialInline(s: []u32, b: []u8) void {
+ comptime var i: usize = 0;
+ inline while (i < 4) : (i += 1) {
+ s[i] = 0;
+ s[i] |= @as(u32, b[i * 4 + 0]) << 24;
+ s[i] |= @as(u32, b[i * 4 + 1]) << 16;
+ s[i] |= @as(u32, b[i * 4 + 2]) << 8;
+ s[i] |= @as(u32, b[i * 4 + 3]) << 0;
+ }
+}
+
+test "binary math operator in partially inlined function" {
+ var s: [4]u32 = undefined;
+ var b: [16]u8 = undefined;
+
+ for (b) |*r, i|
+ r.* = @intCast(u8, i + 1);
+
+ copyWithPartialInline(s[0..], b[0..]);
+ expect(s[0] == 0x1020304);
+ expect(s[1] == 0x5060708);
+ expect(s[2] == 0x90a0b0c);
+ expect(s[3] == 0xd0e0f10);
+}
+
+test "comptime function with the same args is memoized" {
+ comptime {
+ expect(MakeType(i32) == MakeType(i32));
+ expect(MakeType(i32) != MakeType(f64));
+ }
+}
+
+fn MakeType(comptime T: type) type {
+ return struct {
+ field: T,
+ };
+}
+
+test "comptime function with mutable pointer is not memoized" {
+ comptime {
+ var x: i32 = 1;
+ const ptr = &x;
+ increment(ptr);
+ increment(ptr);
+ expect(x == 3);
+ }
+}
+
+fn increment(value: *i32) void {
+ value.* += 1;
+}
+
+fn generateTable(comptime T: type) [1010]T {
+ var res: [1010]T = undefined;
+ var i: usize = 0;
+ while (i < 1010) : (i += 1) {
+ res[i] = @intCast(T, i);
+ }
+ return res;
+}
+
+fn doesAlotT(comptime T: type, value: usize) T {
+ @setEvalBranchQuota(5000);
+ const table = comptime blk: {
+ break :blk generateTable(T);
+ };
+ return table[value];
+}
+
+test "@setEvalBranchQuota at same scope as generic function call" {
+ expect(doesAlotT(u32, 2) == 2);
+}
+
+test "comptime slice of slice preserves comptime var" {
+ comptime {
+ var buff: [10]u8 = undefined;
+ buff[0..][0..][0] = 1;
+ expect(buff[0..][0..][0] == 1);
+ }
+}
+
+test "comptime slice of pointer preserves comptime var" {
+ comptime {
+ var buff: [10]u8 = undefined;
+ var a = @ptrCast([*]u8, &buff);
+ a[0..1][0] = 1;
+ expect(buff[0..][0..][0] == 1);
+ }
+}
+
+const SingleFieldStruct = struct {
+ x: i32,
+
+ fn read_x(self: *const SingleFieldStruct) i32 {
+ return self.x;
+ }
+};
+test "const ptr to comptime mutable data is not memoized" {
+ comptime {
+ var foo = SingleFieldStruct{ .x = 1 };
+ expect(foo.read_x() == 1);
+ foo.x = 2;
+ expect(foo.read_x() == 2);
+ }
+}
+
+test "array concat of slices gives slice" {
+ comptime {
+ var a: []const u8 = "aoeu";
+ var b: []const u8 = "asdf";
+ const c = a ++ b;
+ expect(std.mem.eql(u8, c, "aoeuasdf"));
+ }
+}
+
+test "comptime shlWithOverflow" {
+ const ct_shifted: u64 = comptime amt: {
+ var amt = @as(u64, 0);
+ _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
+ break :amt amt;
+ };
+
+ const rt_shifted: u64 = amt: {
+ var amt = @as(u64, 0);
+ _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
+ break :amt amt;
+ };
+
+ expect(ct_shifted == rt_shifted);
+}
+
+test "runtime 128 bit integer division" {
+ var a: u128 = 152313999999999991610955792383;
+ var b: u128 = 10000000000000000000;
+ var c = a / b;
+ expect(c == 15231399999);
+}
+
+pub const Info = struct {
+ version: u8,
+};
+
+pub const diamond_info = Info{ .version = 0 };
+
+test "comptime modification of const struct field" {
+ comptime {
+ var res = diamond_info;
+ res.version = 1;
+ expect(diamond_info.version == 0);
+ expect(res.version == 1);
+ }
+}
+
+test "pointer to type" {
+ comptime {
+ var T: type = i32;
+ expect(T == i32);
+ var ptr = &T;
+ expect(@TypeOf(ptr) == *type);
+ ptr.* = f32;
+ expect(T == f32);
+ expect(*T == *f32);
+ }
+}
+
+test "slice of type" {
+ comptime {
+ var types_array = [_]type{ i32, f64, type };
+ for (types_array) |T, i| {
+ switch (i) {
+ 0 => expect(T == i32),
+ 1 => expect(T == f64),
+ 2 => expect(T == type),
+ else => unreachable,
+ }
+ }
+ for (types_array[0..]) |T, i| {
+ switch (i) {
+ 0 => expect(T == i32),
+ 1 => expect(T == f64),
+ 2 => expect(T == type),
+ else => unreachable,
+ }
+ }
+ }
+}
+
+const Wrapper = struct {
+ T: type,
+};
+
+fn wrap(comptime T: type) Wrapper {
+ return Wrapper{ .T = T };
+}
+
+test "function which returns struct with type field causes implicit comptime" {
+ const ty = wrap(i32).T;
+ expect(ty == i32);
+}
+
+test "call method with comptime pass-by-non-copying-value self parameter" {
+ const S = struct {
+ a: u8,
+
+ fn b(comptime s: @This()) u8 {
+ return s.a;
+ }
+ };
+
+ const s = S{ .a = 2 };
+ var b = s.b();
+ expect(b == 2);
+}
+
+test "@tagName of @typeInfo" {
+ const str = @tagName(@typeInfo(u8));
+ expect(std.mem.eql(u8, str, "Int"));
+}
+
+test "setting backward branch quota just before a generic fn call" {
+ @setEvalBranchQuota(1001);
+ loopNTimes(1001);
+}
+
+fn loopNTimes(comptime n: usize) void {
+ comptime var i = 0;
+ inline while (i < n) : (i += 1) {}
+}
+
+test "variable inside inline loop that has different types on different iterations" {
+ testVarInsideInlineLoop(.{ true, @as(u32, 42) });
+}
+
+fn testVarInsideInlineLoop(args: anytype) void {
+ comptime var i = 0;
+ inline while (i < args.len) : (i += 1) {
+ const x = args[i];
+ if (i == 0) expect(x);
+ if (i == 1) expect(x == 42);
+ }
+}
+
+test "inline for with same type but different values" {
+ var res: usize = 0;
+ inline for ([_]type{ [2]u8, [1]u8, [2]u8 }) |T| {
+ var a: T = undefined;
+ res += a.len;
+ }
+ expect(res == 5);
+}
+
+test "refer to the type of a generic function" {
+ const Func = fn (type) void;
+ const f: Func = doNothingWithType;
+ f(i32);
+}
+
+fn doNothingWithType(comptime T: type) void {}
+
+test "zero extend from u0 to u1" {
+ var zero_u0: u0 = 0;
+ var zero_u1: u1 = zero_u0;
+ expect(zero_u1 == 0);
+}
+
+test "bit shift a u1" {
+ var x: u1 = 1;
+ var y = x << 0;
+ expect(y == 1);
+}
+
+test "comptime pointer cast array and then slice" {
+ const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
+ const sliceA: []const u8 = ptrA[0..2];
+
+ const ptrB: [*]const u8 = &array;
+ const sliceB: []const u8 = ptrB[0..2];
+
+ expect(sliceA[1] == 2);
+ expect(sliceB[1] == 2);
+}
+
+test "slice bounds in comptime concatenation" {
+ const bs = comptime blk: {
+ const b = "........1........";
+ break :blk b[8..9];
+ };
+ const str = "" ++ bs;
+ expect(str.len == 1);
+ expect(std.mem.eql(u8, str, "1"));
+
+ const str2 = bs ++ "";
+ expect(str2.len == 1);
+ expect(std.mem.eql(u8, str2, "1"));
+}
+
+test "comptime bitwise operators" {
+ comptime {
+ expect(3 & 1 == 1);
+ expect(3 & -1 == 3);
+ expect(-3 & -1 == -3);
+ expect(3 | -1 == -1);
+ expect(-3 | -1 == -1);
+ expect(3 ^ -1 == -4);
+ expect(-3 ^ -1 == 2);
+ expect(~@as(i8, -1) == 0);
+ expect(~@as(i128, -1) == 0);
+ expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611);
+ expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615);
+ expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff);
+ }
+}
+
+test "*align(1) u16 is the same as *align(1:0:2) u16" {
+ comptime {
+ expect(*align(1:0:2) u16 == *align(1) u16);
+ expect(*align(2:0:2) u16 == *u16);
+ }
+}
+
+test "array concatenation forces comptime" {
+ var a = oneItem(3) ++ oneItem(4);
+ expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 }));
+}
+
+test "array multiplication forces comptime" {
+ var a = oneItem(3) ** scalar(2);
+ expect(std.mem.eql(i32, &a, &[_]i32{ 3, 3 }));
+}
+
+fn oneItem(x: i32) [1]i32 {
+ return [_]i32{x};
+}
+
+fn scalar(x: u32) u32 {
+ return x;
+}
+
+test "no undeclared identifier error in unanalyzed branches" {
+ if (false) {
+ lol_this_doesnt_exist = nonsense;
+ }
+}
+
+test "comptime assign int to optional int" {
+ comptime {
+ var x: ?i32 = null;
+ x = 2;
+ x.? *= 10;
+ expectEqual(20, x.?);
+ }
+}
+
+test "return 0 from function that has u0 return type" {
+ const S = struct {
+ fn foo_zero() u0 {
+ return 0;
+ }
+ };
+ comptime {
+ if (S.foo_zero() != 0) {
+ @compileError("test failed");
+ }
+ }
+}
+
+test "two comptime calls with array default initialized to undefined" {
+ const S = struct {
+ const CrossTarget = struct {
+ dynamic_linker: DynamicLinker = DynamicLinker{},
+
+ pub fn parse() void {
+ var result: CrossTarget = .{};
+ result.getCpuArch();
+ }
+
+ pub fn getCpuArch(self: CrossTarget) void {}
+ };
+
+ const DynamicLinker = struct {
+ buffer: [255]u8 = undefined,
+ };
+ };
+
+ comptime {
+ S.CrossTarget.parse();
+ S.CrossTarget.parse();
+ }
+}
diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig
new file mode 100644
index 0000000000..6026a49d12
--- /dev/null
+++ b/test/behavior/field_parent_ptr.zig
@@ -0,0 +1,41 @@
+const expect = @import("std").testing.expect;
+
+test "@fieldParentPtr non-first field" {
+ testParentFieldPtr(&foo.c);
+ comptime testParentFieldPtr(&foo.c);
+}
+
+test "@fieldParentPtr first field" {
+ testParentFieldPtrFirst(&foo.a);
+ comptime testParentFieldPtrFirst(&foo.a);
+}
+
+const Foo = struct {
+ a: bool,
+ b: f32,
+ c: i32,
+ d: i32,
+};
+
+const foo = Foo{
+ .a = true,
+ .b = 0.123,
+ .c = 1234,
+ .d = -10,
+};
+
+fn testParentFieldPtr(c: *const i32) void {
+ expect(c == &foo.c);
+
+ const base = @fieldParentPtr(Foo, "c", c);
+ expect(base == &foo);
+ expect(&base.c == c);
+}
+
+fn testParentFieldPtrFirst(a: *const bool) void {
+ expect(a == &foo.a);
+
+ const base = @fieldParentPtr(Foo, "a", a);
+ expect(base == &foo);
+ expect(&base.a == a);
+}
diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig
new file mode 100644
index 0000000000..9f2e45ed26
--- /dev/null
+++ b/test/behavior/floatop.zig
@@ -0,0 +1,465 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const math = std.math;
+const pi = std.math.pi;
+const e = std.math.e;
+const Vector = std.meta.Vector;
+
+const epsilon = 0.000001;
+
+test "@sqrt" {
+ comptime testSqrt();
+ testSqrt();
+}
+
+fn testSqrt() void {
+ {
+ var a: f16 = 4;
+ expect(@sqrt(a) == 2);
+ }
+ {
+ var a: f32 = 9;
+ expect(@sqrt(a) == 3);
+ var b: f32 = 1.1;
+ expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon));
+ }
+ {
+ var a: f64 = 25;
+ expect(@sqrt(a) == 5);
+ }
+ {
+ const a: comptime_float = 25.0;
+ expect(@sqrt(a) == 5.0);
+ }
+ // TODO https://github.com/ziglang/zig/issues/4026
+ //{
+ // var a: f128 = 49;
+ // expect(@sqrt(a) == 7);
+ //}
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
+ var result = @sqrt(v);
+ expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @sqrt(@as(f32, 3.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon));
+ }
+}
+
+test "more @sqrt f16 tests" {
+ // TODO these are not all passing at comptime
+ expect(@sqrt(@as(f16, 0.0)) == 0.0);
+ expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon));
+ expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon));
+ expect(@sqrt(@as(f16, 4.0)) == 2.0);
+ expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon));
+ expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon));
+ expect(@sqrt(@as(f16, 64.0)) == 8.0);
+ expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon));
+ expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon));
+
+ // special cases
+ expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16)))));
+ expect(@sqrt(@as(f16, 0.0)) == 0.0);
+ expect(@sqrt(@as(f16, -0.0)) == -0.0);
+ expect(math.isNan(@sqrt(@as(f16, -1.0))));
+ expect(math.isNan(@sqrt(@as(f16, math.nan(f16)))));
+}
+
+test "@sin" {
+ comptime testSin();
+ testSin();
+}
+
+fn testSin() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 0;
+ expect(@sin(a) == 0);
+ }
+ {
+ var a: f32 = 0;
+ expect(@sin(a) == 0);
+ }
+ {
+ var a: f64 = 0;
+ expect(@sin(a) == 0);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
+ var result = @sin(v);
+ expect(math.approxEqAbs(f32, @sin(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @sin(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @sin(@as(f32, 3.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @sin(@as(f32, 4.4)), result[3], epsilon));
+ }
+}
+
+test "@cos" {
+ comptime testCos();
+ testCos();
+}
+
+fn testCos() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 0;
+ expect(@cos(a) == 1);
+ }
+ {
+ var a: f32 = 0;
+ expect(@cos(a) == 1);
+ }
+ {
+ var a: f64 = 0;
+ expect(@cos(a) == 1);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
+ var result = @cos(v);
+ expect(math.approxEqAbs(f32, @cos(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @cos(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @cos(@as(f32, 3.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @cos(@as(f32, 4.4)), result[3], epsilon));
+ }
+}
+
+test "@exp" {
+ comptime testExp();
+ testExp();
+}
+
+fn testExp() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 0;
+ expect(@exp(a) == 1);
+ }
+ {
+ var a: f32 = 0;
+ expect(@exp(a) == 1);
+ }
+ {
+ var a: f64 = 0;
+ expect(@exp(a) == 1);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
+ var result = @exp(v);
+ expect(math.approxEqAbs(f32, @exp(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @exp(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @exp(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @exp(@as(f32, 0.4)), result[3], epsilon));
+ }
+}
+
+test "@exp2" {
+ comptime testExp2();
+ testExp2();
+}
+
+fn testExp2() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 2;
+ expect(@exp2(a) == 4);
+ }
+ {
+ var a: f32 = 2;
+ expect(@exp2(a) == 4);
+ }
+ {
+ var a: f64 = 2;
+ expect(@exp2(a) == 4);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
+ var result = @exp2(v);
+ expect(math.approxEqAbs(f32, @exp2(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @exp2(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.4)), result[3], epsilon));
+ }
+}
+
+test "@log" {
+ // Old musl (and glibc?), and our current math.ln implementation do not return 1
+ // so also accept those values.
+ comptime testLog();
+ testLog();
+}
+
+fn testLog() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = e;
+ expect(math.approxEqAbs(f16, @log(a), 1, epsilon));
+ }
+ {
+ var a: f32 = e;
+ expect(@log(a) == 1 or @log(a) == @bitCast(f32, @as(u32, 0x3f7fffff)));
+ }
+ {
+ var a: f64 = e;
+ expect(@log(a) == 1 or @log(a) == @bitCast(f64, @as(u64, 0x3ff0000000000000)));
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
+ var result = @log(v);
+ expect(math.approxEqAbs(f32, @log(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @log(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @log(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @log(@as(f32, 0.4)), result[3], epsilon));
+ }
+}
+
+test "@log2" {
+ comptime testLog2();
+ testLog2();
+}
+
+fn testLog2() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 4;
+ expect(@log2(a) == 2);
+ }
+ {
+ var a: f32 = 4;
+ expect(@log2(a) == 2);
+ }
+ {
+ var a: f64 = 4;
+ expect(@log2(a) == 2);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
+ var result = @log2(v);
+ expect(math.approxEqAbs(f32, @log2(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @log2(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @log2(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @log2(@as(f32, 0.4)), result[3], epsilon));
+ }
+}
+
+test "@log10" {
+ comptime testLog10();
+ testLog10();
+}
+
+fn testLog10() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 100;
+ expect(@log10(a) == 2);
+ }
+ {
+ var a: f32 = 100;
+ expect(@log10(a) == 2);
+ }
+ {
+ var a: f64 = 1000;
+ expect(@log10(a) == 3);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
+ var result = @log10(v);
+ expect(math.approxEqAbs(f32, @log10(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @log10(@as(f32, 2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @log10(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @log10(@as(f32, 0.4)), result[3], epsilon));
+ }
+}
+
+test "@fabs" {
+ comptime testFabs();
+ testFabs();
+}
+
+fn testFabs() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = -2.5;
+ var b: f16 = 2.5;
+ expect(@fabs(a) == 2.5);
+ expect(@fabs(b) == 2.5);
+ }
+ {
+ var a: f32 = -2.5;
+ var b: f32 = 2.5;
+ expect(@fabs(a) == 2.5);
+ expect(@fabs(b) == 2.5);
+ }
+ {
+ var a: f64 = -2.5;
+ var b: f64 = 2.5;
+ expect(@fabs(a) == 2.5);
+ expect(@fabs(b) == 2.5);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
+ var result = @fabs(v);
+ expect(math.approxEqAbs(f32, @fabs(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @fabs(@as(f32, -2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @fabs(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @fabs(@as(f32, -0.4)), result[3], epsilon));
+ }
+}
+
+test "@floor" {
+ comptime testFloor();
+ testFloor();
+}
+
+fn testFloor() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 2.1;
+ expect(@floor(a) == 2);
+ }
+ {
+ var a: f32 = 2.1;
+ expect(@floor(a) == 2);
+ }
+ {
+ var a: f64 = 3.5;
+ expect(@floor(a) == 3);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
+ var result = @floor(v);
+ expect(math.approxEqAbs(f32, @floor(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @floor(@as(f32, -2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @floor(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @floor(@as(f32, -0.4)), result[3], epsilon));
+ }
+}
+
+test "@ceil" {
+ comptime testCeil();
+ testCeil();
+}
+
+fn testCeil() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 2.1;
+ expect(@ceil(a) == 3);
+ }
+ {
+ var a: f32 = 2.1;
+ expect(@ceil(a) == 3);
+ }
+ {
+ var a: f64 = 3.5;
+ expect(@ceil(a) == 4);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
+ var result = @ceil(v);
+ expect(math.approxEqAbs(f32, @ceil(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @ceil(@as(f32, -2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @ceil(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @ceil(@as(f32, -0.4)), result[3], epsilon));
+ }
+}
+
+test "@trunc" {
+ comptime testTrunc();
+ testTrunc();
+}
+
+fn testTrunc() void {
+ // TODO test f128, and c_longdouble
+ // https://github.com/ziglang/zig/issues/4026
+ {
+ var a: f16 = 2.1;
+ expect(@trunc(a) == 2);
+ }
+ {
+ var a: f32 = 2.1;
+ expect(@trunc(a) == 2);
+ }
+ {
+ var a: f64 = -3.5;
+ expect(@trunc(a) == -3);
+ }
+ {
+ var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
+ var result = @trunc(v);
+ expect(math.approxEqAbs(f32, @trunc(@as(f32, 1.1)), result[0], epsilon));
+ expect(math.approxEqAbs(f32, @trunc(@as(f32, -2.2)), result[1], epsilon));
+ expect(math.approxEqAbs(f32, @trunc(@as(f32, 0.3)), result[2], epsilon));
+ expect(math.approxEqAbs(f32, @trunc(@as(f32, -0.4)), result[3], epsilon));
+ }
+}
+
+test "floating point comparisons" {
+ testFloatComparisons();
+ comptime testFloatComparisons();
+}
+
+fn testFloatComparisons() void {
+ inline for ([_]type{ f16, f32, f64, f128 }) |ty| {
+ // No decimal part
+ {
+ const x: ty = 1.0;
+ expect(x == 1);
+ expect(x != 0);
+ expect(x > 0);
+ expect(x < 2);
+ expect(x >= 1);
+ expect(x <= 1);
+ }
+ // Non-zero decimal part
+ {
+ const x: ty = 1.5;
+ expect(x != 1);
+ expect(x != 2);
+ expect(x > 1);
+ expect(x < 2);
+ expect(x >= 1);
+ expect(x <= 2);
+ }
+ }
+}
+
+test "different sized float comparisons" {
+ testDifferentSizedFloatComparisons();
+ comptime testDifferentSizedFloatComparisons();
+}
+
+fn testDifferentSizedFloatComparisons() void {
+ var a: f16 = 1;
+ var b: f64 = 2;
+ expect(a < b);
+}
+
+// TODO This is waiting on library support for the Windows build (not sure why the other's don't need it)
+//test "@nearbyint" {
+// comptime testNearbyInt();
+// testNearbyInt();
+//}
+
+//fn testNearbyInt() void {
+// // TODO test f16, f128, and c_longdouble
+// // https://github.com/ziglang/zig/issues/4026
+// {
+// var a: f32 = 2.1;
+// expect(@nearbyint(a) == 2);
+// }
+// {
+// var a: f64 = -3.75;
+// expect(@nearbyint(a) == -4);
+// }
+//}
diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig
new file mode 100644
index 0000000000..6cefee6a01
--- /dev/null
+++ b/test/behavior/fn.zig
@@ -0,0 +1,287 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
+
+test "params" {
+ expect(testParamsAdd(22, 11) == 33);
+}
+fn testParamsAdd(a: i32, b: i32) i32 {
+ return a + b;
+}
+
+test "local variables" {
+ testLocVars(2);
+}
+fn testLocVars(b: i32) void {
+ const a: i32 = 1;
+ if (a + b != 3) unreachable;
+}
+
+test "void parameters" {
+ voidFun(1, void{}, 2, {});
+}
+fn voidFun(a: i32, b: void, c: i32, d: void) void {
+ const v = b;
+ const vv: void = if (a == 1) v else {};
+ expect(a + c == 3);
+ return vv;
+}
+
+test "mutable local variables" {
+ var zero: i32 = 0;
+ expect(zero == 0);
+
+ var i = @as(i32, 0);
+ while (i != 3) {
+ i += 1;
+ }
+ expect(i == 3);
+}
+
+test "separate block scopes" {
+ {
+ const no_conflict: i32 = 5;
+ expect(no_conflict == 5);
+ }
+
+ const c = x: {
+ const no_conflict = @as(i32, 10);
+ break :x no_conflict;
+ };
+ expect(c == 10);
+}
+
+test "call function with empty string" {
+ acceptsString("");
+}
+
+fn acceptsString(foo: []u8) void {}
+
+fn @"weird function name"() i32 {
+ return 1234;
+}
+test "weird function name" {
+ expect(@"weird function name"() == 1234);
+}
+
+test "implicit cast function unreachable return" {
+ wantsFnWithVoid(fnWithUnreachable);
+}
+
+fn wantsFnWithVoid(f: fn () void) void {}
+
+fn fnWithUnreachable() noreturn {
+ unreachable;
+}
+
+test "function pointers" {
+ const fns = [_]@TypeOf(fn1){
+ fn1,
+ fn2,
+ fn3,
+ fn4,
+ };
+ for (fns) |f, i| {
+ expect(f() == @intCast(u32, i) + 5);
+ }
+}
+fn fn1() u32 {
+ return 5;
+}
+fn fn2() u32 {
+ return 6;
+}
+fn fn3() u32 {
+ return 7;
+}
+fn fn4() u32 {
+ return 8;
+}
+
+test "number literal as an argument" {
+ numberLiteralArg(3);
+ comptime numberLiteralArg(3);
+}
+
+fn numberLiteralArg(a: anytype) void {
+ expect(a == 3);
+}
+
+test "assign inline fn to const variable" {
+ const a = inlineFn;
+ a();
+}
+
+fn inlineFn() callconv(.Inline) void {}
+
+test "pass by non-copying value" {
+ expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3);
+}
+
+const Point = struct {
+ x: i32,
+ y: i32,
+};
+
+fn addPointCoords(pt: Point) i32 {
+ return pt.x + pt.y;
+}
+
+test "pass by non-copying value through var arg" {
+ expect(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3);
+}
+
+fn addPointCoordsVar(pt: anytype) i32 {
+ comptime expect(@TypeOf(pt) == Point);
+ return pt.x + pt.y;
+}
+
+test "pass by non-copying value as method" {
+ var pt = Point2{ .x = 1, .y = 2 };
+ expect(pt.addPointCoords() == 3);
+}
+
+const Point2 = struct {
+ x: i32,
+ y: i32,
+
+ fn addPointCoords(self: Point2) i32 {
+ return self.x + self.y;
+ }
+};
+
+test "pass by non-copying value as method, which is generic" {
+ var pt = Point3{ .x = 1, .y = 2 };
+ expect(pt.addPointCoords(i32) == 3);
+}
+
+const Point3 = struct {
+ x: i32,
+ y: i32,
+
+ fn addPointCoords(self: Point3, comptime T: type) i32 {
+ return self.x + self.y;
+ }
+};
+
+test "pass by non-copying value as method, at comptime" {
+ comptime {
+ var pt = Point2{ .x = 1, .y = 2 };
+ expect(pt.addPointCoords() == 3);
+ }
+}
+
+fn outer(y: u32) fn (u32) u32 {
+ const Y = @TypeOf(y);
+ const st = struct {
+ fn get(z: u32) u32 {
+ return z + @sizeOf(Y);
+ }
+ };
+ return st.get;
+}
+
+test "return inner function which references comptime variable of outer function" {
+ var func = outer(10);
+ expect(func(3) == 7);
+}
+
+test "extern struct with stdcallcc fn pointer" {
+ const S = extern struct {
+ ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32,
+
+ fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 {
+ return 1234;
+ }
+ };
+
+ var s: S = undefined;
+ s.ptr = S.foo;
+ expect(s.ptr() == 1234);
+}
+
+test "implicit cast fn call result to optional in field result" {
+ const S = struct {
+ fn entry() void {
+ var x = Foo{
+ .field = optionalPtr(),
+ };
+ expect(x.field.?.* == 999);
+ }
+
+ const glob: i32 = 999;
+
+ fn optionalPtr() *const i32 {
+ return &glob;
+ }
+
+ const Foo = struct {
+ field: ?*const i32,
+ };
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+test "discard the result of a function that returns a struct" {
+ const S = struct {
+ fn entry() void {
+ _ = func();
+ }
+
+ fn func() Foo {
+ return undefined;
+ }
+
+ const Foo = struct {
+ a: u64,
+ b: u64,
+ };
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+test "function call with anon list literal" {
+ const S = struct {
+ fn doTheTest() void {
+ consumeVec(.{ 9, 8, 7 });
+ }
+
+ fn consumeVec(vec: [3]f32) void {
+ expect(vec[0] == 9);
+ expect(vec[1] == 8);
+ expect(vec[2] == 7);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "ability to give comptime types and non comptime types to same parameter" {
+ const S = struct {
+ fn doTheTest() void {
+ var x: i32 = 1;
+ expect(foo(x) == 10);
+ expect(foo(i32) == 20);
+ }
+
+ fn foo(arg: anytype) i32 {
+ if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;
+ return 9 + arg;
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "function with inferred error set but returning no error" {
+ const S = struct {
+ fn foo() !void {}
+ };
+
+ const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
+ expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
+}
diff --git a/test/behavior/fn_delegation.zig b/test/behavior/fn_delegation.zig
new file mode 100644
index 0000000000..57006805c8
--- /dev/null
+++ b/test/behavior/fn_delegation.zig
@@ -0,0 +1,39 @@
+const expect = @import("std").testing.expect;
+
+const Foo = struct {
+ a: u64 = 10,
+
+ fn one(self: Foo) u64 {
+ return self.a + 1;
+ }
+
+ const two = __two;
+
+ fn __two(self: Foo) u64 {
+ return self.a + 2;
+ }
+
+ const three = __three;
+
+ const four = custom(Foo, 4);
+};
+
+fn __three(self: Foo) u64 {
+ return self.a + 3;
+}
+
+fn custom(comptime T: type, comptime num: u64) fn (T) u64 {
+ return struct {
+ fn function(self: T) u64 {
+ return self.a + num;
+ }
+ }.function;
+}
+
+test "fn delegation" {
+ const foo = Foo{};
+ expect(foo.one() == 11);
+ expect(foo.two() == 12);
+ expect(foo.three() == 13);
+ expect(foo.four() == 14);
+}
diff --git a/test/behavior/fn_in_struct_in_comptime.zig b/test/behavior/fn_in_struct_in_comptime.zig
new file mode 100644
index 0000000000..030693ac59
--- /dev/null
+++ b/test/behavior/fn_in_struct_in_comptime.zig
@@ -0,0 +1,17 @@
+const expect = @import("std").testing.expect;
+
+fn get_foo() fn (*u8) usize {
+ comptime {
+ return struct {
+ fn func(ptr: *u8) usize {
+ var u = @ptrToInt(ptr);
+ return u;
+ }
+ }.func;
+ }
+}
+
+test "define a function in an anonymous struct in comptime" {
+ const foo = get_foo();
+ expect(foo(@intToPtr(*u8, 12345)) == 12345);
+}
diff --git a/test/behavior/for.zig b/test/behavior/for.zig
new file mode 100644
index 0000000000..c49c73802d
--- /dev/null
+++ b/test/behavior/for.zig
@@ -0,0 +1,172 @@
+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..]);
+ expect(mem.eql(u8, &target, "bcdefgh"));
+
+ for (source) |*c, i|
+ expect(@TypeOf(c) == *const u8);
+ for (target) |*c, i|
+ 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| {
+ 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| {
+ 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) |item, index| {
+ buffer[buf_index] = @intCast(u8, index);
+ buf_index += 1;
+ }
+
+ expect(mem.eql(u8, buffer[0..buf_index], &expected_result));
+}
+
+test "break from outer for loop" {
+ testBreakOuter();
+ comptime testBreakOuter();
+}
+
+fn testBreakOuter() void {
+ var array = "aoeu";
+ var count: usize = 0;
+ outer: for (array) |_| {
+ for (array) |_| {
+ count += 1;
+ break :outer;
+ }
+ }
+ expect(count == 1);
+}
+
+test "continue outer for loop" {
+ testContinueOuter();
+ comptime testContinueOuter();
+}
+
+fn testContinueOuter() void {
+ var array = "aoeu";
+ var counter: usize = 0;
+ outer: for (array) |_| {
+ for (array) |_| {
+ counter += 1;
+ continue :outer;
+ }
+ }
+ 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| {
+ if (f) break false;
+ if (t) break true;
+ } else false;
+ expect(ok);
+ }
+ };
+ S.entry(true, false);
+ comptime 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| {
+ @panic("fail");
+ }
+ }
+ };
+ S.doTheTest(&[_]u8{ 1, 2 });
+ comptime 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;
+ expectEqual(value, i + 1);
+ }
+ }
+ };
+ S.doTheTest();
+ comptime 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| expect(x == i + 1);
+ for (ptr) |*x, i| expect(x.* == i + 1);
+ }
+ };
+ S.doTheTest(&[_]u8{ 1, 2, 3, 4 });
+ comptime S.doTheTest(&[_]u8{ 1, 2, 3, 4 });
+}
diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig
new file mode 100644
index 0000000000..ee6496bcaa
--- /dev/null
+++ b/test/behavior/generics.zig
@@ -0,0 +1,169 @@
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
+
+test "simple generic fn" {
+ expect(max(i32, 3, -1) == 3);
+ expect(max(f32, 0.123, 0.456) == 0.456);
+ expect(add(2, 3) == 5);
+}
+
+fn max(comptime T: type, a: T, b: T) T {
+ return if (a > b) a else b;
+}
+
+fn add(comptime a: i32, b: i32) i32 {
+ return (comptime a) + b;
+}
+
+const the_max = max(u32, 1234, 5678);
+test "compile time generic eval" {
+ expect(the_max == 5678);
+}
+
+fn gimmeTheBigOne(a: u32, b: u32) u32 {
+ return max(u32, a, b);
+}
+
+fn shouldCallSameInstance(a: u32, b: u32) u32 {
+ return max(u32, a, b);
+}
+
+fn sameButWithFloats(a: f64, b: f64) f64 {
+ return max(f64, a, b);
+}
+
+test "fn with comptime args" {
+ expect(gimmeTheBigOne(1234, 5678) == 5678);
+ expect(shouldCallSameInstance(34, 12) == 34);
+ expect(sameButWithFloats(0.43, 0.49) == 0.49);
+}
+
+test "var params" {
+ expect(max_i32(12, 34) == 34);
+ expect(max_f64(1.2, 3.4) == 3.4);
+}
+
+comptime {
+ expect(max_i32(12, 34) == 34);
+ expect(max_f64(1.2, 3.4) == 3.4);
+}
+
+fn max_var(a: anytype, b: anytype) @TypeOf(a + b) {
+ return if (a > b) a else b;
+}
+
+fn max_i32(a: i32, b: i32) i32 {
+ return max_var(a, b);
+}
+
+fn max_f64(a: f64, b: f64) f64 {
+ return max_var(a, b);
+}
+
+pub fn List(comptime T: type) type {
+ return SmallList(T, 8);
+}
+
+pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type {
+ return struct {
+ items: []T,
+ length: usize,
+ prealloc_items: [STATIC_SIZE]T,
+ };
+}
+
+test "function with return type type" {
+ var list: List(i32) = undefined;
+ var list2: List(i32) = undefined;
+ list.length = 10;
+ list2.length = 10;
+ expect(list.prealloc_items.len == 8);
+ expect(list2.prealloc_items.len == 8);
+}
+
+test "generic struct" {
+ var a1 = GenNode(i32){
+ .value = 13,
+ .next = null,
+ };
+ var b1 = GenNode(bool){
+ .value = true,
+ .next = null,
+ };
+ expect(a1.value == 13);
+ expect(a1.value == a1.getVal());
+ expect(b1.getVal());
+}
+fn GenNode(comptime T: type) type {
+ return struct {
+ value: T,
+ next: ?*GenNode(T),
+ fn getVal(n: *const GenNode(T)) T {
+ return n.value;
+ }
+ };
+}
+
+test "const decls in struct" {
+ expect(GenericDataThing(3).count_plus_one == 4);
+}
+fn GenericDataThing(comptime count: isize) type {
+ return struct {
+ const count_plus_one = count + 1;
+ };
+}
+
+test "use generic param in generic param" {
+ expect(aGenericFn(i32, 3, 4) == 7);
+}
+fn aGenericFn(comptime T: type, comptime a: T, b: T) T {
+ return a + b;
+}
+
+test "generic fn with implicit cast" {
+ expect(getFirstByte(u8, &[_]u8{13}) == 13);
+ expect(getFirstByte(u16, &[_]u16{
+ 0,
+ 13,
+ }) == 0);
+}
+fn getByte(ptr: ?*const u8) u8 {
+ return ptr.?.*;
+}
+fn getFirstByte(comptime T: type, mem: []const T) u8 {
+ return getByte(@ptrCast(*const u8, &mem[0]));
+}
+
+const foos = [_]fn (anytype) bool{
+ foo1,
+ foo2,
+};
+
+fn foo1(arg: anytype) bool {
+ return arg;
+}
+fn foo2(arg: anytype) bool {
+ return !arg;
+}
+
+test "array of generic fns" {
+ expect(foos[0](true));
+ expect(!foos[1](true));
+}
+
+test "generic fn keeps non-generic parameter types" {
+ const A = 128;
+
+ const S = struct {
+ fn f(comptime T: type, s: []T) void {
+ expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment);
+ }
+ };
+
+ // The compiler monomorphizes `S.f` for `T=u8` on its first use, check that
+ // `x` type not affect `s` parameter type.
+ var x: [16]u8 align(A) = undefined;
+ S.f(u8, &x);
+}
diff --git a/test/behavior/hasdecl.zig b/test/behavior/hasdecl.zig
new file mode 100644
index 0000000000..f3bb9887fe
--- /dev/null
+++ b/test/behavior/hasdecl.zig
@@ -0,0 +1,21 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const Foo = @import("hasdecl/foo.zig");
+
+const Bar = struct {
+ nope: i32,
+
+ const hi = 1;
+ pub var blah = "xxx";
+};
+
+test "@hasDecl" {
+ expect(@hasDecl(Foo, "public_thing"));
+ expect(!@hasDecl(Foo, "private_thing"));
+ expect(!@hasDecl(Foo, "no_thing"));
+
+ expect(@hasDecl(Bar, "hi"));
+ expect(@hasDecl(Bar, "blah"));
+ expect(!@hasDecl(Bar, "nope"));
+}
diff --git a/test/behavior/hasdecl/foo.zig b/test/behavior/hasdecl/foo.zig
new file mode 100644
index 0000000000..c42faaa890
--- /dev/null
+++ b/test/behavior/hasdecl/foo.zig
@@ -0,0 +1,2 @@
+pub const public_thing = 42;
+const private_thing = 666;
diff --git a/test/behavior/hasfield.zig b/test/behavior/hasfield.zig
new file mode 100644
index 0000000000..c179fedd56
--- /dev/null
+++ b/test/behavior/hasfield.zig
@@ -0,0 +1,37 @@
+const expect = @import("std").testing.expect;
+const builtin = @import("builtin");
+
+test "@hasField" {
+ const struc = struct {
+ a: i32,
+ b: []u8,
+
+ pub const nope = 1;
+ };
+ expect(@hasField(struc, "a") == true);
+ expect(@hasField(struc, "b") == true);
+ expect(@hasField(struc, "non-existant") == false);
+ expect(@hasField(struc, "nope") == false);
+
+ const unin = union {
+ a: u64,
+ b: []u16,
+
+ pub const nope = 1;
+ };
+ expect(@hasField(unin, "a") == true);
+ expect(@hasField(unin, "b") == true);
+ expect(@hasField(unin, "non-existant") == false);
+ expect(@hasField(unin, "nope") == false);
+
+ const enm = enum {
+ a,
+ b,
+
+ pub const nope = 1;
+ };
+ expect(@hasField(enm, "a") == true);
+ expect(@hasField(enm, "b") == true);
+ expect(@hasField(enm, "non-existant") == false);
+ expect(@hasField(enm, "nope") == false);
+}
diff --git a/test/behavior/if.zig b/test/behavior/if.zig
new file mode 100644
index 0000000000..de8a29c76d
--- /dev/null
+++ b/test/behavior/if.zig
@@ -0,0 +1,109 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+
+test "if statements" {
+ shouldBeEqual(1, 1);
+ firstEqlThird(2, 1, 2);
+}
+fn shouldBeEqual(a: i32, b: i32) void {
+ if (a != b) {
+ unreachable;
+ } else {
+ return;
+ }
+}
+fn firstEqlThird(a: i32, b: i32, c: i32) void {
+ if (a == b) {
+ unreachable;
+ } else if (b == c) {
+ unreachable;
+ } else if (a == c) {
+ return;
+ } else {
+ unreachable;
+ }
+}
+
+test "else if expression" {
+ expect(elseIfExpressionF(1) == 1);
+}
+fn elseIfExpressionF(c: u8) u8 {
+ if (c == 0) {
+ return 0;
+ } else if (c == 1) {
+ return 1;
+ } else {
+ return @as(u8, 2);
+ }
+}
+
+// #2297
+var global_with_val: anyerror!u32 = 0;
+var global_with_err: anyerror!u32 = error.SomeError;
+
+test "unwrap mutable global var" {
+ if (global_with_val) |v| {
+ expect(v == 0);
+ } else |e| {
+ unreachable;
+ }
+ if (global_with_err) |_| {
+ unreachable;
+ } else |e| {
+ expect(e == error.SomeError);
+ }
+}
+
+test "labeled break inside comptime if inside runtime if" {
+ var answer: i32 = 0;
+ var c = true;
+ if (c) {
+ answer = if (true) blk: {
+ break :blk @as(i32, 42);
+ };
+ }
+ expect(answer == 42);
+}
+
+test "const result loc, runtime if cond, else unreachable" {
+ const Num = enum {
+ One,
+ Two,
+ };
+
+ var t = true;
+ const x = if (t) Num.Two else unreachable;
+ expect(x == .Two);
+}
+
+test "if prongs cast to expected type instead of peer type resolution" {
+ const S = struct {
+ fn doTheTest(f: bool) void {
+ var x: i32 = 0;
+ x = if (f) 1 else 2;
+ expect(x == 2);
+
+ var b = true;
+ const y: i32 = if (b) 1 else 2;
+ expect(y == 1);
+ }
+ };
+ S.doTheTest(false);
+ comptime 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;
+ expectEqual(@as(i32, 10), value);
+ } else unreachable;
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/import.zig b/test/behavior/import.zig
new file mode 100644
index 0000000000..30655554bf
--- /dev/null
+++ b/test/behavior/import.zig
@@ -0,0 +1,22 @@
+const expect = @import("std").testing.expect;
+const expectEqual = @import("std").testing.expectEqual;
+const a_namespace = @import("import/a_namespace.zig");
+
+test "call fn via namespace lookup" {
+ expectEqual(@as(i32, 1234), a_namespace.foo());
+}
+
+test "importing the same thing gives the same import" {
+ expect(@import("std") == @import("std"));
+}
+
+test "import in non-toplevel scope" {
+ const S = struct {
+ usingnamespace @import("import/a_namespace.zig");
+ };
+ expectEqual(@as(i32, 1234), S.foo());
+}
+
+test "import empty file" {
+ const empty = @import("import/empty.zig");
+}
diff --git a/test/behavior/import/a_namespace.zig b/test/behavior/import/a_namespace.zig
new file mode 100644
index 0000000000..042f1867a5
--- /dev/null
+++ b/test/behavior/import/a_namespace.zig
@@ -0,0 +1,3 @@
+pub fn foo() i32 {
+ return 1234;
+}
diff --git a/test/behavior/import/empty.zig b/test/behavior/import/empty.zig
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/behavior/import/empty.zig
diff --git a/test/behavior/incomplete_struct_param_tld.zig b/test/behavior/incomplete_struct_param_tld.zig
new file mode 100644
index 0000000000..77a3dfd221
--- /dev/null
+++ b/test/behavior/incomplete_struct_param_tld.zig
@@ -0,0 +1,30 @@
+const expect = @import("std").testing.expect;
+
+const A = struct {
+ b: B,
+};
+
+const B = struct {
+ c: C,
+};
+
+const C = struct {
+ x: i32,
+
+ fn d(c: *const C) i32 {
+ return c.x;
+ }
+};
+
+fn foo(a: A) i32 {
+ return a.b.c.d();
+}
+
+test "incomplete struct param top level declaration" {
+ const a = A{
+ .b = B{
+ .c = C{ .x = 13 },
+ },
+ };
+ expect(foo(a) == 13);
+}
diff --git a/test/behavior/inttoptr.zig b/test/behavior/inttoptr.zig
new file mode 100644
index 0000000000..b1780f93d6
--- /dev/null
+++ b/test/behavior/inttoptr.zig
@@ -0,0 +1,26 @@
+const builtin = @import("builtin");
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "casting random address to function pointer" {
+ randomAddressToFunction();
+ comptime randomAddressToFunction();
+}
+
+fn randomAddressToFunction() void {
+ var addr: usize = 0xdeadbeef;
+ var ptr = @intToPtr(fn () void, addr);
+}
+
+test "mutate through ptr initialized with constant intToPtr value" {
+ forceCompilerAnalyzeBranchHardCodedPtrDereference(false);
+}
+
+fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void {
+ const hardCodedP = @intToPtr(*volatile u8, 0xdeadbeef);
+ if (x) {
+ hardCodedP.* = hardCodedP.* | 10;
+ } else {
+ return;
+ }
+}
diff --git a/test/behavior/ir_block_deps.zig b/test/behavior/ir_block_deps.zig
new file mode 100644
index 0000000000..821079df79
--- /dev/null
+++ b/test/behavior/ir_block_deps.zig
@@ -0,0 +1,21 @@
+const expect = @import("std").testing.expect;
+
+fn foo(id: u64) !i32 {
+ return switch (id) {
+ 1 => getErrInt(),
+ 2 => {
+ const size = try getErrInt();
+ return try getErrInt();
+ },
+ else => error.ItBroke,
+ };
+}
+
+fn getErrInt() anyerror!i32 {
+ return 0;
+}
+
+test "ir block deps" {
+ expect((foo(1) catch unreachable) == 0);
+ expect((foo(2) catch unreachable) == 0);
+}
diff --git a/test/behavior/math.zig b/test/behavior/math.zig
new file mode 100644
index 0000000000..32f4842702
--- /dev/null
+++ b/test/behavior/math.zig
@@ -0,0 +1,872 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectEqualSlices = std.testing.expectEqualSlices;
+const maxInt = std.math.maxInt;
+const minInt = std.math.minInt;
+const mem = std.mem;
+
+test "division" {
+ testDivision();
+ comptime testDivision();
+}
+fn testDivision() void {
+ expect(div(u32, 13, 3) == 4);
+ expect(div(f16, 1.0, 2.0) == 0.5);
+ expect(div(f32, 1.0, 2.0) == 0.5);
+
+ expect(divExact(u32, 55, 11) == 5);
+ expect(divExact(i32, -55, 11) == -5);
+ expect(divExact(f16, 55.0, 11.0) == 5.0);
+ expect(divExact(f16, -55.0, 11.0) == -5.0);
+ expect(divExact(f32, 55.0, 11.0) == 5.0);
+ expect(divExact(f32, -55.0, 11.0) == -5.0);
+
+ expect(divFloor(i32, 5, 3) == 1);
+ expect(divFloor(i32, -5, 3) == -2);
+ expect(divFloor(f16, 5.0, 3.0) == 1.0);
+ expect(divFloor(f16, -5.0, 3.0) == -2.0);
+ expect(divFloor(f32, 5.0, 3.0) == 1.0);
+ expect(divFloor(f32, -5.0, 3.0) == -2.0);
+ expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
+ expect(divFloor(i32, 0, -0x80000000) == 0);
+ expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
+ expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
+ expect(divFloor(i32, 10, 12) == 0);
+ expect(divFloor(i32, -14, 12) == -2);
+ expect(divFloor(i32, -2, 12) == -1);
+
+ expect(divTrunc(i32, 5, 3) == 1);
+ expect(divTrunc(i32, -5, 3) == -1);
+ expect(divTrunc(f16, 5.0, 3.0) == 1.0);
+ expect(divTrunc(f16, -5.0, 3.0) == -1.0);
+ expect(divTrunc(f32, 5.0, 3.0) == 1.0);
+ expect(divTrunc(f32, -5.0, 3.0) == -1.0);
+ expect(divTrunc(f64, 5.0, 3.0) == 1.0);
+ expect(divTrunc(f64, -5.0, 3.0) == -1.0);
+ expect(divTrunc(i32, 10, 12) == 0);
+ expect(divTrunc(i32, -14, 12) == -1);
+ expect(divTrunc(i32, -2, 12) == 0);
+
+ expect(mod(i32, 10, 12) == 10);
+ expect(mod(i32, -14, 12) == 10);
+ expect(mod(i32, -2, 12) == 10);
+
+ comptime {
+ expect(
+ 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
+ );
+ expect(
+ @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
+ );
+ expect(
+ 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
+ );
+ expect(
+ @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
+ );
+ expect(
+ @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
+ );
+ expect(
+ @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
+ );
+ expect(
+ 4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
+ );
+ }
+}
+fn div(comptime T: type, a: T, b: T) T {
+ return a / b;
+}
+fn divExact(comptime T: type, a: T, b: T) T {
+ return @divExact(a, b);
+}
+fn divFloor(comptime T: type, a: T, b: T) T {
+ return @divFloor(a, b);
+}
+fn divTrunc(comptime T: type, a: T, b: T) T {
+ return @divTrunc(a, b);
+}
+fn mod(comptime T: type, a: T, b: T) T {
+ return @mod(a, b);
+}
+
+test "@addWithOverflow" {
+ var result: u8 = undefined;
+ expect(@addWithOverflow(u8, 250, 100, &result));
+ expect(!@addWithOverflow(u8, 100, 150, &result));
+ expect(result == 250);
+}
+
+// TODO test mulWithOverflow
+// TODO test subWithOverflow
+
+test "@shlWithOverflow" {
+ var result: u16 = undefined;
+ expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
+ expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
+ expect(result == 0b1011111111111100);
+}
+
+test "@*WithOverflow with u0 values" {
+ var result: u0 = undefined;
+ expect(!@addWithOverflow(u0, 0, 0, &result));
+ expect(!@subWithOverflow(u0, 0, 0, &result));
+ expect(!@mulWithOverflow(u0, 0, 0, &result));
+ expect(!@shlWithOverflow(u0, 0, 0, &result));
+}
+
+test "@clz" {
+ testClz();
+ comptime testClz();
+}
+
+fn testClz() void {
+ expect(clz(u8, 0b10001010) == 0);
+ expect(clz(u8, 0b00001010) == 4);
+ expect(clz(u8, 0b00011010) == 3);
+ expect(clz(u8, 0b00000000) == 8);
+ expect(clz(u128, 0xffffffffffffffff) == 64);
+ expect(clz(u128, 0x10000000000000000) == 63);
+}
+
+fn clz(comptime T: type, x: T) usize {
+ return @clz(T, x);
+}
+
+test "@ctz" {
+ testCtz();
+ comptime testCtz();
+}
+
+fn testCtz() void {
+ expect(ctz(u8, 0b10100000) == 5);
+ expect(ctz(u8, 0b10001010) == 1);
+ expect(ctz(u8, 0b00000000) == 8);
+ expect(ctz(u16, 0b00000000) == 16);
+}
+
+fn ctz(comptime T: type, x: T) usize {
+ return @ctz(T, x);
+}
+
+test "assignment operators" {
+ var i: u32 = 0;
+ i += 5;
+ expect(i == 5);
+ i -= 2;
+ expect(i == 3);
+ i *= 20;
+ expect(i == 60);
+ i /= 3;
+ expect(i == 20);
+ i %= 11;
+ expect(i == 9);
+ i <<= 1;
+ expect(i == 18);
+ i >>= 2;
+ expect(i == 4);
+ i = 6;
+ i &= 5;
+ expect(i == 4);
+ i ^= 6;
+ expect(i == 2);
+ i = 6;
+ i |= 3;
+ expect(i == 7);
+}
+
+test "three expr in a row" {
+ testThreeExprInARow(false, true);
+ comptime testThreeExprInARow(false, true);
+}
+fn testThreeExprInARow(f: bool, t: bool) void {
+ assertFalse(f or f or f);
+ assertFalse(t and t and f);
+ assertFalse(1 | 2 | 4 != 7);
+ assertFalse(3 ^ 6 ^ 8 != 13);
+ assertFalse(7 & 14 & 28 != 4);
+ assertFalse(9 << 1 << 2 != 9 << 3);
+ assertFalse(90 >> 1 >> 2 != 90 >> 3);
+ assertFalse(100 - 1 + 1000 != 1099);
+ assertFalse(5 * 4 / 2 % 3 != 1);
+ assertFalse(@as(i32, @as(i32, 5)) != 5);
+ assertFalse(!!false);
+ assertFalse(@as(i32, 7) != --(@as(i32, 7)));
+}
+fn assertFalse(b: bool) void {
+ expect(!b);
+}
+
+test "const number literal" {
+ const one = 1;
+ const eleven = ten + one;
+
+ expect(eleven == 11);
+}
+const ten = 10;
+
+test "unsigned wrapping" {
+ testUnsignedWrappingEval(maxInt(u32));
+ comptime testUnsignedWrappingEval(maxInt(u32));
+}
+fn testUnsignedWrappingEval(x: u32) void {
+ const zero = x +% 1;
+ expect(zero == 0);
+ const orig = zero -% 1;
+ expect(orig == maxInt(u32));
+}
+
+test "signed wrapping" {
+ testSignedWrappingEval(maxInt(i32));
+ comptime testSignedWrappingEval(maxInt(i32));
+}
+fn testSignedWrappingEval(x: i32) void {
+ const min_val = x +% 1;
+ expect(min_val == minInt(i32));
+ const max_val = min_val -% 1;
+ expect(max_val == maxInt(i32));
+}
+
+test "signed negation wrapping" {
+ testSignedNegationWrappingEval(minInt(i16));
+ comptime testSignedNegationWrappingEval(minInt(i16));
+}
+fn testSignedNegationWrappingEval(x: i16) void {
+ expect(x == -32768);
+ const neg = -%x;
+ expect(neg == -32768);
+}
+
+test "unsigned negation wrapping" {
+ testUnsignedNegationWrappingEval(1);
+ comptime testUnsignedNegationWrappingEval(1);
+}
+fn testUnsignedNegationWrappingEval(x: u16) void {
+ expect(x == 1);
+ const neg = -%x;
+ expect(neg == maxInt(u16));
+}
+
+test "unsigned 64-bit division" {
+ test_u64_div();
+ comptime test_u64_div();
+}
+fn test_u64_div() void {
+ const result = divWithResult(1152921504606846976, 34359738365);
+ expect(result.quotient == 33554432);
+ expect(result.remainder == 100663296);
+}
+fn divWithResult(a: u64, b: u64) DivResult {
+ return DivResult{
+ .quotient = a / b,
+ .remainder = a % b,
+ };
+}
+const DivResult = struct {
+ quotient: u64,
+ remainder: u64,
+};
+
+test "binary not" {
+ expect(comptime x: {
+ break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;
+ });
+ expect(comptime x: {
+ break :x ~@as(u64, 2147483647) == 18446744071562067968;
+ });
+ testBinaryNot(0b1010101010101010);
+}
+
+fn testBinaryNot(x: u16) void {
+ expect(~x == 0b0101010101010101);
+}
+
+test "small int addition" {
+ var x: u2 = 0;
+ expect(x == 0);
+
+ x += 1;
+ expect(x == 1);
+
+ x += 1;
+ expect(x == 2);
+
+ x += 1;
+ expect(x == 3);
+
+ var result: @TypeOf(x) = 3;
+ expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
+
+ expect(result == 0);
+}
+
+test "float equality" {
+ const x: f64 = 0.012;
+ const y: f64 = x + 1.0;
+
+ testFloatEqualityImpl(x, y);
+ comptime testFloatEqualityImpl(x, y);
+}
+
+fn testFloatEqualityImpl(x: f64, y: f64) void {
+ const y2 = x + 1.0;
+ expect(y == y2);
+}
+
+test "allow signed integer division/remainder when values are comptime known and positive or exact" {
+ expect(5 / 3 == 1);
+ expect(-5 / -3 == 1);
+ expect(-6 / 3 == -2);
+
+ expect(5 % 3 == 2);
+ expect(-6 % 3 == 0);
+}
+
+test "hex float literal parsing" {
+ comptime expect(0x1.0 == 1.0);
+}
+
+test "quad hex float literal parsing in range" {
+ const a = 0x1.af23456789bbaaab347645365cdep+5;
+ const b = 0x1.dedafcff354b6ae9758763545432p-9;
+ const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;
+ const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
+}
+
+test "quad hex float literal parsing accurate" {
+ const a: f128 = 0x1.1111222233334444555566667777p+0;
+
+ // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing.
+ const expected: u128 = 0x3fff1111222233334444555566667777;
+ expect(@bitCast(u128, a) == expected);
+
+ // non-normalized
+ const b: f128 = 0x11.111222233334444555566667777p-4;
+ expect(@bitCast(u128, b) == expected);
+
+ const S = struct {
+ fn doTheTest() void {
+ {
+ var f: f128 = 0x1.2eab345678439abcdefea56782346p+5;
+ expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234);
+ }
+ {
+ var f: f128 = 0x1.edcb34a235253948765432134674fp-1;
+ expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674);
+ }
+ {
+ var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50;
+ expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50);
+ }
+ {
+ var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9;
+ expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568);
+ }
+ const exp2ft = [_]f64{
+ 0x1.6a09e667f3bcdp-1,
+ 0x1.7a11473eb0187p-1,
+ 0x1.8ace5422aa0dbp-1,
+ 0x1.9c49182a3f090p-1,
+ 0x1.ae89f995ad3adp-1,
+ 0x1.c199bdd85529cp-1,
+ 0x1.d5818dcfba487p-1,
+ 0x1.ea4afa2a490dap-1,
+ 0x1.0000000000000p+0,
+ 0x1.0b5586cf9890fp+0,
+ 0x1.172b83c7d517bp+0,
+ 0x1.2387a6e756238p+0,
+ 0x1.306fe0a31b715p+0,
+ 0x1.3dea64c123422p+0,
+ 0x1.4bfdad5362a27p+0,
+ 0x1.5ab07dd485429p+0,
+ 0x1.8p23,
+ 0x1.62e430p-1,
+ 0x1.ebfbe0p-3,
+ 0x1.c6b348p-5,
+ 0x1.3b2c9cp-7,
+ 0x1.0p127,
+ -0x1.0p-149,
+ };
+
+ const answers = [_]u64{
+ 0x3fe6a09e667f3bcd,
+ 0x3fe7a11473eb0187,
+ 0x3fe8ace5422aa0db,
+ 0x3fe9c49182a3f090,
+ 0x3feae89f995ad3ad,
+ 0x3fec199bdd85529c,
+ 0x3fed5818dcfba487,
+ 0x3feea4afa2a490da,
+ 0x3ff0000000000000,
+ 0x3ff0b5586cf9890f,
+ 0x3ff172b83c7d517b,
+ 0x3ff2387a6e756238,
+ 0x3ff306fe0a31b715,
+ 0x3ff3dea64c123422,
+ 0x3ff4bfdad5362a27,
+ 0x3ff5ab07dd485429,
+ 0x4168000000000000,
+ 0x3fe62e4300000000,
+ 0x3fcebfbe00000000,
+ 0x3fac6b3480000000,
+ 0x3f83b2c9c0000000,
+ 0x47e0000000000000,
+ 0xb6a0000000000000,
+ };
+
+ for (exp2ft) |x, i| {
+ expect(@bitCast(u64, x) == answers[i]);
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "underscore separator parsing" {
+ expect(0_0_0_0 == 0);
+ expect(1_234_567 == 1234567);
+ expect(001_234_567 == 1234567);
+ expect(0_0_1_2_3_4_5_6_7 == 1234567);
+
+ expect(0b0_0_0_0 == 0);
+ expect(0b1010_1010 == 0b10101010);
+ expect(0b0000_1010_1010 == 0b10101010);
+ expect(0b1_0_1_0_1_0_1_0 == 0b10101010);
+
+ expect(0o0_0_0_0 == 0);
+ expect(0o1010_1010 == 0o10101010);
+ expect(0o0000_1010_1010 == 0o10101010);
+ expect(0o1_0_1_0_1_0_1_0 == 0o10101010);
+
+ expect(0x0_0_0_0 == 0);
+ expect(0x1010_1010 == 0x10101010);
+ expect(0x0000_1010_1010 == 0x10101010);
+ expect(0x1_0_1_0_1_0_1_0 == 0x10101010);
+
+ expect(123_456.789_000e1_0 == 123456.789000e10);
+ expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10);
+
+ expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10);
+ expect(0x1_2_3_4_5_6_7_8.9_A_B_C_D_E_F_0p-0_0_0_1_0 == 0x12345678.9ABCDEF0p-10);
+}
+
+test "hex float literal within range" {
+ const a = 0x1.0p16383;
+ const b = 0x0.1p16387;
+ const c = 0x1.0p-16382;
+}
+
+test "truncating shift left" {
+ testShlTrunc(maxInt(u16));
+ comptime testShlTrunc(maxInt(u16));
+}
+fn testShlTrunc(x: u16) void {
+ const shifted = x << 1;
+ expect(shifted == 65534);
+}
+
+test "truncating shift right" {
+ testShrTrunc(maxInt(u16));
+ comptime testShrTrunc(maxInt(u16));
+}
+fn testShrTrunc(x: u16) void {
+ const shifted = x >> 1;
+ expect(shifted == 32767);
+}
+
+test "exact shift left" {
+ testShlExact(0b00110101);
+ comptime testShlExact(0b00110101);
+}
+fn testShlExact(x: u8) void {
+ const shifted = @shlExact(x, 2);
+ expect(shifted == 0b11010100);
+}
+
+test "exact shift right" {
+ testShrExact(0b10110100);
+ comptime testShrExact(0b10110100);
+}
+fn testShrExact(x: u8) void {
+ const shifted = @shrExact(x, 2);
+ expect(shifted == 0b00101101);
+}
+
+test "shift left/right on u0 operand" {
+ const S = struct {
+ fn doTheTest() void {
+ var x: u0 = 0;
+ var y: u0 = 0;
+ expectEqual(@as(u0, 0), x << 0);
+ expectEqual(@as(u0, 0), x >> 0);
+ expectEqual(@as(u0, 0), x << y);
+ expectEqual(@as(u0, 0), x >> y);
+ expectEqual(@as(u0, 0), @shlExact(x, 0));
+ expectEqual(@as(u0, 0), @shrExact(x, 0));
+ expectEqual(@as(u0, 0), @shlExact(x, y));
+ expectEqual(@as(u0, 0), @shrExact(x, y));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "comptime_int addition" {
+ comptime {
+ expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950);
+ expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380);
+ }
+}
+
+test "comptime_int multiplication" {
+ comptime {
+ expect(
+ 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,
+ );
+ expect(
+ 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,
+ );
+ }
+}
+
+test "comptime_int shifting" {
+ comptime {
+ expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000);
+ }
+}
+
+test "comptime_int multi-limb shift and mask" {
+ comptime {
+ var a = 0xefffffffa0000001eeeeeeefaaaaaaab;
+
+ expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab);
+ a >>= 32;
+ expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef);
+ a >>= 32;
+ expect(@as(u32, a & 0xffffffff) == 0xa0000001);
+ a >>= 32;
+ expect(@as(u32, a & 0xffffffff) == 0xefffffff);
+ a >>= 32;
+
+ expect(a == 0);
+ }
+}
+
+test "comptime_int multi-limb partial shift right" {
+ comptime {
+ var a = 0x1ffffffffeeeeeeee;
+ a >>= 16;
+ expect(a == 0x1ffffffffeeee);
+ }
+}
+
+test "xor" {
+ test_xor();
+ comptime test_xor();
+}
+
+fn test_xor() void {
+ expect(0xFF ^ 0x00 == 0xFF);
+ expect(0xF0 ^ 0x0F == 0xFF);
+ expect(0xFF ^ 0xF0 == 0x0F);
+ expect(0xFF ^ 0x0F == 0xF0);
+ expect(0xFF ^ 0xFF == 0x00);
+}
+
+test "comptime_int xor" {
+ comptime {
+ expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+ expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+ expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF);
+ expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000);
+ expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000);
+ expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+ expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF);
+ expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000);
+ }
+}
+
+test "f128" {
+ test_f128();
+ comptime test_f128();
+}
+
+fn make_f128(x: f128) f128 {
+ return x;
+}
+
+fn test_f128() void {
+ expect(@sizeOf(f128) == 16);
+ expect(make_f128(1.0) == 1.0);
+ expect(make_f128(1.0) != 1.1);
+ expect(make_f128(1.0) > 0.9);
+ expect(make_f128(1.0) >= 0.9);
+ expect(make_f128(1.0) >= 1.0);
+ should_not_be_zero(1.0);
+}
+
+fn should_not_be_zero(x: f128) void {
+ expect(x != 0.0);
+}
+
+test "comptime float rem int" {
+ comptime {
+ var x = @as(f32, 1) % 2;
+ expect(x == 1.0);
+ }
+}
+
+test "remainder division" {
+ comptime remdiv(f16);
+ comptime remdiv(f32);
+ comptime remdiv(f64);
+ comptime remdiv(f128);
+ remdiv(f16);
+ remdiv(f64);
+ remdiv(f128);
+}
+
+fn remdiv(comptime T: type) void {
+ expect(@as(T, 1) == @as(T, 1) % @as(T, 2));
+ expect(@as(T, 1) == @as(T, 7) % @as(T, 3));
+}
+
+test "@sqrt" {
+ testSqrt(f64, 12.0);
+ comptime testSqrt(f64, 12.0);
+ testSqrt(f32, 13.0);
+ comptime testSqrt(f32, 13.0);
+ testSqrt(f16, 13.0);
+ comptime testSqrt(f16, 13.0);
+
+ const x = 14.0;
+ const y = x * x;
+ const z = @sqrt(y);
+ comptime expect(z == x);
+}
+
+fn testSqrt(comptime T: type, x: T) void {
+ expect(@sqrt(x * x) == x);
+}
+
+test "@fabs" {
+ testFabs(f128, 12.0);
+ comptime testFabs(f128, 12.0);
+ testFabs(f64, 12.0);
+ comptime testFabs(f64, 12.0);
+ testFabs(f32, 12.0);
+ comptime testFabs(f32, 12.0);
+ testFabs(f16, 12.0);
+ comptime testFabs(f16, 12.0);
+
+ const x = 14.0;
+ const y = -x;
+ const z = @fabs(y);
+ comptime expectEqual(x, z);
+}
+
+fn testFabs(comptime T: type, x: T) void {
+ const y = -x;
+ const z = @fabs(y);
+ expectEqual(x, z);
+}
+
+test "@floor" {
+ // FIXME: Generates a floorl function call
+ // testFloor(f128, 12.0);
+ comptime testFloor(f128, 12.0);
+ testFloor(f64, 12.0);
+ comptime testFloor(f64, 12.0);
+ testFloor(f32, 12.0);
+ comptime testFloor(f32, 12.0);
+ testFloor(f16, 12.0);
+ comptime testFloor(f16, 12.0);
+
+ const x = 14.0;
+ const y = x + 0.7;
+ const z = @floor(y);
+ comptime expectEqual(x, z);
+}
+
+fn testFloor(comptime T: type, x: T) void {
+ const y = x + 0.6;
+ const z = @floor(y);
+ expectEqual(x, z);
+}
+
+test "@ceil" {
+ // FIXME: Generates a ceill function call
+ //testCeil(f128, 12.0);
+ comptime testCeil(f128, 12.0);
+ testCeil(f64, 12.0);
+ comptime testCeil(f64, 12.0);
+ testCeil(f32, 12.0);
+ comptime testCeil(f32, 12.0);
+ testCeil(f16, 12.0);
+ comptime testCeil(f16, 12.0);
+
+ const x = 14.0;
+ const y = x - 0.7;
+ const z = @ceil(y);
+ comptime expectEqual(x, z);
+}
+
+fn testCeil(comptime T: type, x: T) void {
+ const y = x - 0.8;
+ const z = @ceil(y);
+ expectEqual(x, z);
+}
+
+test "@trunc" {
+ // FIXME: Generates a truncl function call
+ //testTrunc(f128, 12.0);
+ comptime testTrunc(f128, 12.0);
+ testTrunc(f64, 12.0);
+ comptime testTrunc(f64, 12.0);
+ testTrunc(f32, 12.0);
+ comptime testTrunc(f32, 12.0);
+ testTrunc(f16, 12.0);
+ comptime testTrunc(f16, 12.0);
+
+ const x = 14.0;
+ const y = x + 0.7;
+ const z = @trunc(y);
+ comptime expectEqual(x, z);
+}
+
+fn testTrunc(comptime T: type, x: T) void {
+ {
+ const y = x + 0.8;
+ const z = @trunc(y);
+ expectEqual(x, z);
+ }
+
+ {
+ const y = -x - 0.8;
+ const z = @trunc(y);
+ expectEqual(-x, z);
+ }
+}
+
+test "@round" {
+ // FIXME: Generates a roundl function call
+ //testRound(f128, 12.0);
+ comptime testRound(f128, 12.0);
+ testRound(f64, 12.0);
+ comptime testRound(f64, 12.0);
+ testRound(f32, 12.0);
+ comptime testRound(f32, 12.0);
+ testRound(f16, 12.0);
+ comptime testRound(f16, 12.0);
+
+ const x = 14.0;
+ const y = x + 0.4;
+ const z = @round(y);
+ comptime expectEqual(x, z);
+}
+
+fn testRound(comptime T: type, x: T) void {
+ const y = x - 0.5;
+ const z = @round(y);
+ expectEqual(x, z);
+}
+
+test "comptime_int param and return" {
+ const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
+ expect(a == 137114567242441932203689521744947848950);
+
+ const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768);
+ expect(b == 985095453608931032642182098849559179469148836107390954364380);
+}
+
+fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
+ return a + b;
+}
+
+test "vector integer addition" {
+ const S = struct {
+ fn doTheTest() void {
+ var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
+ var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
+ var result = a + b;
+ var result_array: [4]i32 = result;
+ const expected = [_]i32{ 6, 8, 10, 12 };
+ expectEqualSlices(i32, &expected, &result_array);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "NaN comparison" {
+ testNanEqNan(f16);
+ testNanEqNan(f32);
+ testNanEqNan(f64);
+ testNanEqNan(f128);
+ comptime testNanEqNan(f16);
+ comptime testNanEqNan(f32);
+ comptime testNanEqNan(f64);
+ comptime testNanEqNan(f128);
+}
+
+fn testNanEqNan(comptime F: type) void {
+ var nan1 = std.math.nan(F);
+ var nan2 = std.math.nan(F);
+ expect(nan1 != nan2);
+ expect(!(nan1 == nan2));
+ expect(!(nan1 > nan2));
+ expect(!(nan1 >= nan2));
+ expect(!(nan1 < nan2));
+ expect(!(nan1 <= nan2));
+}
+
+test "128-bit multiplication" {
+ var a: i128 = 3;
+ var b: i128 = 2;
+ var c = a * b;
+ expect(c == 6);
+}
+
+test "vector comparison" {
+ const S = struct {
+ fn doTheTest() void {
+ var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 };
+ var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 };
+ expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false }));
+ expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false }));
+ expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false }));
+ expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true }));
+ expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true }));
+ expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "compare undefined literal with comptime_int" {
+ var x = undefined == 1;
+ // x is now undefined with type bool
+ x = true;
+ expect(x);
+}
+
+test "signed zeros are represented properly" {
+ const S = struct {
+ fn doTheTest() void {
+ inline for ([_]type{ f16, f32, f64, f128 }) |T| {
+ const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
+ var as_fp_val = -@as(T, 0.0);
+ var as_uint_val = @bitCast(ST, as_fp_val);
+ // Ensure the sign bit is set.
+ expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1);
+ }
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/merge_error_sets.zig b/test/behavior/merge_error_sets.zig
new file mode 100644
index 0000000000..147b580232
--- /dev/null
+++ b/test/behavior/merge_error_sets.zig
@@ -0,0 +1,21 @@
+const A = error{
+ FileNotFound,
+ NotDir,
+};
+const B = error{OutOfMemory};
+
+const C = A || B;
+
+fn foo() C!void {
+ return error.NotDir;
+}
+
+test "merge error sets" {
+ if (foo()) {
+ @panic("unexpected");
+ } else |err| switch (err) {
+ error.OutOfMemory => @panic("unexpected"),
+ error.FileNotFound => @panic("unexpected"),
+ error.NotDir => {},
+ }
+}
diff --git a/test/behavior/misc.zig b/test/behavior/misc.zig
new file mode 100644
index 0000000000..850622c5ba
--- /dev/null
+++ b/test/behavior/misc.zig
@@ -0,0 +1,761 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqualSlices = std.testing.expectEqualSlices;
+const mem = std.mem;
+const builtin = @import("builtin");
+
+// normal comment
+
+/// this is a documentation comment
+/// doc comment line 2
+fn emptyFunctionWithComments() void {}
+
+test "empty function with comments" {
+ emptyFunctionWithComments();
+}
+
+comptime {
+ @export(disabledExternFn, .{ .name = "disabledExternFn", .linkage = .Internal });
+}
+
+fn disabledExternFn() callconv(.C) void {}
+
+test "call disabled extern fn" {
+ disabledExternFn();
+}
+
+test "short circuit" {
+ testShortCircuit(false, true);
+ comptime testShortCircuit(false, true);
+}
+
+fn testShortCircuit(f: bool, t: bool) void {
+ var hit_1 = f;
+ var hit_2 = f;
+ var hit_3 = f;
+ var hit_4 = f;
+
+ if (t or x: {
+ expect(f);
+ break :x f;
+ }) {
+ hit_1 = t;
+ }
+ if (f or x: {
+ hit_2 = t;
+ break :x f;
+ }) {
+ expect(f);
+ }
+
+ if (t and x: {
+ hit_3 = t;
+ break :x f;
+ }) {
+ expect(f);
+ }
+ if (f and x: {
+ expect(f);
+ break :x f;
+ }) {
+ expect(f);
+ } else {
+ hit_4 = t;
+ }
+ expect(hit_1);
+ expect(hit_2);
+ expect(hit_3);
+ expect(hit_4);
+}
+
+test "truncate" {
+ expect(testTruncate(0x10fd) == 0xfd);
+}
+fn testTruncate(x: u32) u8 {
+ return @truncate(u8, x);
+}
+
+fn first4KeysOfHomeRow() []const u8 {
+ return "aoeu";
+}
+
+test "return string from function" {
+ expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
+}
+
+const g1: i32 = 1233 + 1;
+var g2: i32 = 0;
+
+test "global variables" {
+ expect(g2 == 0);
+ g2 = g1;
+ expect(g2 == 1234);
+}
+
+test "memcpy and memset intrinsics" {
+ var foo: [20]u8 = undefined;
+ var bar: [20]u8 = undefined;
+
+ @memset(&foo, 'A', foo.len);
+ @memcpy(&bar, &foo, bar.len);
+
+ if (bar[11] != 'A') unreachable;
+}
+
+test "builtin static eval" {
+ const x: i32 = comptime x: {
+ break :x 1 + 2 + 3;
+ };
+ expect(x == comptime 6);
+}
+
+test "slicing" {
+ var array: [20]i32 = undefined;
+
+ array[5] = 1234;
+
+ var slice = array[5..10];
+
+ if (slice.len != 5) unreachable;
+
+ const ptr = &slice[0];
+ if (ptr.* != 1234) unreachable;
+
+ var slice_rest = array[10..];
+ if (slice_rest.len != 10) unreachable;
+}
+
+test "constant equal function pointers" {
+ const alias = emptyFn;
+ expect(comptime x: {
+ break :x emptyFn == alias;
+ });
+}
+
+fn emptyFn() void {}
+
+test "hex escape" {
+ expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
+}
+
+test "string concatenation" {
+ expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
+}
+
+test "array mult operator" {
+ expect(mem.eql(u8, "ab" ** 5, "ababababab"));
+}
+
+test "string escapes" {
+ expect(mem.eql(u8, "\"", "\x22"));
+ expect(mem.eql(u8, "\'", "\x27"));
+ expect(mem.eql(u8, "\n", "\x0a"));
+ expect(mem.eql(u8, "\r", "\x0d"));
+ expect(mem.eql(u8, "\t", "\x09"));
+ expect(mem.eql(u8, "\\", "\x5c"));
+ expect(mem.eql(u8, "\u{1234}\u{069}\u{1}", "\xe1\x88\xb4\x69\x01"));
+}
+
+test "multiline string" {
+ const s1 =
+ \\one
+ \\two)
+ \\three
+ ;
+ const s2 = "one\ntwo)\nthree";
+ expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments at start" {
+ const s1 =
+ //\\one
+ \\two)
+ \\three
+ ;
+ const s2 = "two)\nthree";
+ expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments at end" {
+ const s1 =
+ \\one
+ \\two)
+ //\\three
+ ;
+ const s2 = "one\ntwo)";
+ expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments in middle" {
+ const s1 =
+ \\one
+ //\\two)
+ \\three
+ ;
+ const s2 = "one\nthree";
+ expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments at multiple places" {
+ const s1 =
+ \\one
+ //\\two
+ \\three
+ //\\four
+ \\five
+ ;
+ const s2 = "one\nthree\nfive";
+ expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline C string" {
+ const s1 =
+ \\one
+ \\two)
+ \\three
+ ;
+ const s2 = "one\ntwo)\nthree";
+ expect(std.cstr.cmp(s1, s2) == 0);
+}
+
+test "type equality" {
+ expect(*const u8 != *u8);
+}
+
+const global_a: i32 = 1234;
+const global_b: *const i32 = &global_a;
+const global_c: *const f32 = @ptrCast(*const f32, global_b);
+test "compile time global reinterpret" {
+ const d = @ptrCast(*const i32, global_c);
+ expect(d.* == 1234);
+}
+
+test "explicit cast maybe pointers" {
+ const a: ?*i32 = undefined;
+ const b: ?*f32 = @ptrCast(?*f32, a);
+}
+
+test "generic malloc free" {
+ const a = memAlloc(u8, 10) catch unreachable;
+ memFree(u8, a);
+}
+var some_mem: [100]u8 = undefined;
+fn memAlloc(comptime T: type, n: usize) anyerror![]T {
+ return @ptrCast([*]T, &some_mem[0])[0..n];
+}
+fn memFree(comptime T: type, memory: []T) void {}
+
+test "cast undefined" {
+ const array: [100]u8 = undefined;
+ const slice = @as([]const u8, &array);
+ testCastUndefined(slice);
+}
+fn testCastUndefined(x: []const u8) void {}
+
+test "cast small unsigned to larger signed" {
+ expect(castSmallUnsignedToLargerSigned1(200) == @as(i16, 200));
+ expect(castSmallUnsignedToLargerSigned2(9999) == @as(i64, 9999));
+}
+fn castSmallUnsignedToLargerSigned1(x: u8) i16 {
+ return x;
+}
+fn castSmallUnsignedToLargerSigned2(x: u16) i64 {
+ return x;
+}
+
+test "implicit cast after unreachable" {
+ expect(outer() == 1234);
+}
+fn inner() i32 {
+ return 1234;
+}
+fn outer() i64 {
+ return inner();
+}
+
+test "pointer dereferencing" {
+ var x = @as(i32, 3);
+ const y = &x;
+
+ y.* += 1;
+
+ expect(x == 4);
+ expect(y.* == 4);
+}
+
+test "call result of if else expression" {
+ expect(mem.eql(u8, f2(true), "a"));
+ expect(mem.eql(u8, f2(false), "b"));
+}
+fn f2(x: bool) []const u8 {
+ return (if (x) fA else fB)();
+}
+fn fA() []const u8 {
+ return "a";
+}
+fn fB() []const u8 {
+ return "b";
+}
+
+test "const expression eval handling of variables" {
+ var x = true;
+ while (x) {
+ x = false;
+ }
+}
+
+test "constant enum initialization with differing sizes" {
+ test3_1(test3_foo);
+ test3_2(test3_bar);
+}
+const Test3Foo = union(enum) {
+ One: void,
+ Two: f32,
+ Three: Test3Point,
+};
+const Test3Point = struct {
+ x: i32,
+ y: i32,
+};
+const test3_foo = Test3Foo{
+ .Three = Test3Point{
+ .x = 3,
+ .y = 4,
+ },
+};
+const test3_bar = Test3Foo{ .Two = 13 };
+fn test3_1(f: Test3Foo) void {
+ switch (f) {
+ Test3Foo.Three => |pt| {
+ expect(pt.x == 3);
+ expect(pt.y == 4);
+ },
+ else => unreachable,
+ }
+}
+fn test3_2(f: Test3Foo) void {
+ switch (f) {
+ Test3Foo.Two => |x| {
+ expect(x == 13);
+ },
+ else => unreachable,
+ }
+}
+
+test "character literals" {
+ expect('\'' == single_quote);
+}
+const single_quote = '\'';
+
+test "take address of parameter" {
+ testTakeAddressOfParameter(12.34);
+}
+fn testTakeAddressOfParameter(f: f32) void {
+ const f_ptr = &f;
+ expect(f_ptr.* == 12.34);
+}
+
+test "pointer comparison" {
+ const a = @as([]const u8, "a");
+ const b = &a;
+ expect(ptrEql(b, b));
+}
+fn ptrEql(a: *const []const u8, b: *const []const u8) bool {
+ return a == b;
+}
+
+test "string concatenation" {
+ const a = "OK" ++ " IT " ++ "WORKED";
+ const b = "OK IT WORKED";
+
+ comptime expect(@TypeOf(a) == *const [12:0]u8);
+ comptime expect(@TypeOf(b) == *const [12:0]u8);
+
+ const len = mem.len(b);
+ const len_with_null = len + 1;
+ {
+ var i: u32 = 0;
+ while (i < len_with_null) : (i += 1) {
+ expect(a[i] == b[i]);
+ }
+ }
+ expect(a[len] == 0);
+ expect(b[len] == 0);
+}
+
+test "pointer to void return type" {
+ testPointerToVoidReturnType() catch unreachable;
+}
+fn testPointerToVoidReturnType() anyerror!void {
+ const a = testPointerToVoidReturnType2();
+ return a.*;
+}
+const test_pointer_to_void_return_type_x = void{};
+fn testPointerToVoidReturnType2() *const void {
+ return &test_pointer_to_void_return_type_x;
+}
+
+test "non const ptr to aliased type" {
+ const int = i32;
+ expect(?*int == ?*i32);
+}
+
+test "array 2D const double ptr" {
+ const rect_2d_vertexes = [_][1]f32{
+ [_]f32{1.0},
+ [_]f32{2.0},
+ };
+ testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]);
+}
+
+fn testArray2DConstDoublePtr(ptr: *const f32) void {
+ const ptr2 = @ptrCast([*]const f32, ptr);
+ expect(ptr2[0] == 1.0);
+ expect(ptr2[1] == 2.0);
+}
+
+const AStruct = struct {
+ x: i32,
+};
+const AnEnum = enum {
+ One,
+ Two,
+};
+const AUnionEnum = union(enum) {
+ One: i32,
+ Two: void,
+};
+const AUnion = union {
+ One: void,
+ Two: void,
+};
+
+test "@typeName" {
+ const Struct = struct {};
+ const Union = union {
+ unused: u8,
+ };
+ const Enum = enum {
+ Unused,
+ };
+ comptime {
+ expect(mem.eql(u8, @typeName(i64), "i64"));
+ expect(mem.eql(u8, @typeName(*usize), "*usize"));
+ // https://github.com/ziglang/zig/issues/675
+ expect(mem.eql(u8, "behavior.misc.TypeFromFn(u8)", @typeName(TypeFromFn(u8))));
+ expect(mem.eql(u8, @typeName(Struct), "Struct"));
+ expect(mem.eql(u8, @typeName(Union), "Union"));
+ expect(mem.eql(u8, @typeName(Enum), "Enum"));
+ }
+}
+
+fn TypeFromFn(comptime T: type) type {
+ return struct {};
+}
+
+test "double implicit cast in same expression" {
+ var x = @as(i32, @as(u16, nine()));
+ expect(x == 9);
+}
+fn nine() u8 {
+ return 9;
+}
+
+test "global variable initialized to global variable array element" {
+ expect(global_ptr == &gdt[0]);
+}
+const GDTEntry = struct {
+ field: i32,
+};
+var gdt = [_]GDTEntry{
+ GDTEntry{ .field = 1 },
+ GDTEntry{ .field = 2 },
+};
+var global_ptr = &gdt[0];
+
+// can't really run this test but we can make sure it has no compile error
+// and generates code
+const vram = @intToPtr([*]volatile u8, 0x20000000)[0..0x8000];
+export fn writeToVRam() void {
+ vram[0] = 'X';
+}
+
+const OpaqueA = opaque {};
+const OpaqueB = opaque {};
+test "opaque types" {
+ expect(*OpaqueA != *OpaqueB);
+ expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));
+ expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
+}
+
+test "variable is allowed to be a pointer to an opaque type" {
+ var x: i32 = 1234;
+ _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));
+}
+fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA {
+ var a = ptr;
+ return a;
+}
+
+test "comptime if inside runtime while which unconditionally breaks" {
+ testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
+ comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
+}
+fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void {
+ while (cond) {
+ if (false) {}
+ break;
+ }
+}
+
+test "implicit comptime while" {
+ while (false) {
+ @compileError("bad");
+ }
+}
+
+fn fnThatClosesOverLocalConst() type {
+ const c = 1;
+ return struct {
+ fn g() i32 {
+ return c;
+ }
+ };
+}
+
+test "function closes over local const" {
+ const x = fnThatClosesOverLocalConst().g();
+ expect(x == 1);
+}
+
+test "cold function" {
+ thisIsAColdFn();
+ comptime thisIsAColdFn();
+}
+
+fn thisIsAColdFn() void {
+ @setCold(true);
+}
+
+const PackedStruct = packed struct {
+ a: u8,
+ b: u8,
+};
+const PackedUnion = packed union {
+ a: u8,
+ b: u32,
+};
+const PackedEnum = packed enum {
+ A,
+ B,
+};
+
+test "packed struct, enum, union parameters in extern function" {
+ testPackedStuff(&(PackedStruct{
+ .a = 1,
+ .b = 2,
+ }), &(PackedUnion{ .a = 1 }), PackedEnum.A);
+}
+
+export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: PackedEnum) void {}
+
+test "slicing zero length array" {
+ const s1 = ""[0..];
+ const s2 = ([_]u32{})[0..];
+ expect(s1.len == 0);
+ expect(s2.len == 0);
+ expect(mem.eql(u8, s1, ""));
+ expect(mem.eql(u32, s2, &[_]u32{}));
+}
+
+const addr1 = @ptrCast(*const u8, emptyFn);
+test "comptime cast fn to ptr" {
+ const addr2 = @ptrCast(*const u8, emptyFn);
+ comptime expect(addr1 == addr2);
+}
+
+test "equality compare fn ptrs" {
+ var a = emptyFn;
+ expect(a == a);
+}
+
+test "self reference through fn ptr field" {
+ const S = struct {
+ const A = struct {
+ f: fn (A) u8,
+ };
+
+ fn foo(a: A) u8 {
+ return 12;
+ }
+ };
+ var a: S.A = undefined;
+ a.f = S.foo;
+ expect(a.f(a) == 12);
+}
+
+test "volatile load and store" {
+ var number: i32 = 1234;
+ const ptr = @as(*volatile i32, &number);
+ ptr.* += 1;
+ expect(ptr.* == 1235);
+}
+
+test "slice string literal has correct type" {
+ comptime {
+ expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);
+ const array = [_]i32{ 1, 2, 3, 4 };
+ expect(@TypeOf(array[0..]) == *const [4]i32);
+ }
+ var runtime_zero: usize = 0;
+ comptime expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8);
+ const array = [_]i32{ 1, 2, 3, 4 };
+ comptime expect(@TypeOf(array[runtime_zero..]) == []const i32);
+}
+
+test "struct inside function" {
+ testStructInFn();
+ comptime testStructInFn();
+}
+
+fn testStructInFn() void {
+ const BlockKind = u32;
+
+ const Block = struct {
+ kind: BlockKind,
+ };
+
+ var block = Block{ .kind = 1234 };
+
+ block.kind += 1;
+
+ expect(block.kind == 1235);
+}
+
+test "fn call returning scalar optional in equality expression" {
+ expect(getNull() == null);
+}
+
+fn getNull() ?*i32 {
+ return null;
+}
+
+test "thread local variable" {
+ const S = struct {
+ threadlocal var t: i32 = 1234;
+ };
+ S.t += 1;
+ expect(S.t == 1235);
+}
+
+test "unicode escape in character literal" {
+ var a: u24 = '\u{01f4a9}';
+ expect(a == 128169);
+}
+
+test "unicode character in character literal" {
+ expect('💩' == 128169);
+}
+
+test "result location zero sized array inside struct field implicit cast to slice" {
+ const E = struct {
+ entries: []u32,
+ };
+ var foo = E{ .entries = &[_]u32{} };
+ expect(foo.entries.len == 0);
+}
+
+var global_foo: *i32 = undefined;
+
+test "global variable assignment with optional unwrapping with var initialized to undefined" {
+ const S = struct {
+ var data: i32 = 1234;
+ fn foo() ?*i32 {
+ return &data;
+ }
+ };
+ global_foo = S.foo() orelse {
+ @panic("bad");
+ };
+ expect(global_foo.* == 1234);
+}
+
+test "peer result location with typed parent, runtime condition, comptime prongs" {
+ const S = struct {
+ fn doTheTest(arg: i32) i32 {
+ const st = Structy{
+ .bleh = if (arg == 1) 1 else 1,
+ };
+
+ if (st.bleh == 1)
+ return 1234;
+ return 0;
+ }
+
+ const Structy = struct {
+ bleh: i32,
+ };
+ };
+ expect(S.doTheTest(0) == 1234);
+ expect(S.doTheTest(1) == 1234);
+}
+
+test "nested optional field in struct" {
+ const S2 = struct {
+ y: u8,
+ };
+ const S1 = struct {
+ x: ?S2,
+ };
+ var s = S1{
+ .x = S2{ .y = 127 },
+ };
+ expect(s.x.?.y == 127);
+}
+
+fn maybe(x: bool) anyerror!?u32 {
+ return switch (x) {
+ true => @as(u32, 42),
+ else => null,
+ };
+}
+
+test "result location is optional inside error union" {
+ const x = maybe(true) catch unreachable;
+ expect(x.? == 42);
+}
+
+threadlocal var buffer: [11]u8 = undefined;
+
+test "pointer to thread local array" {
+ const s = "Hello world";
+ std.mem.copy(u8, buffer[0..], s);
+ std.testing.expectEqualSlices(u8, buffer[0..], s);
+}
+
+test "auto created variables have correct alignment" {
+ const S = struct {
+ fn foo(str: [*]const u8) u32 {
+ for (@ptrCast([*]align(1) const u32, str)[0..1]) |v| {
+ return v;
+ }
+ return 0;
+ }
+ };
+ expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);
+ comptime expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);
+}
+
+extern var opaque_extern_var: opaque {};
+var var_to_export: u32 = 42;
+test "extern variable with non-pointer opaque type" {
+ @export(var_to_export, .{ .name = "opaque_extern_var" });
+ expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42);
+}
+
+test "lazy typeInfo value as generic parameter" {
+ const S = struct {
+ fn foo(args: anytype) void {}
+ };
+ S.foo(@typeInfo(@TypeOf(.{})));
+}
diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig
new file mode 100644
index 0000000000..d507f503f5
--- /dev/null
+++ b/test/behavior/muladd.zig
@@ -0,0 +1,34 @@
+const expect = @import("std").testing.expect;
+
+test "@mulAdd" {
+ comptime testMulAdd();
+ testMulAdd();
+}
+
+fn testMulAdd() void {
+ {
+ var a: f16 = 5.5;
+ var b: f16 = 2.5;
+ var c: f16 = 6.25;
+ expect(@mulAdd(f16, a, b, c) == 20);
+ }
+ {
+ var a: f32 = 5.5;
+ var b: f32 = 2.5;
+ var c: f32 = 6.25;
+ expect(@mulAdd(f32, a, b, c) == 20);
+ }
+ {
+ var a: f64 = 5.5;
+ var b: f64 = 2.5;
+ var c: f64 = 6.25;
+ expect(@mulAdd(f64, a, b, c) == 20);
+ }
+ // Awaits implementation in libm.zig
+ //{
+ // var a: f16 = 5.5;
+ // var b: f128 = 2.5;
+ // var c: f128 = 6.25;
+ // expect(@mulAdd(f128, a, b, c) == 20);
+ //}
+}
diff --git a/test/behavior/namespace_depends_on_compile_var.zig b/test/behavior/namespace_depends_on_compile_var.zig
new file mode 100644
index 0000000000..8c5c19d733
--- /dev/null
+++ b/test/behavior/namespace_depends_on_compile_var.zig
@@ -0,0 +1,14 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "namespace depends on compile var" {
+ if (some_namespace.a_bool) {
+ expect(some_namespace.a_bool);
+ } else {
+ expect(!some_namespace.a_bool);
+ }
+}
+const some_namespace = switch (std.builtin.os.tag) {
+ .linux => @import("namespace_depends_on_compile_var/a.zig"),
+ else => @import("namespace_depends_on_compile_var/b.zig"),
+};
diff --git a/test/behavior/namespace_depends_on_compile_var/a.zig b/test/behavior/namespace_depends_on_compile_var/a.zig
new file mode 100644
index 0000000000..5ce0e94f8b
--- /dev/null
+++ b/test/behavior/namespace_depends_on_compile_var/a.zig
@@ -0,0 +1 @@
+pub const a_bool = true;
diff --git a/test/behavior/namespace_depends_on_compile_var/b.zig b/test/behavior/namespace_depends_on_compile_var/b.zig
new file mode 100644
index 0000000000..a12a54b589
--- /dev/null
+++ b/test/behavior/namespace_depends_on_compile_var/b.zig
@@ -0,0 +1 @@
+pub const a_bool = false;
diff --git a/test/behavior/null.zig b/test/behavior/null.zig
new file mode 100644
index 0000000000..8c9b86b260
--- /dev/null
+++ b/test/behavior/null.zig
@@ -0,0 +1,162 @@
+const expect = @import("std").testing.expect;
+
+test "optional type" {
+ const x: ?bool = true;
+
+ if (x) |y| {
+ if (y) {
+ // OK
+ } else {
+ unreachable;
+ }
+ } else {
+ unreachable;
+ }
+
+ const next_x: ?i32 = null;
+
+ const z = next_x orelse 1234;
+
+ expect(z == 1234);
+
+ const final_x: ?i32 = 13;
+
+ const num = final_x orelse unreachable;
+
+ expect(num == 13);
+}
+
+test "test maybe object and get a pointer to the inner value" {
+ var maybe_bool: ?bool = true;
+
+ if (maybe_bool) |*b| {
+ b.* = false;
+ }
+
+ expect(maybe_bool.? == false);
+}
+
+test "rhs maybe unwrap return" {
+ const x: ?bool = true;
+ const y = x orelse return;
+}
+
+test "maybe return" {
+ maybeReturnImpl();
+ comptime maybeReturnImpl();
+}
+
+fn maybeReturnImpl() void {
+ expect(foo(1235).?);
+ if (foo(null) != null) unreachable;
+ expect(!foo(1234).?);
+}
+
+fn foo(x: ?i32) ?bool {
+ const value = x orelse return null;
+ return value > 1234;
+}
+
+test "if var maybe pointer" {
+ expect(shouldBeAPlus1(Particle{
+ .a = 14,
+ .b = 1,
+ .c = 1,
+ .d = 1,
+ }) == 15);
+}
+fn shouldBeAPlus1(p: Particle) u64 {
+ var maybe_particle: ?Particle = p;
+ if (maybe_particle) |*particle| {
+ particle.a += 1;
+ }
+ if (maybe_particle) |particle| {
+ return particle.a;
+ }
+ return 0;
+}
+const Particle = struct {
+ a: u64,
+ b: u64,
+ c: u64,
+ d: u64,
+};
+
+test "null literal outside function" {
+ const is_null = here_is_a_null_literal.context == null;
+ expect(is_null);
+
+ const is_non_null = here_is_a_null_literal.context != null;
+ expect(!is_non_null);
+}
+const SillyStruct = struct {
+ context: ?i32,
+};
+const here_is_a_null_literal = SillyStruct{ .context = null };
+
+test "test null runtime" {
+ testTestNullRuntime(null);
+}
+fn testTestNullRuntime(x: ?i32) void {
+ expect(x == null);
+ expect(!(x != null));
+}
+
+test "optional void" {
+ optionalVoidImpl();
+ comptime optionalVoidImpl();
+}
+
+fn optionalVoidImpl() void {
+ expect(bar(null) == null);
+ expect(bar({}) != null);
+}
+
+fn bar(x: ?void) ?void {
+ if (x) |_| {
+ return {};
+ } else {
+ return null;
+ }
+}
+
+const StructWithOptional = struct {
+ field: ?i32,
+};
+
+var struct_with_optional: StructWithOptional = undefined;
+
+test "unwrap optional which is field of global var" {
+ struct_with_optional.field = null;
+ if (struct_with_optional.field) |payload| {
+ unreachable;
+ }
+ struct_with_optional.field = 1234;
+ if (struct_with_optional.field) |payload| {
+ expect(payload == 1234);
+ } else {
+ unreachable;
+ }
+}
+
+test "null with default unwrap" {
+ const x: i32 = null orelse 1;
+ expect(x == 1);
+}
+
+test "optional types" {
+ comptime {
+ const opt_type_struct = StructWithOptionalType{ .t = u8 };
+ expect(opt_type_struct.t != null and opt_type_struct.t.? == u8);
+ }
+}
+
+const StructWithOptionalType = struct {
+ t: ?type,
+};
+
+test "optional pointer to 0 bit type null value at runtime" {
+ const EmptyStruct = struct {};
+ var x: ?*EmptyStruct = null;
+ expect(x == null);
+}
diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig
new file mode 100644
index 0000000000..1dc33eb8ea
--- /dev/null
+++ b/test/behavior/optional.zig
@@ -0,0 +1,269 @@
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
+
+pub const EmptyStruct = struct {};
+
+test "optional pointer to size zero struct" {
+ var e = EmptyStruct{};
+ var o: ?*EmptyStruct = &e;
+ expect(o != null);
+}
+
+test "equality compare nullable pointers" {
+ testNullPtrsEql();
+ comptime testNullPtrsEql();
+}
+
+fn testNullPtrsEql() void {
+ var number: i32 = 1234;
+
+ var x: ?*i32 = null;
+ var y: ?*i32 = null;
+ expect(x == y);
+ y = &number;
+ expect(x != y);
+ expect(x != &number);
+ expect(&number != x);
+ x = &number;
+ expect(x == y);
+ expect(x == &number);
+ expect(&number == x);
+}
+
+test "address of unwrap optional" {
+ const S = struct {
+ const Foo = struct {
+ a: i32,
+ };
+
+ var global: ?Foo = null;
+
+ pub fn getFoo() anyerror!*Foo {
+ return &global.?;
+ }
+ };
+ S.global = S.Foo{ .a = 1234 };
+ const foo = S.getFoo() catch unreachable;
+ expect(foo.a == 1234);
+}
+
+test "equality compare optional with non-optional" {
+ test_cmp_optional_non_optional();
+ comptime test_cmp_optional_non_optional();
+}
+
+fn test_cmp_optional_non_optional() void {
+ var ten: i32 = 10;
+ var opt_ten: ?i32 = 10;
+ var five: i32 = 5;
+ var int_n: ?i32 = null;
+
+ expect(int_n != ten);
+ expect(opt_ten == ten);
+ expect(opt_ten != five);
+
+ // test evaluation is always lexical
+ // ensure that the optional isn't always computed before the non-optional
+ var mutable_state: i32 = 0;
+ _ = blk1: {
+ mutable_state += 1;
+ break :blk1 @as(?f64, 10.0);
+ } != blk2: {
+ expect(mutable_state == 1);
+ break :blk2 @as(f64, 5.0);
+ };
+ _ = blk1: {
+ mutable_state += 1;
+ break :blk1 @as(f64, 10.0);
+ } != blk2: {
+ expect(mutable_state == 2);
+ break :blk2 @as(?f64, 5.0);
+ };
+}
+
+test "passing an optional integer as a parameter" {
+ const S = struct {
+ fn entry() bool {
+ var x: i32 = 1234;
+ return foo(x);
+ }
+
+ fn foo(x: ?i32) bool {
+ return x.? == 1234;
+ }
+ };
+ expect(S.entry());
+ comptime expect(S.entry());
+}
+
+test "unwrap function call with optional pointer return value" {
+ const S = struct {
+ fn entry() void {
+ expect(foo().?.* == 1234);
+ expect(bar() == null);
+ }
+ const global: i32 = 1234;
+ fn foo() ?*const i32 {
+ return &global;
+ }
+ fn bar() ?*i32 {
+ return null;
+ }
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+test "nested orelse" {
+ const S = struct {
+ fn entry() void {
+ expect(func() == null);
+ }
+ fn maybe() ?Foo {
+ return null;
+ }
+ fn func() ?Foo {
+ const x = maybe() orelse
+ maybe() orelse
+ return null;
+ unreachable;
+ }
+ const Foo = struct {
+ field: i32,
+ };
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+test "self-referential struct through a slice of optional" {
+ const S = struct {
+ const Node = struct {
+ children: []?Node,
+ data: ?u8,
+
+ fn new() Node {
+ return Node{
+ .children = undefined,
+ .data = null,
+ };
+ }
+ };
+ };
+
+ var n = S.Node.new();
+ expect(n.data == null);
+}
+
+test "assigning to an unwrapped optional field in an inline loop" {
+ comptime var maybe_pos_arg: ?comptime_int = null;
+ inline for ("ab") |x| {
+ maybe_pos_arg = 0;
+ if (maybe_pos_arg.? != 0) {
+ @compileError("bad");
+ }
+ maybe_pos_arg.? = 10;
+ }
+}
+
+test "coerce an anon struct literal to optional struct" {
+ const S = struct {
+ const Struct = struct {
+ field: u32,
+ };
+ export fn doTheTest() void {
+ var maybe_dims: ?Struct = null;
+ maybe_dims = .{ .field = 1 };
+ expect(maybe_dims.?.field == 1);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "optional with void type" {
+ const Foo = struct {
+ x: ?void,
+ };
+ var x = Foo{ .x = null };
+ expect(x.x == null);
+}
+
+test "0-bit child type coerced to optional return ptr result location" {
+ const S = struct {
+ fn doTheTest() void {
+ var y = Foo{};
+ var z = y.thing();
+ expect(z != null);
+ }
+
+ const Foo = struct {
+ pub const Bar = struct {
+ field: *Foo,
+ };
+
+ pub fn thing(self: *Foo) ?Bar {
+ return Bar{ .field = self };
+ }
+ };
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "0-bit child type coerced to optional" {
+ const S = struct {
+ fn doTheTest() void {
+ var it: Foo = .{
+ .list = undefined,
+ };
+ expect(it.foo() != null);
+ }
+
+ const Empty = struct {};
+ const Foo = struct {
+ list: [10]Empty,
+
+ fn foo(self: *Foo) ?*Empty {
+ const data = &self.list[0];
+ return data;
+ }
+ };
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "array of optional unaligned types" {
+ const Enum = enum { one, two, three };
+
+ const SomeUnion = union(enum) {
+ Num: Enum,
+ Other: u32,
+ };
+
+ const values = [_]?SomeUnion{
+ SomeUnion{ .Num = .one },
+ SomeUnion{ .Num = .two },
+ SomeUnion{ .Num = .three },
+ SomeUnion{ .Num = .one },
+ SomeUnion{ .Num = .two },
+ SomeUnion{ .Num = .three },
+ };
+
+ // The index must be a runtime value
+ var i: usize = 0;
+ expectEqual(Enum.one, values[i].?.Num);
+ i += 1;
+ expectEqual(Enum.two, values[i].?.Num);
+ i += 1;
+ expectEqual(Enum.three, values[i].?.Num);
+ i += 1;
+ expectEqual(Enum.one, values[i].?.Num);
+ i += 1;
+ expectEqual(Enum.two, values[i].?.Num);
+ i += 1;
+ expectEqual(Enum.three, values[i].?.Num);
+}
diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig
new file mode 100644
index 0000000000..a4f619b538
--- /dev/null
+++ b/test/behavior/pointers.zig
@@ -0,0 +1,339 @@
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+const expectError = testing.expectError;
+
+test "dereference pointer" {
+ comptime testDerefPtr();
+ testDerefPtr();
+}
+
+fn testDerefPtr() void {
+ var x: i32 = 1234;
+ var y = &x;
+ y.* += 1;
+ expect(x == 1235);
+}
+
+const Foo1 = struct {
+ x: void,
+};
+
+test "dereference pointer again" {
+ testDerefPtrOneVal();
+ comptime testDerefPtrOneVal();
+}
+
+fn testDerefPtrOneVal() void {
+ // Foo1 satisfies the OnePossibleValueYes criteria
+ const x = &Foo1{ .x = {} };
+ const y = x.*;
+ expect(@TypeOf(y.x) == void);
+}
+
+test "pointer arithmetic" {
+ var ptr: [*]const u8 = "abcd";
+
+ expect(ptr[0] == 'a');
+ ptr += 1;
+ expect(ptr[0] == 'b');
+ ptr += 1;
+ expect(ptr[0] == 'c');
+ ptr += 1;
+ expect(ptr[0] == 'd');
+ ptr += 1;
+ expect(ptr[0] == 0);
+ ptr -= 1;
+ expect(ptr[0] == 'd');
+ ptr -= 1;
+ expect(ptr[0] == 'c');
+ ptr -= 1;
+ expect(ptr[0] == 'b');
+ ptr -= 1;
+ expect(ptr[0] == 'a');
+}
+
+test "double pointer parsing" {
+ comptime expect(PtrOf(PtrOf(i32)) == **i32);
+}
+
+fn PtrOf(comptime T: type) type {
+ return *T;
+}
+
+test "assigning integer to C pointer" {
+ var x: i32 = 0;
+ var ptr: [*c]u8 = 0;
+ var ptr2: [*c]u8 = x;
+}
+
+test "implicit cast single item pointer to C pointer and back" {
+ var y: u8 = 11;
+ var x: [*c]u8 = &y;
+ var z: *u8 = x;
+ z.* += 1;
+ expect(y == 12);
+}
+
+test "C pointer comparison and arithmetic" {
+ const S = struct {
+ fn doTheTest() void {
+ var one: usize = 1;
+ var ptr1: [*c]u32 = 0;
+ var ptr2 = ptr1 + 10;
+ expect(ptr1 == 0);
+ expect(ptr1 >= 0);
+ expect(ptr1 <= 0);
+ // expect(ptr1 < 1);
+ // expect(ptr1 < one);
+ // expect(1 > ptr1);
+ // expect(one > ptr1);
+ expect(ptr1 < ptr2);
+ expect(ptr2 > ptr1);
+ expect(ptr2 >= 40);
+ expect(ptr2 == 40);
+ expect(ptr2 <= 40);
+ ptr2 -= 10;
+ expect(ptr1 == ptr2);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "peer type resolution with C pointers" {
+ var ptr_one: *u8 = undefined;
+ var ptr_many: [*]u8 = undefined;
+ var ptr_c: [*c]u8 = undefined;
+ var t = true;
+ var x1 = if (t) ptr_one else ptr_c;
+ var x2 = if (t) ptr_many else ptr_c;
+ var x3 = if (t) ptr_c else ptr_one;
+ var x4 = if (t) ptr_c else ptr_many;
+ expect(@TypeOf(x1) == [*c]u8);
+ expect(@TypeOf(x2) == [*c]u8);
+ expect(@TypeOf(x3) == [*c]u8);
+ expect(@TypeOf(x4) == [*c]u8);
+}
+
+test "implicit casting between C pointer and optional non-C pointer" {
+ var slice: []const u8 = "aoeu";
+ const opt_many_ptr: ?[*]const u8 = slice.ptr;
+ var ptr_opt_many_ptr = &opt_many_ptr;
+ var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
+ expect(c_ptr.*.* == 'a');
+ ptr_opt_many_ptr = c_ptr;
+ expect(ptr_opt_many_ptr.*.?[1] == 'o');
+}
+
+test "implicit cast error unions with non-optional to optional pointer" {
+ const S = struct {
+ fn doTheTest() void {
+ expectError(error.Fail, foo());
+ }
+ fn foo() anyerror!?*u8 {
+ return bar() orelse error.Fail;
+ }
+ fn bar() ?*u8 {
+ return null;
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "initialize const optional C pointer to null" {
+ const a: ?[*c]i32 = null;
+ expect(a == null);
+ comptime expect(a == null);
+}
+
+test "compare equality of optional and non-optional pointer" {
+ const a = @intToPtr(*const usize, 0x12345678);
+ const b = @intToPtr(?*usize, 0x12345678);
+ expect(a == b);
+ expect(b == a);
+}
+
+test "allowzero pointer and slice" {
+ var ptr = @intToPtr([*]allowzero i32, 0);
+ var opt_ptr: ?[*]allowzero i32 = ptr;
+ expect(opt_ptr != null);
+ expect(@ptrToInt(ptr) == 0);
+ var runtime_zero: usize = 0;
+ var slice = ptr[runtime_zero..10];
+ comptime expect(@TypeOf(slice) == []allowzero i32);
+ expect(@ptrToInt(&slice[5]) == 20);
+
+ comptime expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
+ comptime expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
+}
+
+test "assign null directly to C pointer and test null equality" {
+ var x: [*c]i32 = null;
+ expect(x == null);
+ expect(null == x);
+ expect(!(x != null));
+ expect(!(null != x));
+ if (x) |same_x| {
+ @panic("fail");
+ }
+ var otherx: i32 = undefined;
+ expect((x orelse &otherx) == &otherx);
+
+ const y: [*c]i32 = null;
+ comptime expect(y == null);
+ comptime expect(null == y);
+ comptime expect(!(y != null));
+ comptime expect(!(null != y));
+ if (y) |same_y| @panic("fail");
+ const othery: i32 = undefined;
+ comptime expect((y orelse &othery) == &othery);
+
+ var n: i32 = 1234;
+ var x1: [*c]i32 = &n;
+ expect(!(x1 == null));
+ expect(!(null == x1));
+ expect(x1 != null);
+ expect(null != x1);
+ expect(x1.?.* == 1234);
+ if (x1) |same_x1| {
+ expect(same_x1.* == 1234);
+ } else {
+ @panic("fail");
+ }
+ expect((x1 orelse &otherx) == x1);
+
+ const nc: i32 = 1234;
+ const y1: [*c]const i32 = &nc;
+ comptime expect(!(y1 == null));
+ comptime expect(!(null == y1));
+ comptime expect(y1 != null);
+ comptime expect(null != y1);
+ comptime expect(y1.?.* == 1234);
+ if (y1) |same_y1| {
+ expect(same_y1.* == 1234);
+ } else {
+ @compileError("fail");
+ }
+ comptime expect((y1 orelse &othery) == y1);
+}
+
+test "null terminated pointer" {
+ const S = struct {
+ fn doTheTest() void {
+ var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' };
+ var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero);
+ var no_zero_ptr: [*]const u8 = zero_ptr;
+ var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr);
+ expect(std.mem.eql(u8, std.mem.spanZ(zero_ptr_again), "hello"));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "allow any sentinel" {
+ const S = struct {
+ fn doTheTest() void {
+ var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 };
+ var ptr: [*:std.math.minInt(i32)]i32 = &array;
+ expect(ptr[4] == std.math.minInt(i32));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "pointer sentinel with enums" {
+ const S = struct {
+ const Number = enum {
+ one,
+ two,
+ sentinel,
+ };
+
+ fn doTheTest() void {
+ var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one };
+ expect(ptr[4] == .sentinel); // TODO this should be comptime expect, see #3731
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "pointer sentinel with optional element" {
+ const S = struct {
+ fn doTheTest() void {
+ var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 };
+ expect(ptr[4] == null); // TODO this should be comptime expect, see #3731
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "pointer sentinel with +inf" {
+ const S = struct {
+ fn doTheTest() void {
+ const inf = std.math.inf_f32;
+ var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 };
+ expect(ptr[4] == inf); // TODO this should be comptime expect, see #3731
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "pointer to array at fixed address" {
+ const array = @intToPtr(*volatile [1]u32, 0x10);
+ // Silly check just to reference `array`
+ expect(@ptrToInt(&array[0]) == 0x10);
+}
+
+test "pointer arithmetic affects the alignment" {
+ {
+ var ptr: [*]align(8) u32 = undefined;
+ var x: usize = 1;
+
+ expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8);
+ const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4
+ expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4);
+ const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8
+ expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8);
+ const ptr3 = ptr + 0; // no-op
+ expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
+ const ptr4 = ptr + x; // runtime-known addend
+ expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
+ }
+ {
+ var ptr: [*]align(8) [3]u8 = undefined;
+ var x: usize = 1;
+
+ const ptr1 = ptr + 17; // 3 * 17 = 51
+ expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1);
+ const ptr2 = ptr + x; // runtime-known addend
+ expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1);
+ const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8
+ expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
+ const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4
+ expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
+ }
+}
+
+test "@ptrToInt on null optional at comptime" {
+ {
+ const pointer = @intToPtr(?*u8, 0x000);
+ const x = @ptrToInt(pointer);
+ comptime expect(0 == @ptrToInt(pointer));
+ }
+ {
+ const pointer = @intToPtr(?*u8, 0xf00);
+ comptime expect(0xf00 == @ptrToInt(pointer));
+ }
+}
+
+test "indexing array with sentinel returns correct type" {
+ var s: [:0]const u8 = "abc";
+ testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
+}
diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig
new file mode 100644
index 0000000000..884a7bdb6d
--- /dev/null
+++ b/test/behavior/popcount.zig
@@ -0,0 +1,43 @@
+const expect = @import("std").testing.expect;
+
+test "@popCount" {
+ comptime testPopCount();
+ testPopCount();
+}
+
+fn testPopCount() void {
+ {
+ var x: u32 = 0xffffffff;
+ expect(@popCount(u32, x) == 32);
+ }
+ {
+ var x: u5 = 0x1f;
+ expect(@popCount(u5, x) == 5);
+ }
+ {
+ var x: u32 = 0xaa;
+ expect(@popCount(u32, x) == 4);
+ }
+ {
+ var x: u32 = 0xaaaaaaaa;
+ expect(@popCount(u32, x) == 16);
+ }
+ {
+ var x: u32 = 0xaaaaaaaa;
+ expect(@popCount(u32, x) == 16);
+ }
+ {
+ var x: i16 = -1;
+ expect(@popCount(i16, x) == 16);
+ }
+ {
+ var x: i8 = -120;
+ expect(@popCount(i8, x) == 2);
+ }
+ comptime {
+ expect(@popCount(u8, @bitCast(u8, @as(i8, -120))) == 2);
+ }
+ comptime {
+ expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24);
+ }
+}
diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig
new file mode 100644
index 0000000000..2f6ce1243f
--- /dev/null
+++ b/test/behavior/ptrcast.zig
@@ -0,0 +1,73 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const expect = std.testing.expect;
+const native_endian = builtin.target.cpu.arch.endian();
+
+test "reinterpret bytes as integer with nonzero offset" {
+ testReinterpretBytesAsInteger();
+ comptime testReinterpretBytesAsInteger();
+}
+
+fn testReinterpretBytesAsInteger() void {
+ const bytes = "\x12\x34\x56\x78\xab";
+ const expected = switch (native_endian) {
+ .Little => 0xab785634,
+ .Big => 0x345678ab,
+ };
+ expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
+}
+
+test "reinterpret bytes of an array into an extern struct" {
+ testReinterpretBytesAsExternStruct();
+ comptime testReinterpretBytesAsExternStruct();
+}
+
+fn testReinterpretBytesAsExternStruct() void {
+ var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
+
+ const S = extern struct {
+ a: u8,
+ b: u16,
+ c: u8,
+ };
+
+ var ptr = @ptrCast(*const S, &bytes);
+ var val = ptr.c;
+ expect(val == 5);
+}
+
+test "reinterpret struct field at comptime" {
+ const numNative = comptime Bytes.init(0x12345678);
+ if (native_endian != .Little) {
+ expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes));
+ } else {
+ expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes));
+ }
+}
+
+const Bytes = struct {
+ bytes: [4]u8,
+
+ pub fn init(v: u32) Bytes {
+ var res: Bytes = undefined;
+ @ptrCast(*align(1) u32, &res.bytes).* = v;
+
+ return res;
+ }
+};
+
+test "comptime ptrcast keeps larger alignment" {
+ comptime {
+ const a: u32 = 1234;
+ const p = @ptrCast([*]const u8, &a);
+ std.debug.assert(@TypeOf(p) == [*]align(@alignOf(u32)) const u8);
+ }
+}
+
+test "implicit optional pointer to optional c_void pointer" {
+ var buf: [4]u8 = "aoeu".*;
+ var x: ?[*]u8 = &buf;
+ var y: ?*c_void = x;
+ var z = @ptrCast(*[4]u8, y);
+ expect(std.mem.eql(u8, z, "aoeu"));
+}
diff --git a/test/behavior/pub_enum.zig b/test/behavior/pub_enum.zig
new file mode 100644
index 0000000000..0613df94d9
--- /dev/null
+++ b/test/behavior/pub_enum.zig
@@ -0,0 +1,13 @@
+const other = @import("pub_enum/other.zig");
+const expect = @import("std").testing.expect;
+
+test "pub enum" {
+ pubEnumTest(other.APubEnum.Two);
+}
+fn pubEnumTest(foo: other.APubEnum) void {
+ expect(foo == other.APubEnum.Two);
+}
+
+test "cast with imported symbol" {
+ expect(@as(other.size_t, 42) == 42);
+}
diff --git a/test/behavior/pub_enum/other.zig b/test/behavior/pub_enum/other.zig
new file mode 100644
index 0000000000..c663950383
--- /dev/null
+++ b/test/behavior/pub_enum/other.zig
@@ -0,0 +1,6 @@
+pub const APubEnum = enum {
+ One,
+ Two,
+ Three,
+};
+pub const size_t = u64;
diff --git a/test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig
new file mode 100644
index 0000000000..2c1cf06268
--- /dev/null
+++ b/test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig
@@ -0,0 +1,37 @@
+const expect = @import("std").testing.expect;
+const mem = @import("std").mem;
+
+var ok: bool = false;
+test "reference a variable in an if after an if in the 2nd switch prong" {
+ foo(true, Num.Two, false, "aoeu");
+ expect(!ok);
+ foo(false, Num.One, false, "aoeu");
+ expect(!ok);
+ foo(true, Num.One, false, "aoeu");
+ expect(ok);
+}
+
+const Num = enum {
+ One,
+ Two,
+};
+
+fn foo(c: bool, k: Num, c2: bool, b: []const u8) void {
+ switch (k) {
+ Num.Two => {},
+ Num.One => {
+ if (c) {
+ const output_path = b;
+
+ if (c2) {}
+
+ a(output_path);
+ }
+ },
+ }
+}
+
+fn a(x: []const u8) void {
+ expect(mem.eql(u8, x, "aoeu"));
+ ok = true;
+}
diff --git a/test/behavior/reflection.zig b/test/behavior/reflection.zig
new file mode 100644
index 0000000000..6d1c341713
--- /dev/null
+++ b/test/behavior/reflection.zig
@@ -0,0 +1,55 @@
+const expect = @import("std").testing.expect;
+const mem = @import("std").mem;
+const reflection = @This();
+
+test "reflection: function return type, var args, and param types" {
+ comptime {
+ const info = @typeInfo(@TypeOf(dummy)).Fn;
+ expect(info.return_type.? == i32);
+ expect(!info.is_var_args);
+ expect(info.args.len == 3);
+ expect(info.args[0].arg_type.? == bool);
+ expect(info.args[1].arg_type.? == i32);
+ expect(info.args[2].arg_type.? == f32);
+ }
+}
+
+fn dummy(a: bool, b: i32, c: f32) i32 {
+ return 1234;
+}
+
+test "reflection: @field" {
+ var f = Foo{
+ .one = 42,
+ .two = true,
+ .three = void{},
+ };
+
+ expect(f.one == f.one);
+ expect(@field(f, "o" ++ "ne") == f.one);
+ expect(@field(f, "t" ++ "wo") == f.two);
+ expect(@field(f, "th" ++ "ree") == f.three);
+ expect(@field(Foo, "const" ++ "ant") == Foo.constant);
+ expect(@field(Bar, "O" ++ "ne") == Bar.One);
+ expect(@field(Bar, "T" ++ "wo") == Bar.Two);
+ expect(@field(Bar, "Th" ++ "ree") == Bar.Three);
+ expect(@field(Bar, "F" ++ "our") == Bar.Four);
+ expect(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2));
+ @field(f, "o" ++ "ne") = 4;
+ expect(f.one == 4);
+}
+
+const Foo = struct {
+ const constant = 52;
+
+ one: i32,
+ two: bool,
+ three: void,
+};
+
+const Bar = union(enum) {
+ One: void,
+ Two: i32,
+ Three: bool,
+ Four: f64,
+};
diff --git a/test/behavior/shuffle.zig b/test/behavior/shuffle.zig
new file mode 100644
index 0000000000..3c26adbd48
--- /dev/null
+++ b/test/behavior/shuffle.zig
@@ -0,0 +1,63 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const mem = std.mem;
+const expect = std.testing.expect;
+const Vector = std.meta.Vector;
+
+test "@shuffle" {
+ // TODO investigate why this fails when cross-compiling to wasm.
+ if (builtin.os.tag == .wasi) return error.SkipZigTest;
+
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
+ var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
+ const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) };
+ var res = @shuffle(i32, v, x, mask);
+ expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));
+
+ // Implicit cast from array (of mask)
+ res = @shuffle(i32, v, x, [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) });
+ expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));
+
+ // Undefined
+ const mask2: Vector(4, i32) = [4]i32{ 3, 1, 2, 0 };
+ res = @shuffle(i32, v, undefined, mask2);
+ expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 40, -2, 30, 2147483647 }));
+
+ // Upcasting of b
+ var v2: Vector(2, i32) = [2]i32{ 2147483647, undefined };
+ const mask3: Vector(4, i32) = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 };
+ res = @shuffle(i32, x, v2, mask3);
+ expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 2147483647, 4 }));
+
+ // Upcasting of a
+ var v3: Vector(2, i32) = [2]i32{ 2147483647, -2 };
+ const mask4: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) };
+ res = @shuffle(i32, v3, x, mask4);
+ expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, -2, 4 }));
+
+ // bool
+ // https://github.com/ziglang/zig/issues/3317
+ if (builtin.target.cpu.arch != .mipsel and builtin.target.cpu.arch != .mips) {
+ var x2: Vector(4, bool) = [4]bool{ false, true, false, true };
+ var v4: Vector(2, bool) = [2]bool{ true, false };
+ const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
+ var res2 = @shuffle(bool, x2, v4, mask5);
+ expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false }));
+ }
+
+ // TODO re-enable when LLVM codegen is fixed
+ // https://github.com/ziglang/zig/issues/3246
+ if (false) {
+ var x2: Vector(3, bool) = [3]bool{ false, true, false };
+ var v4: Vector(2, bool) = [2]bool{ true, false };
+ const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
+ var res2 = @shuffle(bool, x2, v4, mask5);
+ expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false }));
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig
new file mode 100644
index 0000000000..54efe9cb35
--- /dev/null
+++ b/test/behavior/sizeof_and_typeof.zig
@@ -0,0 +1,264 @@
+const std = @import("std");
+const builtin = std.builtin;
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+
+test "@sizeOf and @TypeOf" {
+ const y: @TypeOf(x) = 120;
+ expect(@sizeOf(@TypeOf(y)) == 2);
+}
+const x: u16 = 13;
+const z: @TypeOf(x) = 19;
+
+const A = struct {
+ a: u8,
+ b: u32,
+ c: u8,
+ d: u3,
+ e: u5,
+ f: u16,
+ g: u16,
+ h: u9,
+ i: u7,
+};
+
+const P = packed struct {
+ a: u8,
+ b: u32,
+ c: u8,
+ d: u3,
+ e: u5,
+ f: u16,
+ g: u16,
+ h: u9,
+ i: u7,
+};
+
+test "@byteOffsetOf" {
+ // Packed structs have fixed memory layout
+ expect(@byteOffsetOf(P, "a") == 0);
+ expect(@byteOffsetOf(P, "b") == 1);
+ expect(@byteOffsetOf(P, "c") == 5);
+ expect(@byteOffsetOf(P, "d") == 6);
+ expect(@byteOffsetOf(P, "e") == 6);
+ expect(@byteOffsetOf(P, "f") == 7);
+ expect(@byteOffsetOf(P, "g") == 9);
+ expect(@byteOffsetOf(P, "h") == 11);
+ expect(@byteOffsetOf(P, "i") == 12);
+
+ // Normal struct fields can be moved/padded
+ var a: A = undefined;
+ expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a"));
+ expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b"));
+ expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c"));
+ expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d"));
+ expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e"));
+ expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f"));
+ expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g"));
+ expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @byteOffsetOf(A, "h"));
+ expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @byteOffsetOf(A, "i"));
+}
+
+test "@byteOffsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
+ const p3a_len = 3;
+ const P3 = packed struct {
+ a: [p3a_len]u8,
+ b: usize,
+ };
+ std.testing.expectEqual(0, @byteOffsetOf(P3, "a"));
+ std.testing.expectEqual(p3a_len, @byteOffsetOf(P3, "b"));
+
+ const p5a_len = 5;
+ const P5 = packed struct {
+ a: [p5a_len]u8,
+ b: usize,
+ };
+ std.testing.expectEqual(0, @byteOffsetOf(P5, "a"));
+ std.testing.expectEqual(p5a_len, @byteOffsetOf(P5, "b"));
+
+ const p6a_len = 6;
+ const P6 = packed struct {
+ a: [p6a_len]u8,
+ b: usize,
+ };
+ std.testing.expectEqual(0, @byteOffsetOf(P6, "a"));
+ std.testing.expectEqual(p6a_len, @byteOffsetOf(P6, "b"));
+
+ const p7a_len = 7;
+ const P7 = packed struct {
+ a: [p7a_len]u8,
+ b: usize,
+ };
+ std.testing.expectEqual(0, @byteOffsetOf(P7, "a"));
+ std.testing.expectEqual(p7a_len, @byteOffsetOf(P7, "b"));
+
+ const p9a_len = 9;
+ const P9 = packed struct {
+ a: [p9a_len]u8,
+ b: usize,
+ };
+ std.testing.expectEqual(0, @byteOffsetOf(P9, "a"));
+ std.testing.expectEqual(p9a_len, @byteOffsetOf(P9, "b"));
+
+ // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
+}
+
+test "@bitOffsetOf" {
+ // Packed structs have fixed memory layout
+ expect(@bitOffsetOf(P, "a") == 0);
+ expect(@bitOffsetOf(P, "b") == 8);
+ expect(@bitOffsetOf(P, "c") == 40);
+ expect(@bitOffsetOf(P, "d") == 48);
+ expect(@bitOffsetOf(P, "e") == 51);
+ expect(@bitOffsetOf(P, "f") == 56);
+ expect(@bitOffsetOf(P, "g") == 72);
+
+ expect(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
+ expect(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
+ expect(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
+ expect(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
+ expect(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
+ expect(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
+ expect(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
+}
+
+test "@sizeOf on compile-time types" {
+ expect(@sizeOf(comptime_int) == 0);
+ expect(@sizeOf(comptime_float) == 0);
+ expect(@sizeOf(@TypeOf(.hi)) == 0);
+ expect(@sizeOf(@TypeOf(type)) == 0);
+}
+
+test "@sizeOf(T) == 0 doesn't force resolving struct size" {
+ const S = struct {
+ const Foo = struct {
+ y: if (@sizeOf(Foo) == 0) u64 else u32,
+ };
+ const Bar = struct {
+ x: i32,
+ y: if (0 == @sizeOf(Bar)) u64 else u32,
+ };
+ };
+
+ expect(@sizeOf(S.Foo) == 4);
+ expect(@sizeOf(S.Bar) == 8);
+}
+
+test "@TypeOf() has no runtime side effects" {
+ const S = struct {
+ fn foo(comptime T: type, ptr: *T) T {
+ ptr.* += 1;
+ return ptr.*;
+ }
+ };
+ var data: i32 = 0;
+ const T = @TypeOf(S.foo(i32, &data));
+ comptime expect(T == i32);
+ expect(data == 0);
+}
+
+test "@TypeOf() with multiple arguments" {
+ {
+ var var_1: u32 = undefined;
+ var var_2: u8 = undefined;
+ var var_3: u64 = undefined;
+ comptime expect(@TypeOf(var_1, var_2, var_3) == u64);
+ }
+ {
+ var var_1: f16 = undefined;
+ var var_2: f32 = undefined;
+ var var_3: f64 = undefined;
+ comptime expect(@TypeOf(var_1, var_2, var_3) == f64);
+ }
+ {
+ var var_1: u16 = undefined;
+ comptime expect(@TypeOf(var_1, 0xffff) == u16);
+ }
+ {
+ var var_1: f32 = undefined;
+ comptime expect(@TypeOf(var_1, 3.1415) == f32);
+ }
+}
+
+test "branching logic inside @TypeOf" {
+ const S = struct {
+ var data: i32 = 0;
+ fn foo() anyerror!i32 {
+ data += 1;
+ return undefined;
+ }
+ };
+ const T = @TypeOf(S.foo() catch undefined);
+ comptime expect(T == i32);
+ expect(S.data == 0);
+}
+
+fn fn1(alpha: bool) void {
+ const n: usize = 7;
+ const v = if (alpha) n else @sizeOf(usize);
+}
+
+test "lazy @sizeOf result is checked for definedness" {
+ const f = fn1;
+}
+
+test "@bitSizeOf" {
+ expect(@bitSizeOf(u2) == 2);
+ expect(@bitSizeOf(u8) == @sizeOf(u8) * 8);
+ expect(@bitSizeOf(struct {
+ a: u2,
+ }) == 8);
+ expect(@bitSizeOf(packed struct {
+ a: u2,
+ }) == 2);
+}
+
+test "@sizeOf comparison against zero" {
+ const S0 = struct {
+ f: *@This(),
+ };
+ const U0 = union {
+ f: *@This(),
+ };
+ const S1 = struct {
+ fn H(comptime T: type) type {
+ return struct {
+ x: T,
+ };
+ }
+ f0: H(*@This()),
+ f1: H(**@This()),
+ f2: H(***@This()),
+ };
+ const U1 = union {
+ fn H(comptime T: type) type {
+ return struct {
+ x: T,
+ };
+ }
+ f0: H(*@This()),
+ f1: H(**@This()),
+ f2: H(***@This()),
+ };
+ const S = struct {
+ fn doTheTest(comptime T: type, comptime result: bool) void {
+ expectEqual(result, @sizeOf(T) > 0);
+ }
+ };
+ // Zero-sized type
+ S.doTheTest(u0, false);
+ S.doTheTest(*u0, false);
+ // Non byte-sized type
+ S.doTheTest(u1, true);
+ S.doTheTest(*u1, true);
+ // Regular type
+ S.doTheTest(u8, true);
+ S.doTheTest(*u8, true);
+ S.doTheTest(f32, true);
+ S.doTheTest(*f32, true);
+ // Container with ptr pointing to themselves
+ S.doTheTest(S0, true);
+ S.doTheTest(U0, true);
+ S.doTheTest(S1, true);
+ S.doTheTest(U1, true);
+}
diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig
new file mode 100644
index 0000000000..330e218f93
--- /dev/null
+++ b/test/behavior/slice.zig
@@ -0,0 +1,337 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqualSlices = std.testing.expectEqualSlices;
+const expectEqual = std.testing.expectEqual;
+const mem = std.mem;
+
+const x = @intToPtr([*]i32, 0x1000)[0..0x500];
+const y = x[0x100..];
+test "compile time slice of pointer to hard coded address" {
+ expect(@ptrToInt(x) == 0x1000);
+ expect(x.len == 0x500);
+
+ expect(@ptrToInt(y) == 0x1100);
+ expect(y.len == 0x400);
+}
+
+test "runtime safety lets us slice from len..len" {
+ var an_array = [_]u8{
+ 1,
+ 2,
+ 3,
+ };
+ expect(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), ""));
+}
+
+fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {
+ return a_slice[start..end];
+}
+
+test "implicitly cast array of size 0 to slice" {
+ var msg = [_]u8{};
+ assertLenIsZero(&msg);
+}
+
+fn assertLenIsZero(msg: []const u8) void {
+ expect(msg.len == 0);
+}
+
+test "C pointer" {
+ var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
+ var len: u32 = 10;
+ var slice = buf[0..len];
+ expectEqualSlices(u8, "kjdhfkjdhf", slice);
+}
+
+test "C pointer slice access" {
+ var buf: [10]u32 = [1]u32{42} ** 10;
+ const c_ptr = @ptrCast([*c]const u32, &buf);
+
+ var runtime_zero: usize = 0;
+ comptime expectEqual([]const u32, @TypeOf(c_ptr[runtime_zero..1]));
+ comptime expectEqual(*const [1]u32, @TypeOf(c_ptr[0..1]));
+
+ for (c_ptr[0..5]) |*cl| {
+ expectEqual(@as(u32, 42), cl.*);
+ }
+}
+
+fn sliceSum(comptime q: []const u8) i32 {
+ comptime var result = 0;
+ inline for (q) |item| {
+ result += item;
+ }
+ return result;
+}
+
+test "comptime slices are disambiguated" {
+ expect(sliceSum(&[_]u8{ 1, 2 }) == 3);
+ expect(sliceSum(&[_]u8{ 3, 4 }) == 7);
+}
+
+test "slice type with custom alignment" {
+ const LazilyResolvedType = struct {
+ anything: i32,
+ };
+ var slice: []align(32) LazilyResolvedType = undefined;
+ var array: [10]LazilyResolvedType align(32) = undefined;
+ slice = &array;
+ slice[1].anything = 42;
+ expect(array[1].anything == 42);
+}
+
+test "access len index of sentinel-terminated slice" {
+ const S = struct {
+ fn doTheTest() void {
+ var slice: [:0]const u8 = "hello";
+
+ expect(slice.len == 5);
+ expect(slice[5] == 0);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "obtaining a null terminated slice" {
+ // here we have a normal array
+ var buf: [50]u8 = undefined;
+
+ buf[0] = 'a';
+ buf[1] = 'b';
+ buf[2] = 'c';
+ buf[3] = 0;
+
+ // now we obtain a null terminated slice:
+ const ptr = buf[0..3 :0];
+
+ var runtime_len: usize = 3;
+ const ptr2 = buf[0..runtime_len :0];
+ // ptr2 is a null-terminated slice
+ comptime expect(@TypeOf(ptr2) == [:0]u8);
+ comptime expect(@TypeOf(ptr2[0..2]) == *[2]u8);
+ var runtime_zero: usize = 0;
+ comptime expect(@TypeOf(ptr2[runtime_zero..2]) == []u8);
+}
+
+test "empty array to slice" {
+ const S = struct {
+ fn doTheTest() void {
+ const empty: []align(16) u8 = &[_]u8{};
+ const align_1: []align(1) u8 = empty;
+ const align_4: []align(4) u8 = empty;
+ const align_16: []align(16) u8 = empty;
+ expectEqual(1, @typeInfo(@TypeOf(align_1)).Pointer.alignment);
+ expectEqual(4, @typeInfo(@TypeOf(align_4)).Pointer.alignment);
+ expectEqual(16, @typeInfo(@TypeOf(align_16)).Pointer.alignment);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "@ptrCast slice to pointer" {
+ const S = struct {
+ fn doTheTest() void {
+ var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff };
+ var slice: []u8 = &array;
+ var ptr = @ptrCast(*u16, slice);
+ expect(ptr.* == 65535);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "slice syntax resulting in pointer-to-array" {
+ const S = struct {
+ fn doTheTest() void {
+ testArray();
+ testArrayZ();
+ testArray0();
+ testArrayAlign();
+ testPointer();
+ testPointerZ();
+ testPointer0();
+ testPointerAlign();
+ testSlice();
+ testSliceZ();
+ testSlice0();
+ testSliceOpt();
+ testSliceAlign();
+ }
+
+ fn testArray() void {
+ var array = [5]u8{ 1, 2, 3, 4, 5 };
+ var slice = array[1..3];
+ comptime expect(@TypeOf(slice) == *[2]u8);
+ expect(slice[0] == 2);
+ expect(slice[1] == 3);
+ }
+
+ fn testArrayZ() void {
+ var array = [5:0]u8{ 1, 2, 3, 4, 5 };
+ comptime expect(@TypeOf(array[1..3]) == *[2]u8);
+ comptime expect(@TypeOf(array[1..5]) == *[4:0]u8);
+ comptime expect(@TypeOf(array[1..]) == *[4:0]u8);
+ comptime expect(@TypeOf(array[1..3 :4]) == *[2:4]u8);
+ }
+
+ fn testArray0() void {
+ {
+ var array = [0]u8{};
+ var slice = array[0..0];
+ comptime expect(@TypeOf(slice) == *[0]u8);
+ }
+ {
+ var array = [0:0]u8{};
+ var slice = array[0..0];
+ comptime expect(@TypeOf(slice) == *[0:0]u8);
+ expect(slice[0] == 0);
+ }
+ }
+
+ fn testArrayAlign() void {
+ var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
+ var slice = array[4..5];
+ comptime expect(@TypeOf(slice) == *align(4) [1]u8);
+ expect(slice[0] == 5);
+ comptime expect(@TypeOf(array[0..2]) == *align(4) [2]u8);
+ }
+
+ fn testPointer() void {
+ var array = [5]u8{ 1, 2, 3, 4, 5 };
+ var pointer: [*]u8 = &array;
+ var slice = pointer[1..3];
+ comptime expect(@TypeOf(slice) == *[2]u8);
+ expect(slice[0] == 2);
+ expect(slice[1] == 3);
+ }
+
+ fn testPointerZ() void {
+ var array = [5:0]u8{ 1, 2, 3, 4, 5 };
+ var pointer: [*:0]u8 = &array;
+ comptime expect(@TypeOf(pointer[1..3]) == *[2]u8);
+ comptime expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8);
+ }
+
+ fn testPointer0() void {
+ var pointer: [*]const u0 = &[1]u0{0};
+ var slice = pointer[0..1];
+ comptime expect(@TypeOf(slice) == *const [1]u0);
+ expect(slice[0] == 0);
+ }
+
+ fn testPointerAlign() void {
+ var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
+ var pointer: [*]align(4) u8 = &array;
+ var slice = pointer[4..5];
+ comptime expect(@TypeOf(slice) == *align(4) [1]u8);
+ expect(slice[0] == 5);
+ comptime expect(@TypeOf(pointer[0..2]) == *align(4) [2]u8);
+ }
+
+ fn testSlice() void {
+ var array = [5]u8{ 1, 2, 3, 4, 5 };
+ var src_slice: []u8 = &array;
+ var slice = src_slice[1..3];
+ comptime expect(@TypeOf(slice) == *[2]u8);
+ expect(slice[0] == 2);
+ expect(slice[1] == 3);
+ }
+
+ fn testSliceZ() void {
+ var array = [5:0]u8{ 1, 2, 3, 4, 5 };
+ var slice: [:0]u8 = &array;
+ comptime expect(@TypeOf(slice[1..3]) == *[2]u8);
+ comptime expect(@TypeOf(slice[1..]) == [:0]u8);
+ comptime expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8);
+ }
+
+ fn testSliceOpt() void {
+ var array: [2]u8 = [2]u8{ 1, 2 };
+ var slice: ?[]u8 = &array;
+ comptime expect(@TypeOf(&array, slice) == ?[]u8);
+ comptime expect(@TypeOf(slice.?[0..2]) == *[2]u8);
+ }
+
+ fn testSlice0() void {
+ {
+ var array = [0]u8{};
+ var src_slice: []u8 = &array;
+ var slice = src_slice[0..0];
+ comptime expect(@TypeOf(slice) == *[0]u8);
+ }
+ {
+ var array = [0:0]u8{};
+ var src_slice: [:0]u8 = &array;
+ var slice = src_slice[0..0];
+ comptime expect(@TypeOf(slice) == *[0]u8);
+ }
+ }
+
+ fn testSliceAlign() void {
+ var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
+ var src_slice: []align(4) u8 = &array;
+ var slice = src_slice[4..5];
+ comptime expect(@TypeOf(slice) == *align(4) [1]u8);
+ expect(slice[0] == 5);
+ comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8);
+ }
+
+ fn testConcatStrLiterals() void {
+ expectEqualSlices("a"[0..] ++ "b"[0..], "ab");
+ expectEqualSlices("a"[0..:0] ++ "b"[0..:0], "ab");
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "slice of hardcoded address to pointer" {
+ const S = struct {
+ fn doTheTest() void {
+ const pointer = @intToPtr([*]u8, 0x04)[0..2];
+ comptime expect(@TypeOf(pointer) == *[2]u8);
+ const slice: []const u8 = pointer;
+ expect(@ptrToInt(slice.ptr) == 4);
+ expect(slice.len == 2);
+ }
+ };
+
+ S.doTheTest();
+}
+
+test "type coercion of pointer to anon struct literal to pointer to slice" {
+ const S = struct {
+ const U = union{
+ a: u32,
+ b: bool,
+ c: []const u8,
+ };
+
+ fn doTheTest() void {
+ var x1: u8 = 42;
+ const t1 = &.{ x1, 56, 54 };
+ var slice1: []const u8 = t1;
+ expect(slice1.len == 3);
+ expect(slice1[0] == 42);
+ expect(slice1[1] == 56);
+ expect(slice1[2] == 54);
+
+ var x2: []const u8 = "hello";
+ const t2 = &.{ x2, ", ", "world!" };
+ // @compileLog(@TypeOf(t2));
+ var slice2: []const []const u8 = t2;
+ expect(slice2.len == 3);
+ expect(mem.eql(u8, slice2[0], "hello"));
+ expect(mem.eql(u8, slice2[1], ", "));
+ expect(mem.eql(u8, slice2[2], "world!"));
+ }
+ };
+ // S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/slice_sentinel_comptime.zig b/test/behavior/slice_sentinel_comptime.zig
new file mode 100644
index 0000000000..79da5d3c52
--- /dev/null
+++ b/test/behavior/slice_sentinel_comptime.zig
@@ -0,0 +1,199 @@
+test "comptime slice-sentinel in bounds (unterminated)" {
+ // array
+ comptime {
+ var target = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ const slice = target[0..3 :'d'];
+ }
+
+ // ptr_array
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target = &buf;
+ const slice = target[0..3 :'d'];
+ }
+
+ // vector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*]u8 = &buf;
+ const slice = target[0..3 :'d'];
+ }
+
+ // vector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*]u8 = @ptrCast([*]u8, &buf);
+ const slice = target[0..3 :'d'];
+ }
+
+ // cvector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*c]u8 = &buf;
+ const slice = target[0..3 :'d'];
+ }
+
+ // cvector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*c]u8 = @ptrCast([*c]u8, &buf);
+ const slice = target[0..3 :'d'];
+ }
+
+ // slice
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: []u8 = &buf;
+ const slice = target[0..3 :'d'];
+ }
+}
+
+test "comptime slice-sentinel in bounds (end,unterminated)" {
+ // array
+ comptime {
+ var target = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{0xff} ** 10;
+ const slice = target[0..13 :0xff];
+ }
+
+ // ptr_array
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{0xff} ** 10;
+ var target = &buf;
+ const slice = target[0..13 :0xff];
+ }
+
+ // vector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{0xff} ** 10;
+ var target: [*]u8 = &buf;
+ const slice = target[0..13 :0xff];
+ }
+
+ // vector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{0xff} ** 10;
+ var target: [*]u8 = @ptrCast([*]u8, &buf);
+ const slice = target[0..13 :0xff];
+ }
+
+ // cvector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{0xff} ** 10;
+ var target: [*c]u8 = &buf;
+ const slice = target[0..13 :0xff];
+ }
+
+ // cvector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{0xff} ** 10;
+ var target: [*c]u8 = @ptrCast([*c]u8, &buf);
+ const slice = target[0..13 :0xff];
+ }
+
+ // slice
+ comptime {
+ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{0xff} ** 10;
+ var target: []u8 = &buf;
+ const slice = target[0..13 :0xff];
+ }
+}
+
+test "comptime slice-sentinel in bounds (terminated)" {
+ // array
+ comptime {
+ var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ const slice = target[0..3 :'d'];
+ }
+
+ // ptr_array
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target = &buf;
+ const slice = target[0..3 :'d'];
+ }
+
+ // vector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*]u8 = &buf;
+ const slice = target[0..3 :'d'];
+ }
+
+ // vector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*]u8 = @ptrCast([*]u8, &buf);
+ const slice = target[0..3 :'d'];
+ }
+
+ // cvector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*c]u8 = &buf;
+ const slice = target[0..3 :'d'];
+ }
+
+ // cvector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*c]u8 = @ptrCast([*c]u8, &buf);
+ const slice = target[0..3 :'d'];
+ }
+
+ // slice
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: []u8 = &buf;
+ const slice = target[0..3 :'d'];
+ }
+}
+
+test "comptime slice-sentinel in bounds (on target sentinel)" {
+ // array
+ comptime {
+ var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ const slice = target[0..14 :0];
+ }
+
+ // ptr_array
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target = &buf;
+ const slice = target[0..14 :0];
+ }
+
+ // vector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*]u8 = &buf;
+ const slice = target[0..14 :0];
+ }
+
+ // vector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*]u8 = @ptrCast([*]u8, &buf);
+ const slice = target[0..14 :0];
+ }
+
+ // cvector_ConstPtrSpecialBaseArray
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*c]u8 = &buf;
+ const slice = target[0..14 :0];
+ }
+
+ // cvector_ConstPtrSpecialRef
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: [*c]u8 = @ptrCast([*c]u8, &buf);
+ const slice = target[0..14 :0];
+ }
+
+ // slice
+ comptime {
+ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10;
+ var target: []u8 = &buf;
+ const slice = target[0..14 :0];
+ }
+}
diff --git a/test/behavior/src.zig b/test/behavior/src.zig
new file mode 100644
index 0000000000..27fa144e54
--- /dev/null
+++ b/test/behavior/src.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "@src" {
+ doTheTest();
+}
+
+fn doTheTest() void {
+ const src = @src();
+
+ expect(src.line == 9);
+ expect(src.column == 17);
+ expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
+ expect(std.mem.endsWith(u8, src.file, "src.zig"));
+ expect(src.fn_name[src.fn_name.len] == 0);
+ expect(src.file[src.file.len] == 0);
+}
diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig
new file mode 100644
index 0000000000..aed52c53d8
--- /dev/null
+++ b/test/behavior/struct.zig
@@ -0,0 +1,945 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const native_endian = builtin.target.cpu.arch.endian();
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectEqualSlices = std.testing.expectEqualSlices;
+const maxInt = std.math.maxInt;
+const StructWithNoFields = struct {
+ fn add(a: i32, b: i32) i32 {
+ return a + b;
+ }
+};
+const empty_global_instance = StructWithNoFields{};
+
+top_level_field: i32,
+
+test "top level fields" {
+ var instance = @This(){
+ .top_level_field = 1234,
+ };
+ instance.top_level_field += 1;
+ expectEqual(@as(i32, 1235), instance.top_level_field);
+}
+
+test "call struct static method" {
+ const result = StructWithNoFields.add(3, 4);
+ expect(result == 7);
+}
+
+test "return empty struct instance" {
+ _ = returnEmptyStructInstance();
+}
+fn returnEmptyStructInstance() StructWithNoFields {
+ return empty_global_instance;
+}
+
+const should_be_11 = StructWithNoFields.add(5, 6);
+
+test "invoke static method in global scope" {
+ expect(should_be_11 == 11);
+}
+
+test "void struct fields" {
+ const foo = VoidStructFieldsFoo{
+ .a = void{},
+ .b = 1,
+ .c = void{},
+ };
+ expect(foo.b == 1);
+ expect(@sizeOf(VoidStructFieldsFoo) == 4);
+}
+const VoidStructFieldsFoo = struct {
+ a: void,
+ b: i32,
+ c: void,
+};
+
+test "structs" {
+ var foo: StructFoo = undefined;
+ @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
+ foo.a += 1;
+ foo.b = foo.a == 1;
+ testFoo(foo);
+ testMutation(&foo);
+ expect(foo.c == 100);
+}
+const StructFoo = struct {
+ a: i32,
+ b: bool,
+ c: f32,
+};
+fn testFoo(foo: StructFoo) void {
+ expect(foo.b);
+}
+fn testMutation(foo: *StructFoo) void {
+ foo.c = 100;
+}
+
+const Node = struct {
+ val: Val,
+ next: *Node,
+};
+
+const Val = struct {
+ x: i32,
+};
+
+test "struct point to self" {
+ var root: Node = undefined;
+ root.val.x = 1;
+
+ var node: Node = undefined;
+ node.next = &root;
+ node.val.x = 2;
+
+ root.next = &node;
+
+ expect(node.next.next.next.val.x == 1);
+}
+
+test "struct byval assign" {
+ var foo1: StructFoo = undefined;
+ var foo2: StructFoo = undefined;
+
+ foo1.a = 1234;
+ foo2.a = 0;
+ expect(foo2.a == 0);
+ foo2 = foo1;
+ expect(foo2.a == 1234);
+}
+
+fn structInitializer() void {
+ const val = Val{ .x = 42 };
+ expect(val.x == 42);
+}
+
+test "fn call of struct field" {
+ const Foo = struct {
+ ptr: fn () i32,
+ };
+ const S = struct {
+ fn aFunc() i32 {
+ return 13;
+ }
+
+ fn callStructField(foo: Foo) i32 {
+ return foo.ptr();
+ }
+ };
+
+ expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13);
+}
+
+test "store member function in variable" {
+ const instance = MemberFnTestFoo{ .x = 1234 };
+ const memberFn = MemberFnTestFoo.member;
+ const result = memberFn(instance);
+ expect(result == 1234);
+}
+const MemberFnTestFoo = struct {
+ x: i32,
+ fn member(foo: MemberFnTestFoo) i32 {
+ return foo.x;
+ }
+};
+
+test "call member function directly" {
+ const instance = MemberFnTestFoo{ .x = 1234 };
+ const result = MemberFnTestFoo.member(instance);
+ expect(result == 1234);
+}
+
+test "member functions" {
+ const r = MemberFnRand{ .seed = 1234 };
+ expect(r.getSeed() == 1234);
+}
+const MemberFnRand = struct {
+ seed: u32,
+ pub fn getSeed(r: *const MemberFnRand) u32 {
+ return r.seed;
+ }
+};
+
+test "return struct byval from function" {
+ const bar = makeBar(1234, 5678);
+ expect(bar.y == 5678);
+}
+const Bar = struct {
+ x: i32,
+ y: i32,
+};
+fn makeBar(x: i32, y: i32) Bar {
+ return Bar{
+ .x = x,
+ .y = y,
+ };
+}
+
+test "empty struct method call" {
+ const es = EmptyStruct{};
+ expect(es.method() == 1234);
+}
+const EmptyStruct = struct {
+ fn method(es: *const EmptyStruct) i32 {
+ return 1234;
+ }
+};
+
+test "return empty struct from fn" {
+ _ = testReturnEmptyStructFromFn();
+}
+const EmptyStruct2 = struct {};
+fn testReturnEmptyStructFromFn() EmptyStruct2 {
+ return EmptyStruct2{};
+}
+
+test "pass slice of empty struct to fn" {
+ expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1);
+}
+fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize {
+ return slice.len;
+}
+
+const APackedStruct = packed struct {
+ x: u8,
+ y: u8,
+};
+
+test "packed struct" {
+ var foo = APackedStruct{
+ .x = 1,
+ .y = 2,
+ };
+ foo.y += 1;
+ const four = foo.x + foo.y;
+ expect(four == 4);
+}
+
+const BitField1 = packed struct {
+ a: u3,
+ b: u3,
+ c: u2,
+};
+
+const bit_field_1 = BitField1{
+ .a = 1,
+ .b = 2,
+ .c = 3,
+};
+
+test "bit field access" {
+ var data = bit_field_1;
+ expect(getA(&data) == 1);
+ expect(getB(&data) == 2);
+ expect(getC(&data) == 3);
+ comptime expect(@sizeOf(BitField1) == 1);
+
+ data.b += 1;
+ expect(data.b == 3);
+
+ data.a += 1;
+ expect(data.a == 2);
+ expect(data.b == 3);
+}
+
+fn getA(data: *const BitField1) u3 {
+ return data.a;
+}
+
+fn getB(data: *const BitField1) u3 {
+ return data.b;
+}
+
+fn getC(data: *const BitField1) u2 {
+ return data.c;
+}
+
+const Foo24Bits = packed struct {
+ field: u24,
+};
+const Foo96Bits = packed struct {
+ a: u24,
+ b: u24,
+ c: u24,
+ d: u24,
+};
+
+test "packed struct 24bits" {
+ comptime {
+ expect(@sizeOf(Foo24Bits) == 4);
+ if (@sizeOf(usize) == 4) {
+ expect(@sizeOf(Foo96Bits) == 12);
+ } else {
+ expect(@sizeOf(Foo96Bits) == 16);
+ }
+ }
+
+ var value = Foo96Bits{
+ .a = 0,
+ .b = 0,
+ .c = 0,
+ .d = 0,
+ };
+ value.a += 1;
+ expect(value.a == 1);
+ expect(value.b == 0);
+ expect(value.c == 0);
+ expect(value.d == 0);
+
+ value.b += 1;
+ expect(value.a == 1);
+ expect(value.b == 1);
+ expect(value.c == 0);
+ expect(value.d == 0);
+
+ value.c += 1;
+ expect(value.a == 1);
+ expect(value.b == 1);
+ expect(value.c == 1);
+ expect(value.d == 0);
+
+ value.d += 1;
+ expect(value.a == 1);
+ expect(value.b == 1);
+ expect(value.c == 1);
+ expect(value.d == 1);
+}
+
+const Foo32Bits = packed struct {
+ field: u24,
+ pad: u8,
+};
+
+const FooArray24Bits = packed struct {
+ a: u16,
+ b: [2]Foo32Bits,
+ c: u16,
+};
+
+// TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512
+test "packed array 24bits" {
+ comptime {
+ expect(@sizeOf([9]Foo32Bits) == 9 * 4);
+ expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2);
+ }
+
+ var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1);
+ bytes[bytes.len - 1] = 0xaa;
+ const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0];
+ expect(ptr.a == 0);
+ expect(ptr.b[0].field == 0);
+ expect(ptr.b[1].field == 0);
+ expect(ptr.c == 0);
+
+ ptr.a = maxInt(u16);
+ expect(ptr.a == maxInt(u16));
+ expect(ptr.b[0].field == 0);
+ expect(ptr.b[1].field == 0);
+ expect(ptr.c == 0);
+
+ ptr.b[0].field = maxInt(u24);
+ expect(ptr.a == maxInt(u16));
+ expect(ptr.b[0].field == maxInt(u24));
+ expect(ptr.b[1].field == 0);
+ expect(ptr.c == 0);
+
+ ptr.b[1].field = maxInt(u24);
+ expect(ptr.a == maxInt(u16));
+ expect(ptr.b[0].field == maxInt(u24));
+ expect(ptr.b[1].field == maxInt(u24));
+ expect(ptr.c == 0);
+
+ ptr.c = maxInt(u16);
+ expect(ptr.a == maxInt(u16));
+ expect(ptr.b[0].field == maxInt(u24));
+ expect(ptr.b[1].field == maxInt(u24));
+ expect(ptr.c == maxInt(u16));
+
+ expect(bytes[bytes.len - 1] == 0xaa);
+}
+
+const FooStructAligned = packed struct {
+ a: u8,
+ b: u8,
+};
+
+const FooArrayOfAligned = packed struct {
+ a: [2]FooStructAligned,
+};
+
+test "aligned array of packed struct" {
+ comptime {
+ expect(@sizeOf(FooStructAligned) == 2);
+ expect(@sizeOf(FooArrayOfAligned) == 2 * 2);
+ }
+
+ var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned);
+ const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0];
+
+ expect(ptr.a[0].a == 0xbb);
+ expect(ptr.a[0].b == 0xbb);
+ expect(ptr.a[1].a == 0xbb);
+ expect(ptr.a[1].b == 0xbb);
+}
+
+test "runtime struct initialization of bitfield" {
+ const s1 = Nibbles{
+ .x = x1,
+ .y = x1,
+ };
+ const s2 = Nibbles{
+ .x = @intCast(u4, x2),
+ .y = @intCast(u4, x2),
+ };
+
+ expect(s1.x == x1);
+ expect(s1.y == x1);
+ expect(s2.x == @intCast(u4, x2));
+ expect(s2.y == @intCast(u4, x2));
+}
+
+var x1 = @as(u4, 1);
+var x2 = @as(u8, 2);
+
+const Nibbles = packed struct {
+ x: u4,
+ y: u4,
+};
+
+const Bitfields = packed struct {
+ f1: u16,
+ f2: u16,
+ f3: u8,
+ f4: u8,
+ f5: u4,
+ f6: u4,
+ f7: u8,
+};
+
+test "native bit field understands endianness" {
+ var all: u64 = if (native_endian != .Little)
+ 0x1111222233445677
+ else
+ 0x7765443322221111;
+ var bytes: [8]u8 = undefined;
+ @memcpy(&bytes, @ptrCast([*]u8, &all), 8);
+ var bitfields = @ptrCast(*Bitfields, &bytes).*;
+
+ expect(bitfields.f1 == 0x1111);
+ expect(bitfields.f2 == 0x2222);
+ expect(bitfields.f3 == 0x33);
+ expect(bitfields.f4 == 0x44);
+ expect(bitfields.f5 == 0x5);
+ expect(bitfields.f6 == 0x6);
+ expect(bitfields.f7 == 0x77);
+}
+
+test "align 1 field before self referential align 8 field as slice return type" {
+ const result = alloc(Expr);
+ expect(result.len == 0);
+}
+
+const Expr = union(enum) {
+ Literal: u8,
+ Question: *Expr,
+};
+
+fn alloc(comptime T: type) []T {
+ return &[_]T{};
+}
+
+test "call method with mutable reference to struct with no fields" {
+ const S = struct {
+ fn doC(s: *const @This()) bool {
+ return true;
+ }
+ fn do(s: *@This()) bool {
+ return true;
+ }
+ };
+
+ var s = S{};
+ expect(S.doC(&s));
+ expect(s.doC());
+ expect(S.do(&s));
+ expect(s.do());
+}
+
+test "implicit cast packed struct field to const ptr" {
+ const LevelUpMove = packed struct {
+ move_id: u9,
+ level: u7,
+
+ fn toInt(value: u7) u7 {
+ return value;
+ }
+ };
+
+ var lup: LevelUpMove = undefined;
+ lup.level = 12;
+ const res = LevelUpMove.toInt(lup.level);
+ expect(res == 12);
+}
+
+test "pointer to packed struct member in a stack variable" {
+ const S = packed struct {
+ a: u2,
+ b: u2,
+ };
+
+ var s = S{ .a = 2, .b = 0 };
+ var b_ptr = &s.b;
+ expect(s.b == 0);
+ b_ptr.* = 2;
+ expect(s.b == 2);
+}
+
+test "non-byte-aligned array inside packed struct" {
+ const Foo = packed struct {
+ a: bool,
+ b: [0x16]u8,
+ };
+ const S = struct {
+ fn bar(slice: []const u8) void {
+ expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu");
+ }
+ fn doTheTest() void {
+ var foo = Foo{
+ .a = true,
+ .b = "abcdefghijklmnopqurstu".*,
+ };
+ const value = foo.b;
+ bar(&value);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "packed struct with u0 field access" {
+ const S = packed struct {
+ f0: u0,
+ };
+ var s = S{ .f0 = 0 };
+ comptime expect(s.f0 == 0);
+}
+
+const S0 = struct {
+ bar: S1,
+
+ pub const S1 = struct {
+ value: u8,
+ };
+
+ fn init() @This() {
+ return S0{ .bar = S1{ .value = 123 } };
+ }
+};
+
+var g_foo: S0 = S0.init();
+
+test "access to global struct fields" {
+ g_foo.bar.value = 42;
+ expect(g_foo.bar.value == 42);
+}
+
+test "packed struct with fp fields" {
+ const S = packed struct {
+ data: [3]f32,
+
+ pub fn frob(self: *@This()) void {
+ self.data[0] += self.data[1] + self.data[2];
+ self.data[1] += self.data[0] + self.data[2];
+ self.data[2] += self.data[0] + self.data[1];
+ }
+ };
+
+ var s: S = undefined;
+ s.data[0] = 1.0;
+ s.data[1] = 2.0;
+ s.data[2] = 3.0;
+ s.frob();
+ expectEqual(@as(f32, 6.0), s.data[0]);
+ expectEqual(@as(f32, 11.0), s.data[1]);
+ expectEqual(@as(f32, 20.0), s.data[2]);
+}
+
+test "use within struct scope" {
+ const S = struct {
+ usingnamespace struct {
+ pub fn inner() i32 {
+ return 42;
+ }
+ };
+ };
+ expectEqual(@as(i32, 42), S.inner());
+}
+
+test "default struct initialization fields" {
+ const S = struct {
+ a: i32 = 1234,
+ b: i32,
+ };
+ const x = S{
+ .b = 5,
+ };
+ if (x.a + x.b != 1239) {
+ @compileError("it should be comptime known");
+ }
+ var five: i32 = 5;
+ const y = S{
+ .b = five,
+ };
+ expectEqual(1239, x.a + x.b);
+}
+
+test "fn with C calling convention returns struct by value" {
+ const S = struct {
+ fn entry() void {
+ var x = makeBar(10);
+ expectEqual(@as(i32, 10), x.handle);
+ }
+
+ const ExternBar = extern struct {
+ handle: i32,
+ };
+
+ fn makeBar(t: i32) callconv(.C) ExternBar {
+ return ExternBar{
+ .handle = t,
+ };
+ }
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+test "for loop over pointers to struct, getting field from struct pointer" {
+ const S = struct {
+ const Foo = struct {
+ name: []const u8,
+ };
+
+ var ok = true;
+
+ fn eql(a: []const u8) bool {
+ return true;
+ }
+
+ const ArrayList = struct {
+ fn toSlice(self: *ArrayList) []*Foo {
+ return @as([*]*Foo, undefined)[0..0];
+ }
+ };
+
+ fn doTheTest() void {
+ var objects: ArrayList = undefined;
+
+ for (objects.toSlice()) |obj| {
+ if (eql(obj.name)) {
+ ok = false;
+ }
+ }
+
+ expect(ok);
+ }
+ };
+ S.doTheTest();
+}
+
+test "zero-bit field in packed struct" {
+ const S = packed struct {
+ x: u10,
+ y: void,
+ };
+ var x: S = undefined;
+}
+
+test "struct field init with catch" {
+ const S = struct {
+ fn doTheTest() void {
+ var x: anyerror!isize = 1;
+ var req = Foo{
+ .field = x catch undefined,
+ };
+ expect(req.field == 1);
+ }
+
+ pub const Foo = extern struct {
+ field: isize,
+ };
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "packed struct with non-ABI-aligned field" {
+ const S = packed struct {
+ x: u9,
+ y: u183,
+ };
+ var s: S = undefined;
+ s.x = 1;
+ s.y = 42;
+ expect(s.x == 1);
+ expect(s.y == 42);
+}
+
+test "non-packed struct with u128 entry in union" {
+ const U = union(enum) {
+ Num: u128,
+ Void,
+ };
+
+ const S = struct {
+ f1: U,
+ f2: U,
+ };
+
+ var sx: S = undefined;
+ var s = &sx;
+ std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @byteOffsetOf(S, "f2"));
+ var v2 = U{ .Num = 123 };
+ s.f2 = v2;
+ std.testing.expect(s.f2.Num == 123);
+}
+
+test "packed struct field passed to generic function" {
+ const S = struct {
+ const P = packed struct {
+ b: u5,
+ g: u5,
+ r: u5,
+ a: u1,
+ };
+
+ fn genericReadPackedField(ptr: anytype) u5 {
+ return ptr.*;
+ }
+ };
+
+ var p: S.P = undefined;
+ p.b = 29;
+ var loaded = S.genericReadPackedField(&p.b);
+ expect(loaded == 29);
+}
+
+test "anonymous struct literal syntax" {
+ const S = struct {
+ const Point = struct {
+ x: i32,
+ y: i32,
+ };
+
+ fn doTheTest() void {
+ var p: Point = .{
+ .x = 1,
+ .y = 2,
+ };
+ expect(p.x == 1);
+ expect(p.y == 2);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "fully anonymous struct" {
+ const S = struct {
+ fn doTheTest() void {
+ dump(.{
+ .int = @as(u32, 1234),
+ .float = @as(f64, 12.34),
+ .b = true,
+ .s = "hi",
+ });
+ }
+ fn dump(args: anytype) void {
+ expect(args.int == 1234);
+ expect(args.float == 12.34);
+ expect(args.b);
+ expect(args.s[0] == 'h');
+ expect(args.s[1] == 'i');
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "fully anonymous list literal" {
+ const S = struct {
+ fn doTheTest() void {
+ dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" });
+ }
+ fn dump(args: anytype) void {
+ expect(args.@"0" == 1234);
+ expect(args.@"1" == 12.34);
+ expect(args.@"2");
+ expect(args.@"3"[0] == 'h');
+ expect(args.@"3"[1] == 'i');
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "anonymous struct literal assigned to variable" {
+ var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) };
+ expect(vec.@"0" == 22);
+ expect(vec.@"1" == 55);
+ expect(vec.@"2" == 99);
+}
+
+test "struct with var field" {
+ const Point = struct {
+ x: anytype,
+ y: anytype,
+ };
+ const pt = Point{
+ .x = 1,
+ .y = 2,
+ };
+ expect(pt.x == 1);
+ expect(pt.y == 2);
+}
+
+test "comptime struct field" {
+ const T = struct {
+ a: i32,
+ comptime b: i32 = 1234,
+ };
+
+ var foo: T = undefined;
+ comptime expect(foo.b == 1234);
+}
+
+test "anon struct literal field value initialized with fn call" {
+ const S = struct {
+ fn doTheTest() void {
+ var x = .{foo()};
+ expectEqualSlices(u8, x[0], "hi");
+ }
+ fn foo() []const u8 {
+ return "hi";
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "self-referencing struct via array member" {
+ const T = struct {
+ children: [1]*@This(),
+ };
+ var x: T = undefined;
+ x = T{ .children = .{&x} };
+ expect(x.children[0] == &x);
+}
+
+test "struct with union field" {
+ const Value = struct {
+ ref: u32 = 2,
+ kind: union(enum) {
+ None: usize,
+ Bool: bool,
+ },
+ };
+
+ var True = Value{
+ .kind = .{ .Bool = true },
+ };
+ expectEqual(@as(u32, 2), True.ref);
+ expectEqual(true, True.kind.Bool);
+}
+
+test "type coercion of anon struct literal to struct" {
+ const S = struct {
+ const S2 = struct {
+ A: u32,
+ B: []const u8,
+ C: void,
+ D: Foo = .{},
+ };
+
+ const Foo = struct {
+ field: i32 = 1234,
+ };
+
+ fn doTheTest() void {
+ var y: u32 = 42;
+ const t0 = .{ .A = 123, .B = "foo", .C = {} };
+ const t1 = .{ .A = y, .B = "foo", .C = {} };
+ const y0: S2 = t0;
+ var y1: S2 = t1;
+ expect(y0.A == 123);
+ expect(std.mem.eql(u8, y0.B, "foo"));
+ expect(y0.C == {});
+ expect(y0.D.field == 1234);
+ expect(y1.A == y);
+ expect(std.mem.eql(u8, y1.B, "foo"));
+ expect(y1.C == {});
+ expect(y1.D.field == 1234);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "type coercion of pointer to anon struct literal to pointer to struct" {
+ const S = struct {
+ const S2 = struct {
+ A: u32,
+ B: []const u8,
+ C: void,
+ D: Foo = .{},
+ };
+
+ const Foo = struct {
+ field: i32 = 1234,
+ };
+
+ fn doTheTest() void {
+ var y: u32 = 42;
+ const t0 = &.{ .A = 123, .B = "foo", .C = {} };
+ const t1 = &.{ .A = y, .B = "foo", .C = {} };
+ const y0: *const S2 = t0;
+ var y1: *const S2 = t1;
+ expect(y0.A == 123);
+ expect(std.mem.eql(u8, y0.B, "foo"));
+ expect(y0.C == {});
+ expect(y0.D.field == 1234);
+ expect(y1.A == y);
+ expect(std.mem.eql(u8, y1.B, "foo"));
+ expect(y1.C == {});
+ expect(y1.D.field == 1234);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "packed struct with undefined initializers" {
+ const S = struct {
+ const P = packed struct {
+ a: u3,
+ _a: u3 = undefined,
+ b: u3,
+ _b: u3 = undefined,
+ c: u3,
+ _c: u3 = undefined,
+ };
+
+ fn doTheTest() void {
+ var p: P = undefined;
+ p = P{ .a = 2, .b = 4, .c = 6 };
+ // Make sure the compiler doesn't touch the unprefixed fields.
+ expectEqual(@as(u3, 2), p.a);
+ expectEqual(@as(u3, 4), p.b);
+ expectEqual(@as(u3, 6), p.c);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/struct_contains_null_ptr_itself.zig b/test/behavior/struct_contains_null_ptr_itself.zig
new file mode 100644
index 0000000000..991d742cec
--- /dev/null
+++ b/test/behavior/struct_contains_null_ptr_itself.zig
@@ -0,0 +1,21 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "struct contains null pointer which contains original struct" {
+ var x: ?*NodeLineComment = null;
+ expect(x == null);
+}
+
+pub const Node = struct {
+ id: Id,
+ comment: ?*NodeLineComment,
+
+ pub const Id = enum {
+ Root,
+ LineComment,
+ };
+};
+
+pub const NodeLineComment = struct {
+ base: Node,
+};
diff --git a/test/behavior/struct_contains_slice_of_itself.zig b/test/behavior/struct_contains_slice_of_itself.zig
new file mode 100644
index 0000000000..14bf0320a2
--- /dev/null
+++ b/test/behavior/struct_contains_slice_of_itself.zig
@@ -0,0 +1,85 @@
+const expect = @import("std").testing.expect;
+
+const Node = struct {
+ payload: i32,
+ children: []Node,
+};
+
+const NodeAligned = struct {
+ payload: i32,
+ children: []align(@alignOf(NodeAligned)) NodeAligned,
+};
+
+test "struct contains slice of itself" {
+ var other_nodes = [_]Node{
+ Node{
+ .payload = 31,
+ .children = &[_]Node{},
+ },
+ Node{
+ .payload = 32,
+ .children = &[_]Node{},
+ },
+ };
+ var nodes = [_]Node{
+ Node{
+ .payload = 1,
+ .children = &[_]Node{},
+ },
+ Node{
+ .payload = 2,
+ .children = &[_]Node{},
+ },
+ Node{
+ .payload = 3,
+ .children = other_nodes[0..],
+ },
+ };
+ const root = Node{
+ .payload = 1234,
+ .children = nodes[0..],
+ };
+ expect(root.payload == 1234);
+ expect(root.children[0].payload == 1);
+ expect(root.children[1].payload == 2);
+ expect(root.children[2].payload == 3);
+ expect(root.children[2].children[0].payload == 31);
+ expect(root.children[2].children[1].payload == 32);
+}
+
+test "struct contains aligned slice of itself" {
+ var other_nodes = [_]NodeAligned{
+ NodeAligned{
+ .payload = 31,
+ .children = &[_]NodeAligned{},
+ },
+ NodeAligned{
+ .payload = 32,
+ .children = &[_]NodeAligned{},
+ },
+ };
+ var nodes = [_]NodeAligned{
+ NodeAligned{
+ .payload = 1,
+ .children = &[_]NodeAligned{},
+ },
+ NodeAligned{
+ .payload = 2,
+ .children = &[_]NodeAligned{},
+ },
+ NodeAligned{
+ .payload = 3,
+ .children = other_nodes[0..],
+ },
+ };
+ const root = NodeAligned{
+ .payload = 1234,
+ .children = nodes[0..],
+ };
+ expect(root.payload == 1234);
+ expect(root.children[0].payload == 1);
+ expect(root.children[1].payload == 2);
+ expect(root.children[2].payload == 3);
+ expect(root.children[2].children[0].payload == 31);
+ expect(root.children[2].children[1].payload == 32);
+}
diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig
new file mode 100644
index 0000000000..20ca0d3146
--- /dev/null
+++ b/test/behavior/switch.zig
@@ -0,0 +1,537 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectError = std.testing.expectError;
+const expectEqual = std.testing.expectEqual;
+
+test "switch with numbers" {
+ testSwitchWithNumbers(13);
+}
+
+fn testSwitchWithNumbers(x: u32) void {
+ const result = switch (x) {
+ 1, 2, 3, 4...8 => false,
+ 13 => true,
+ else => false,
+ };
+ expect(result);
+}
+
+test "switch with all ranges" {
+ expect(testSwitchWithAllRanges(50, 3) == 1);
+ expect(testSwitchWithAllRanges(101, 0) == 2);
+ expect(testSwitchWithAllRanges(300, 5) == 3);
+ 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 {
+ 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" {
+ nonConstSwitch(SwitchStatmentFoo.C);
+}
+fn nonConstSwitch(foo: SwitchStatmentFoo) void {
+ const val = switch (foo) {
+ SwitchStatmentFoo.A => @as(i32, 1),
+ SwitchStatmentFoo.B => 2,
+ SwitchStatmentFoo.C => 3,
+ SwitchStatmentFoo.D => 4,
+ };
+ expect(val == 3);
+}
+const SwitchStatmentFoo = enum {
+ A,
+ B,
+ C,
+ D,
+};
+
+test "switch prong with variable" {
+ switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 });
+ switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 });
+ switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} });
+}
+const SwitchProngWithVarEnum = union(enum) {
+ One: i32,
+ Two: f32,
+ Meh: void,
+};
+fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void {
+ switch (a) {
+ SwitchProngWithVarEnum.One => |x| {
+ expect(x == 13);
+ },
+ SwitchProngWithVarEnum.Two => |x| {
+ expect(x == 13.0);
+ },
+ SwitchProngWithVarEnum.Meh => |x| {
+ const v: void = x;
+ },
+ }
+}
+
+test "switch on enum using pointer capture" {
+ testSwitchEnumPtrCapture();
+ comptime testSwitchEnumPtrCapture();
+}
+
+fn testSwitchEnumPtrCapture() void {
+ var value = SwitchProngWithVarEnum{ .One = 1234 };
+ switch (value) {
+ SwitchProngWithVarEnum.One => |*x| x.* += 1,
+ else => unreachable,
+ }
+ switch (value) {
+ SwitchProngWithVarEnum.One => |x| 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),
+ };
+ 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" {
+ expect(!returnsFalse());
+}
+
+test "switch on type" {
+ expect(trueIfBoolFalseOtherwise(bool));
+ expect(!trueIfBoolFalseOtherwise(i32));
+}
+
+fn trueIfBoolFalseOtherwise(comptime T: type) bool {
+ return switch (T) {
+ bool => true,
+ else => false,
+ };
+}
+
+test "switch handles all cases of number" {
+ testSwitchHandleAllCases();
+ comptime testSwitchHandleAllCases();
+}
+
+fn testSwitchHandleAllCases() void {
+ expect(testSwitchHandleAllCasesExhaustive(0) == 3);
+ expect(testSwitchHandleAllCasesExhaustive(1) == 2);
+ expect(testSwitchHandleAllCasesExhaustive(2) == 1);
+ expect(testSwitchHandleAllCasesExhaustive(3) == 0);
+
+ expect(testSwitchHandleAllCasesRange(100) == 0);
+ expect(testSwitchHandleAllCasesRange(200) == 1);
+ expect(testSwitchHandleAllCasesRange(201) == 2);
+ expect(testSwitchHandleAllCasesRange(202) == 4);
+ 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" {
+ testAllProngsUnreachable();
+ comptime testAllProngsUnreachable();
+}
+
+fn testAllProngsUnreachable() void {
+ expect(switchWithUnreachable(1) == 2);
+ 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,
+ };
+ expect(x == 1);
+}
+
+test "switching on booleans" {
+ testSwitchOnBools();
+ comptime testSwitchOnBools();
+}
+
+fn testSwitchOnBools() void {
+ expect(testSwitchOnBoolsTrueAndFalse(true) == false);
+ expect(testSwitchOnBoolsTrueAndFalse(false) == true);
+
+ expect(testSwitchOnBoolsTrueWithElse(true) == false);
+ expect(testSwitchOnBoolsTrueWithElse(false) == true);
+
+ expect(testSwitchOnBoolsFalseWithElse(true) == false);
+ 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 => expect(val == 0),
+ }
+}
+
+test "undefined.u0" {
+ var val: u0 = undefined;
+ switch (val) {
+ 0 => 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| {
+ expect(x == 1234);
+ },
+ }
+}
+
+test "else prong of switch on error set excludes other cases" {
+ const S = struct {
+ fn doTheTest() void {
+ 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,
+ };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "switch prongs with error set cases make a new error set type for capture value" {
+ const S = struct {
+ fn doTheTest() void {
+ 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 => {},
+ };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "return result loc and then switch with range implicit casted to error union" {
+ const S = struct {
+ fn doTheTest() void {
+ expect((func(0xb) catch unreachable) == 0xb);
+ }
+ fn func(d: u8) anyerror!u8 {
+ return switch (d) {
+ 0xa...0xf => d,
+ else => unreachable,
+ };
+ }
+ };
+ S.doTheTest();
+ comptime 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| {
+ @panic("fail");
+ }
+ }
+ };
+ S.doTheTest(1);
+ comptime 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 {
+ doTheSwitch1(Union{ .A = 8 });
+ doTheSwitch2(Union{ .B = -8 });
+ }
+ fn doTheSwitch1(u: Union) void {
+ switch (u) {
+ .A, .C => |e| {
+ expect(@TypeOf(e) == usize);
+ expect(e == 8);
+ },
+ .B => |e| @panic("fail"),
+ }
+ }
+ fn doTheSwitch2(u: Union) void {
+ switch (u) {
+ .A, .C => |e| @panic("fail"),
+ .B => |e| {
+ expect(@TypeOf(e) == isize);
+ expect(e == -8);
+ },
+ }
+ }
+ };
+ S.doTheTest();
+ comptime 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;
+ doTheSwitch(u);
+ comptime doTheSwitch(u);
+ var v: u8 = 42;
+ doTheSwitch(v);
+ comptime doTheSwitch(v);
+ }
+ fn doTheSwitch(q: u8) void {
+ switch (q) {
+ 0...40 => |x| expect(x == 16),
+ 41, 42, 43 => |x| expect(x == 42),
+ else => expect(false),
+ }
+ }
+ };
+}
+
+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,
+ }
+ }
+ };
+
+ expect(1 == S.doTheTest(S.P1));
+ expect(2 == S.doTheTest(S.P2));
+ expect(3 == S.doTheTest(S.P3));
+ comptime expect(1 == S.doTheTest(S.P1));
+ comptime expect(2 == S.doTheTest(S.P2));
+ comptime 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;
+ expect(switch (some) {
+ else => |a| true,
+ });
+ }
+ };
+
+ S.doTheTest();
+ comptime 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 };
+ expectEqual(@as(u8, 42), value);
+ },
+ else => unreachable,
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/switch_prong_err_enum.zig b/test/behavior/switch_prong_err_enum.zig
new file mode 100644
index 0000000000..3593eabb5a
--- /dev/null
+++ b/test/behavior/switch_prong_err_enum.zig
@@ -0,0 +1,30 @@
+const expect = @import("std").testing.expect;
+
+var read_count: u64 = 0;
+
+fn readOnce() anyerror!u64 {
+ read_count += 1;
+ return read_count;
+}
+
+const FormValue = union(enum) {
+ Address: u64,
+ Other: bool,
+};
+
+fn doThing(form_id: u64) anyerror!FormValue {
+ return switch (form_id) {
+ 17 => FormValue{ .Address = try readOnce() },
+ else => error.InvalidDebugInfo,
+ };
+}
+
+test "switch prong returns error enum" {
+ switch (doThing(17) catch unreachable) {
+ FormValue.Address => |payload| {
+ expect(payload == 1);
+ },
+ else => unreachable,
+ }
+ expect(read_count == 1);
+}
diff --git a/test/behavior/switch_prong_implicit_cast.zig b/test/behavior/switch_prong_implicit_cast.zig
new file mode 100644
index 0000000000..da965915ca
--- /dev/null
+++ b/test/behavior/switch_prong_implicit_cast.zig
@@ -0,0 +1,22 @@
+const expect = @import("std").testing.expect;
+
+const FormValue = union(enum) {
+ One: void,
+ Two: bool,
+};
+
+fn foo(id: u64) !FormValue {
+ return switch (id) {
+ 2 => FormValue{ .Two = true },
+ 1 => FormValue{ .One = {} },
+ else => return error.Whatever,
+ };
+}
+
+test "switch prong implicit cast" {
+ const result = switch (foo(2) catch unreachable) {
+ FormValue.One => false,
+ FormValue.Two => |x| x,
+ };
+ expect(result);
+}
diff --git a/test/behavior/syntax.zig b/test/behavior/syntax.zig
new file mode 100644
index 0000000000..12df8a4315
--- /dev/null
+++ b/test/behavior/syntax.zig
@@ -0,0 +1,68 @@
+// Test trailing comma syntax
+// zig fmt: off
+
+extern var a: c_int;
+extern "c" var b: c_int;
+export var c: c_int = 0;
+threadlocal var d: c_int;
+extern threadlocal var e: c_int;
+extern "c" threadlocal var f: c_int;
+export threadlocal var g: c_int = 0;
+
+const struct_trailing_comma = struct { x: i32, y: i32, };
+const struct_no_comma = struct { x: i32, y: i32 };
+const struct_fn_no_comma = struct { fn m() void {} y: i32 };
+
+const enum_no_comma = enum { A, B };
+
+fn container_init() void {
+ const S = struct { x: i32, y: i32 };
+ _ = S { .x = 1, .y = 2 };
+ _ = S { .x = 1, .y = 2, };
+}
+
+fn type_expr_return1() if (true) A {}
+fn type_expr_return2() for (true) |_| A {}
+fn type_expr_return3() while (true) A {}
+fn type_expr_return4() comptime A {}
+
+fn switch_cases(x: i32) void {
+ switch (x) {
+ 1,2,3 => {},
+ 4,5, => {},
+ 6...8, => {},
+ else => {},
+ }
+}
+
+fn switch_prongs(x: i32) void {
+ switch (x) {
+ 0 => {},
+ else => {},
+ }
+ switch (x) {
+ 0 => {},
+ else => {}
+ }
+}
+
+const fn_no_comma = fn(i32, i32)void;
+const fn_trailing_comma = fn(i32, i32,)void;
+
+fn fn_calls() void {
+ fn add(x: i32, y: i32,) i32 { x + y };
+ _ = add(1, 2);
+ _ = add(1, 2,);
+}
+
+fn asm_lists() void {
+ if (false) { // Build AST but don't analyze
+ asm ("not real assembly"
+ :[a] "x" (x),);
+ asm ("not real assembly"
+ :[a] "x" (->i32),:[a] "x" (1),);
+ asm ("still not real assembly"
+ :::"a","b",);
+ }
+}
+
diff --git a/test/behavior/this.zig b/test/behavior/this.zig
new file mode 100644
index 0000000000..927c0808ea
--- /dev/null
+++ b/test/behavior/this.zig
@@ -0,0 +1,34 @@
+const expect = @import("std").testing.expect;
+
+const module = @This();
+
+fn Point(comptime T: type) type {
+ return struct {
+ const Self = @This();
+ x: T,
+ y: T,
+
+ fn addOne(self: *Self) void {
+ self.x += 1;
+ self.y += 1;
+ }
+ };
+}
+
+fn add(x: i32, y: i32) i32 {
+ return x + y;
+}
+
+test "this refer to module call private fn" {
+ expect(module.add(1, 2) == 3);
+}
+
+test "this refer to container" {
+ var pt = Point(i32){
+ .x = 12,
+ .y = 34,
+ };
+ pt.addOne();
+ expect(pt.x == 13);
+ expect(pt.y == 35);
+}
diff --git a/test/behavior/translate_c_macros.h b/test/behavior/translate_c_macros.h
new file mode 100644
index 0000000000..d3a6ac1ea5
--- /dev/null
+++ b/test/behavior/translate_c_macros.h
@@ -0,0 +1,18 @@
+// initializer list expression
+typedef struct Color {
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+ unsigned char a;
+} Color;
+#define CLITERAL(type) (type)
+#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
+
+#define MY_SIZEOF(x) ((int)sizeof(x))
+#define MY_SIZEOF2(x) ((int)sizeof x)
+
+struct Foo {
+ int a;
+};
+
+#define SIZE_OF_FOO sizeof(struct Foo)
diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig
new file mode 100644
index 0000000000..bc0d3b583e
--- /dev/null
+++ b/test/behavior/translate_c_macros.zig
@@ -0,0 +1,22 @@
+const expect = @import("std").testing.expect;
+const expectEqual = @import("std").testing.expectEqual;
+
+const h = @cImport(@cInclude("behavior/translate_c_macros.h"));
+
+test "initializer list expression" {
+ expectEqual(h.Color{
+ .r = 200,
+ .g = 200,
+ .b = 200,
+ .a = 255,
+ }, h.LIGHTGRAY);
+}
+
+test "sizeof in macros" {
+ expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF(u32));
+ expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF2(u32));
+}
+
+test "reference to a struct type" {
+ expectEqual(@sizeOf(h.struct_Foo), h.SIZE_OF_FOO);
+}
diff --git a/test/behavior/truncate.zig b/test/behavior/truncate.zig
new file mode 100644
index 0000000000..099b6c3359
--- /dev/null
+++ b/test/behavior/truncate.zig
@@ -0,0 +1,36 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "truncate u0 to larger integer allowed and has comptime known result" {
+ var x: u0 = 0;
+ const y = @truncate(u8, x);
+ comptime expect(y == 0);
+}
+
+test "truncate.u0.literal" {
+ var z = @truncate(u0, 0);
+ expect(z == 0);
+}
+
+test "truncate.u0.const" {
+ const c0: usize = 0;
+ var z = @truncate(u0, c0);
+ expect(z == 0);
+}
+
+test "truncate.u0.var" {
+ var d: u8 = 2;
+ var z = @truncate(u0, d);
+ expect(z == 0);
+}
+
+test "truncate sign mismatch but comptime known so it works anyway" {
+ const x: u32 = 10;
+ var result = @truncate(i8, x);
+ expect(result == 10);
+}
+
+test "truncate on comptime integer" {
+ var x = @truncate(u16, 9999);
+ expect(x == 9999);
+}
diff --git a/test/behavior/try.zig b/test/behavior/try.zig
new file mode 100644
index 0000000000..9e93183c3b
--- /dev/null
+++ b/test/behavior/try.zig
@@ -0,0 +1,43 @@
+const expect = @import("std").testing.expect;
+
+test "try on error union" {
+ tryOnErrorUnionImpl();
+ comptime tryOnErrorUnionImpl();
+}
+
+fn tryOnErrorUnionImpl() void {
+ const x = if (returnsTen()) |val| val + 1 else |err| switch (err) {
+ error.ItBroke, error.NoMem => 1,
+ error.CrappedOut => @as(i32, 2),
+ else => unreachable,
+ };
+ expect(x == 11);
+}
+
+fn returnsTen() anyerror!i32 {
+ return 10;
+}
+
+test "try without vars" {
+ const result1 = if (failIfTrue(true)) 1 else |_| @as(i32, 2);
+ expect(result1 == 2);
+
+ const result2 = if (failIfTrue(false)) 1 else |_| @as(i32, 2);
+ expect(result2 == 1);
+}
+
+fn failIfTrue(ok: bool) anyerror!void {
+ if (ok) {
+ return error.ItBroke;
+ } else {
+ return;
+ }
+}
+
+test "try then not executed with assignment" {
+ if (failIfTrue(true)) {
+ unreachable;
+ } else |err| {
+ expect(err == error.ItBroke);
+ }
+}
diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig
new file mode 100644
index 0000000000..4eb5b73abb
--- /dev/null
+++ b/test/behavior/tuple.zig
@@ -0,0 +1,113 @@
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
+
+test "tuple concatenation" {
+ const S = struct {
+ fn doTheTest() void {
+ var a: i32 = 1;
+ var b: i32 = 2;
+ var x = .{a};
+ var y = .{b};
+ var c = x ++ y;
+ expectEqual(@as(i32, 1), c[0]);
+ expectEqual(@as(i32, 2), c[1]);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "tuple multiplication" {
+ const S = struct {
+ fn doTheTest() void {
+ {
+ const t = .{} ** 4;
+ expectEqual(0, @typeInfo(@TypeOf(t)).Struct.fields.len);
+ }
+ {
+ const t = .{'a'} ** 4;
+ expectEqual(4, @typeInfo(@TypeOf(t)).Struct.fields.len);
+ inline for (t) |x| expectEqual('a', x);
+ }
+ {
+ const t = .{ 1, 2, 3 } ** 4;
+ expectEqual(12, @typeInfo(@TypeOf(t)).Struct.fields.len);
+ inline for (t) |x, i| expectEqual(1 + i % 3, x);
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+
+ const T = struct {
+ fn consume_tuple(tuple: anytype, len: usize) void {
+ expect(tuple.len == len);
+ }
+
+ fn doTheTest() void {
+ const t1 = .{};
+
+ var rt_var: u8 = 42;
+ const t2 = .{rt_var} ++ .{};
+
+ expect(t2.len == 1);
+ expect(t2.@"0" == rt_var);
+ expect(t2.@"0" == 42);
+ expect(&t2.@"0" != &rt_var);
+
+ consume_tuple(t1 ++ t1, 0);
+ consume_tuple(.{} ++ .{}, 0);
+ consume_tuple(.{0} ++ .{}, 1);
+ consume_tuple(.{0} ++ .{1}, 2);
+ consume_tuple(.{ 0, 1, 2 } ++ .{ u8, 1, noreturn }, 6);
+ consume_tuple(t2 ++ t1, 1);
+ consume_tuple(t1 ++ t2, 1);
+ consume_tuple(t2 ++ t2, 2);
+ consume_tuple(.{rt_var} ++ .{}, 1);
+ consume_tuple(.{rt_var} ++ t1, 1);
+ consume_tuple(.{} ++ .{rt_var}, 1);
+ consume_tuple(t2 ++ .{void}, 2);
+ consume_tuple(t2 ++ .{0}, 2);
+ consume_tuple(.{0} ++ t2, 2);
+ consume_tuple(.{void} ++ t2, 2);
+ consume_tuple(.{u8} ++ .{rt_var} ++ .{true}, 3);
+ }
+ };
+
+ T.doTheTest();
+ comptime T.doTheTest();
+}
+
+test "pass tuple to comptime var parameter" {
+ const S = struct {
+ fn Foo(comptime args: anytype) void {
+ expect(args[0] == 1);
+ }
+
+ fn doTheTest() void {
+ Foo(.{1});
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "tuple initializer for var" {
+ const S = struct {
+ fn doTheTest() void {
+ const Bytes = struct {
+ id: usize,
+ };
+
+ var tmp = .{
+ .id = @as(usize, 2),
+ .name = Bytes{ .id = 20 },
+ };
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/type.zig b/test/behavior/type.zig
new file mode 100644
index 0000000000..60c9117991
--- /dev/null
+++ b/test/behavior/type.zig
@@ -0,0 +1,453 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const TypeInfo = std.builtin.TypeInfo;
+const testing = std.testing;
+
+fn testTypes(comptime types: []const type) void {
+ inline for (types) |testType| {
+ testing.expect(testType == @Type(@typeInfo(testType)));
+ }
+}
+
+test "Type.MetaType" {
+ testing.expect(type == @Type(TypeInfo{ .Type = undefined }));
+ testTypes(&[_]type{type});
+}
+
+test "Type.Void" {
+ testing.expect(void == @Type(TypeInfo{ .Void = undefined }));
+ testTypes(&[_]type{void});
+}
+
+test "Type.Bool" {
+ testing.expect(bool == @Type(TypeInfo{ .Bool = undefined }));
+ testTypes(&[_]type{bool});
+}
+
+test "Type.NoReturn" {
+ testing.expect(noreturn == @Type(TypeInfo{ .NoReturn = undefined }));
+ testTypes(&[_]type{noreturn});
+}
+
+test "Type.Int" {
+ testing.expect(u1 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .signedness = .unsigned, .bits = 1 } }));
+ testing.expect(i1 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .signedness = .signed, .bits = 1 } }));
+ testing.expect(u8 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .signedness = .unsigned, .bits = 8 } }));
+ testing.expect(i8 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .signedness = .signed, .bits = 8 } }));
+ testing.expect(u64 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .signedness = .unsigned, .bits = 64 } }));
+ testing.expect(i64 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .signedness = .signed, .bits = 64 } }));
+ testTypes(&[_]type{ u8, u32, i64 });
+}
+
+test "Type.Float" {
+ testing.expect(f16 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 16 } }));
+ testing.expect(f32 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 32 } }));
+ testing.expect(f64 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 64 } }));
+ testing.expect(f128 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 128 } }));
+ testTypes(&[_]type{ f16, f32, f64, f128 });
+}
+
+test "Type.Pointer" {
+ testTypes(&[_]type{
+ // One Value Pointer Types
+ *u8, *const u8,
+ *volatile u8, *const volatile u8,
+ *align(4) u8, *align(4) const u8,
+ *align(4) volatile u8, *align(4) const volatile u8,
+ *align(8) u8, *align(8) const u8,
+ *align(8) volatile u8, *align(8) const volatile u8,
+ *allowzero u8, *allowzero const u8,
+ *allowzero volatile u8, *allowzero const volatile u8,
+ *allowzero align(4) u8, *allowzero align(4) const u8,
+ *allowzero align(4) volatile u8, *allowzero align(4) const volatile u8,
+ // Many Values Pointer Types
+ [*]u8, [*]const u8,
+ [*]volatile u8, [*]const volatile u8,
+ [*]align(4) u8, [*]align(4) const u8,
+ [*]align(4) volatile u8, [*]align(4) const volatile u8,
+ [*]align(8) u8, [*]align(8) const u8,
+ [*]align(8) volatile u8, [*]align(8) const volatile u8,
+ [*]allowzero u8, [*]allowzero const u8,
+ [*]allowzero volatile u8, [*]allowzero const volatile u8,
+ [*]allowzero align(4) u8, [*]allowzero align(4) const u8,
+ [*]allowzero align(4) volatile u8, [*]allowzero align(4) const volatile u8,
+ // Slice Types
+ []u8, []const u8,
+ []volatile u8, []const volatile u8,
+ []align(4) u8, []align(4) const u8,
+ []align(4) volatile u8, []align(4) const volatile u8,
+ []align(8) u8, []align(8) const u8,
+ []align(8) volatile u8, []align(8) const volatile u8,
+ []allowzero u8, []allowzero const u8,
+ []allowzero volatile u8, []allowzero const volatile u8,
+ []allowzero align(4) u8, []allowzero align(4) const u8,
+ []allowzero align(4) volatile u8, []allowzero align(4) const volatile u8,
+ // C Pointer Types
+ [*c]u8, [*c]const u8,
+ [*c]volatile u8, [*c]const volatile u8,
+ [*c]align(4) u8, [*c]align(4) const u8,
+ [*c]align(4) volatile u8, [*c]align(4) const volatile u8,
+ [*c]align(8) u8, [*c]align(8) const u8,
+ [*c]align(8) volatile u8, [*c]align(8) const volatile u8,
+ });
+}
+
+test "Type.Array" {
+ testing.expect([123]u8 == @Type(TypeInfo{
+ .Array = TypeInfo.Array{
+ .len = 123,
+ .child = u8,
+ .sentinel = null,
+ },
+ }));
+ testing.expect([2]u32 == @Type(TypeInfo{
+ .Array = TypeInfo.Array{
+ .len = 2,
+ .child = u32,
+ .sentinel = null,
+ },
+ }));
+ testing.expect([2:0]u32 == @Type(TypeInfo{
+ .Array = TypeInfo.Array{
+ .len = 2,
+ .child = u32,
+ .sentinel = 0,
+ },
+ }));
+ testTypes(&[_]type{ [1]u8, [30]usize, [7]bool });
+}
+
+test "Type.ComptimeFloat" {
+ testTypes(&[_]type{comptime_float});
+}
+test "Type.ComptimeInt" {
+ testTypes(&[_]type{comptime_int});
+}
+test "Type.Undefined" {
+ testTypes(&[_]type{@TypeOf(undefined)});
+}
+test "Type.Null" {
+ testTypes(&[_]type{@TypeOf(null)});
+}
+test "@Type create slice with null sentinel" {
+ const Slice = @Type(TypeInfo{
+ .Pointer = .{
+ .size = .Slice,
+ .is_const = true,
+ .is_volatile = false,
+ .is_allowzero = false,
+ .alignment = 8,
+ .child = *i32,
+ .sentinel = null,
+ },
+ });
+ testing.expect(Slice == []align(8) const *i32);
+}
+test "@Type picks up the sentinel value from TypeInfo" {
+ testTypes(&[_]type{
+ [11:0]u8, [4:10]u8,
+ [*:0]u8, [*:0]const u8,
+ [*:0]volatile u8, [*:0]const volatile u8,
+ [*:0]align(4) u8, [*:0]align(4) const u8,
+ [*:0]align(4) volatile u8, [*:0]align(4) const volatile u8,
+ [*:0]align(8) u8, [*:0]align(8) const u8,
+ [*:0]align(8) volatile u8, [*:0]align(8) const volatile u8,
+ [*:0]allowzero u8, [*:0]allowzero const u8,
+ [*:0]allowzero volatile u8, [*:0]allowzero const volatile u8,
+ [*:0]allowzero align(4) u8, [*:0]allowzero align(4) const u8,
+ [*:0]allowzero align(4) volatile u8, [*:0]allowzero align(4) const volatile u8,
+ [*:5]allowzero align(4) volatile u8, [*:5]allowzero align(4) const volatile u8,
+ [:0]u8, [:0]const u8,
+ [:0]volatile u8, [:0]const volatile u8,
+ [:0]align(4) u8, [:0]align(4) const u8,
+ [:0]align(4) volatile u8, [:0]align(4) const volatile u8,
+ [:0]align(8) u8, [:0]align(8) const u8,
+ [:0]align(8) volatile u8, [:0]align(8) const volatile u8,
+ [:0]allowzero u8, [:0]allowzero const u8,
+ [:0]allowzero volatile u8, [:0]allowzero const volatile u8,
+ [:0]allowzero align(4) u8, [:0]allowzero align(4) const u8,
+ [:0]allowzero align(4) volatile u8, [:0]allowzero align(4) const volatile u8,
+ [:4]allowzero align(4) volatile u8, [:4]allowzero align(4) const volatile u8,
+ });
+}
+
+test "Type.Optional" {
+ testTypes(&[_]type{
+ ?u8,
+ ?*u8,
+ ?[]u8,
+ ?[*]u8,
+ ?[*c]u8,
+ });
+}
+
+test "Type.ErrorUnion" {
+ testTypes(&[_]type{
+ error{}!void,
+ error{Error}!void,
+ });
+}
+
+test "Type.Opaque" {
+ const Opaque = @Type(.{
+ .Opaque = .{
+ .decls = &[_]TypeInfo.Declaration{},
+ },
+ });
+ testing.expect(Opaque != opaque {});
+ testing.expectEqualSlices(
+ TypeInfo.Declaration,
+ &[_]TypeInfo.Declaration{},
+ @typeInfo(Opaque).Opaque.decls,
+ );
+}
+
+test "Type.Vector" {
+ testTypes(&[_]type{
+ @Vector(0, u8),
+ @Vector(4, u8),
+ @Vector(8, *u8),
+ std.meta.Vector(0, u8),
+ std.meta.Vector(4, u8),
+ std.meta.Vector(8, *u8),
+ });
+}
+
+test "Type.AnyFrame" {
+ testTypes(&[_]type{
+ anyframe,
+ anyframe->u8,
+ anyframe->anyframe->u8,
+ });
+}
+
+test "Type.EnumLiteral" {
+ testTypes(&[_]type{
+ @TypeOf(.Dummy),
+ });
+}
+
+fn add(a: i32, b: i32) i32 {
+ return a + b;
+}
+
+test "Type.Frame" {
+ testTypes(&[_]type{
+ @Frame(add),
+ });
+}
+
+test "Type.ErrorSet" {
+ // error sets don't compare equal so just check if they compile
+ _ = @Type(@typeInfo(error{}));
+ _ = @Type(@typeInfo(error{A}));
+ _ = @Type(@typeInfo(error{ A, B, C }));
+}
+
+test "Type.Struct" {
+ const A = @Type(@typeInfo(struct { x: u8, y: u32 }));
+ const infoA = @typeInfo(A).Struct;
+ testing.expectEqual(TypeInfo.ContainerLayout.Auto, infoA.layout);
+ testing.expectEqualSlices(u8, "x", infoA.fields[0].name);
+ testing.expectEqual(u8, infoA.fields[0].field_type);
+ testing.expectEqual(@as(?u8, null), infoA.fields[0].default_value);
+ testing.expectEqualSlices(u8, "y", infoA.fields[1].name);
+ testing.expectEqual(u32, infoA.fields[1].field_type);
+ testing.expectEqual(@as(?u32, null), infoA.fields[1].default_value);
+ testing.expectEqualSlices(TypeInfo.Declaration, &[_]TypeInfo.Declaration{}, infoA.decls);
+ testing.expectEqual(@as(bool, false), infoA.is_tuple);
+
+ var a = A{ .x = 0, .y = 1 };
+ testing.expectEqual(@as(u8, 0), a.x);
+ testing.expectEqual(@as(u32, 1), a.y);
+ a.y += 1;
+ testing.expectEqual(@as(u32, 2), a.y);
+
+ const B = @Type(@typeInfo(extern struct { x: u8, y: u32 = 5 }));
+ const infoB = @typeInfo(B).Struct;
+ testing.expectEqual(TypeInfo.ContainerLayout.Extern, infoB.layout);
+ testing.expectEqualSlices(u8, "x", infoB.fields[0].name);
+ testing.expectEqual(u8, infoB.fields[0].field_type);
+ testing.expectEqual(@as(?u8, null), infoB.fields[0].default_value);
+ testing.expectEqualSlices(u8, "y", infoB.fields[1].name);
+ testing.expectEqual(u32, infoB.fields[1].field_type);
+ testing.expectEqual(@as(?u32, 5), infoB.fields[1].default_value);
+ testing.expectEqual(@as(usize, 0), infoB.decls.len);
+ testing.expectEqual(@as(bool, false), infoB.is_tuple);
+
+ const C = @Type(@typeInfo(packed struct { x: u8 = 3, y: u32 = 5 }));
+ const infoC = @typeInfo(C).Struct;
+ testing.expectEqual(TypeInfo.ContainerLayout.Packed, infoC.layout);
+ testing.expectEqualSlices(u8, "x", infoC.fields[0].name);
+ testing.expectEqual(u8, infoC.fields[0].field_type);
+ testing.expectEqual(@as(?u8, 3), infoC.fields[0].default_value);
+ testing.expectEqualSlices(u8, "y", infoC.fields[1].name);
+ testing.expectEqual(u32, infoC.fields[1].field_type);
+ testing.expectEqual(@as(?u32, 5), infoC.fields[1].default_value);
+ testing.expectEqual(@as(usize, 0), infoC.decls.len);
+ testing.expectEqual(@as(bool, false), infoC.is_tuple);
+}
+
+test "Type.Enum" {
+ const Foo = @Type(.{
+ .Enum = .{
+ .layout = .Auto,
+ .tag_type = u8,
+ .fields = &[_]TypeInfo.EnumField{
+ .{ .name = "a", .value = 1 },
+ .{ .name = "b", .value = 5 },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ .is_exhaustive = true,
+ },
+ });
+ testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive);
+ testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a));
+ testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b));
+ const Bar = @Type(.{
+ .Enum = .{
+ .layout = .Extern,
+ .tag_type = u32,
+ .fields = &[_]TypeInfo.EnumField{
+ .{ .name = "a", .value = 1 },
+ .{ .name = "b", .value = 5 },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ .is_exhaustive = false,
+ },
+ });
+ testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive);
+ testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a));
+ testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b));
+ testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6)));
+}
+
+test "Type.Union" {
+ const Untagged = @Type(.{
+ .Union = .{
+ .layout = .Auto,
+ .tag_type = null,
+ .fields = &[_]TypeInfo.UnionField{
+ .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) },
+ .{ .name = "float", .field_type = f32, .alignment = @alignOf(f32) },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ },
+ });
+ var untagged = Untagged{ .int = 1 };
+ untagged.float = 2.0;
+ untagged.int = 3;
+ testing.expectEqual(@as(i32, 3), untagged.int);
+
+ const PackedUntagged = @Type(.{
+ .Union = .{
+ .layout = .Packed,
+ .tag_type = null,
+ .fields = &[_]TypeInfo.UnionField{
+ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) },
+ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ },
+ });
+ var packed_untagged = PackedUntagged{ .signed = -1 };
+ testing.expectEqual(@as(i32, -1), packed_untagged.signed);
+ testing.expectEqual(~@as(u32, 0), packed_untagged.unsigned);
+
+ const Tag = @Type(.{
+ .Enum = .{
+ .layout = .Auto,
+ .tag_type = u1,
+ .fields = &[_]TypeInfo.EnumField{
+ .{ .name = "signed", .value = 0 },
+ .{ .name = "unsigned", .value = 1 },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ .is_exhaustive = true,
+ },
+ });
+ const Tagged = @Type(.{
+ .Union = .{
+ .layout = .Auto,
+ .tag_type = Tag,
+ .fields = &[_]TypeInfo.UnionField{
+ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) },
+ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ },
+ });
+ var tagged = Tagged{ .signed = -1 };
+ testing.expectEqual(Tag.signed, tagged);
+ tagged = .{ .unsigned = 1 };
+ testing.expectEqual(Tag.unsigned, tagged);
+}
+
+test "Type.Union from Type.Enum" {
+ const Tag = @Type(.{
+ .Enum = .{
+ .layout = .Auto,
+ .tag_type = u0,
+ .fields = &[_]TypeInfo.EnumField{
+ .{ .name = "working_as_expected", .value = 0 },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ .is_exhaustive = true,
+ },
+ });
+ const T = @Type(.{
+ .Union = .{
+ .layout = .Auto,
+ .tag_type = Tag,
+ .fields = &[_]TypeInfo.UnionField{
+ .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ },
+ });
+ _ = T;
+ _ = @typeInfo(T).Union;
+}
+
+test "Type.Union from regular enum" {
+ const E = enum { working_as_expected = 0 };
+ const T = @Type(.{
+ .Union = .{
+ .layout = .Auto,
+ .tag_type = E,
+ .fields = &[_]TypeInfo.UnionField{
+ .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) },
+ },
+ .decls = &[_]TypeInfo.Declaration{},
+ },
+ });
+ _ = T;
+ _ = @typeInfo(T).Union;
+}
+
+test "Type.Fn" {
+ // wasm doesn't support align attributes on functions
+ if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest;
+
+ const foo = struct {
+ fn func(a: usize, b: bool) align(4) callconv(.C) usize {
+ return 0;
+ }
+ }.func;
+ const Foo = @Type(@typeInfo(@TypeOf(foo)));
+ const foo_2: Foo = foo;
+}
+
+test "Type.BoundFn" {
+ // wasm doesn't support align attributes on functions
+ if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest;
+
+ const TestStruct = packed struct {
+ pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void {}
+ };
+ const test_instance: TestStruct = undefined;
+ testing.expect(std.meta.eql(
+ @typeName(@TypeOf(test_instance.foo)),
+ @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))),
+ ));
+}
diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig
new file mode 100644
index 0000000000..2315290466
--- /dev/null
+++ b/test/behavior/type_info.zig
@@ -0,0 +1,485 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const mem = std.mem;
+
+const TypeInfo = std.builtin.TypeInfo;
+const TypeId = std.builtin.TypeId;
+
+const expect = std.testing.expect;
+const expectEqualStrings = std.testing.expectEqualStrings;
+
+test "type info: tag type, void info" {
+ testBasic();
+ comptime testBasic();
+}
+
+fn testBasic() void {
+ expect(@typeInfo(TypeInfo).Union.tag_type == TypeId);
+ const void_info = @typeInfo(void);
+ expect(void_info == TypeId.Void);
+ expect(void_info.Void == {});
+}
+
+test "type info: integer, floating point type info" {
+ testIntFloat();
+ comptime testIntFloat();
+}
+
+fn testIntFloat() void {
+ const u8_info = @typeInfo(u8);
+ expect(u8_info == .Int);
+ expect(u8_info.Int.signedness == .unsigned);
+ expect(u8_info.Int.bits == 8);
+
+ const f64_info = @typeInfo(f64);
+ expect(f64_info == .Float);
+ expect(f64_info.Float.bits == 64);
+}
+
+test "type info: pointer type info" {
+ testPointer();
+ comptime testPointer();
+}
+
+fn testPointer() void {
+ const u32_ptr_info = @typeInfo(*u32);
+ expect(u32_ptr_info == .Pointer);
+ expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One);
+ expect(u32_ptr_info.Pointer.is_const == false);
+ expect(u32_ptr_info.Pointer.is_volatile == false);
+ expect(u32_ptr_info.Pointer.alignment == @alignOf(u32));
+ expect(u32_ptr_info.Pointer.child == u32);
+ expect(u32_ptr_info.Pointer.sentinel == null);
+}
+
+test "type info: unknown length pointer type info" {
+ testUnknownLenPtr();
+ comptime testUnknownLenPtr();
+}
+
+fn testUnknownLenPtr() void {
+ const u32_ptr_info = @typeInfo([*]const volatile f64);
+ expect(u32_ptr_info == .Pointer);
+ expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
+ expect(u32_ptr_info.Pointer.is_const == true);
+ expect(u32_ptr_info.Pointer.is_volatile == true);
+ expect(u32_ptr_info.Pointer.sentinel == null);
+ expect(u32_ptr_info.Pointer.alignment == @alignOf(f64));
+ expect(u32_ptr_info.Pointer.child == f64);
+}
+
+test "type info: null terminated pointer type info" {
+ testNullTerminatedPtr();
+ comptime testNullTerminatedPtr();
+}
+
+fn testNullTerminatedPtr() void {
+ const ptr_info = @typeInfo([*:0]u8);
+ expect(ptr_info == .Pointer);
+ expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
+ expect(ptr_info.Pointer.is_const == false);
+ expect(ptr_info.Pointer.is_volatile == false);
+ expect(ptr_info.Pointer.sentinel.? == 0);
+
+ expect(@typeInfo([:0]u8).Pointer.sentinel != null);
+}
+
+test "type info: C pointer type info" {
+ testCPtr();
+ comptime testCPtr();
+}
+
+fn testCPtr() void {
+ const ptr_info = @typeInfo([*c]align(4) const i8);
+ expect(ptr_info == .Pointer);
+ expect(ptr_info.Pointer.size == .C);
+ expect(ptr_info.Pointer.is_const);
+ expect(!ptr_info.Pointer.is_volatile);
+ expect(ptr_info.Pointer.alignment == 4);
+ expect(ptr_info.Pointer.child == i8);
+}
+
+test "type info: slice type info" {
+ testSlice();
+ comptime testSlice();
+}
+
+fn testSlice() void {
+ const u32_slice_info = @typeInfo([]u32);
+ expect(u32_slice_info == .Pointer);
+ expect(u32_slice_info.Pointer.size == .Slice);
+ expect(u32_slice_info.Pointer.is_const == false);
+ expect(u32_slice_info.Pointer.is_volatile == false);
+ expect(u32_slice_info.Pointer.alignment == 4);
+ expect(u32_slice_info.Pointer.child == u32);
+}
+
+test "type info: array type info" {
+ testArray();
+ comptime testArray();
+}
+
+fn testArray() void {
+ {
+ const info = @typeInfo([42]u8);
+ expect(info == .Array);
+ expect(info.Array.len == 42);
+ expect(info.Array.child == u8);
+ expect(info.Array.sentinel == null);
+ }
+
+ {
+ const info = @typeInfo([10:0]u8);
+ expect(info.Array.len == 10);
+ expect(info.Array.child == u8);
+ expect(info.Array.sentinel.? == @as(u8, 0));
+ expect(@sizeOf([10:0]u8) == info.Array.len + 1);
+ }
+}
+
+test "type info: optional type info" {
+ testOptional();
+ comptime testOptional();
+}
+
+fn testOptional() void {
+ const null_info = @typeInfo(?void);
+ expect(null_info == .Optional);
+ expect(null_info.Optional.child == void);
+}
+
+test "type info: error set, error union info" {
+ testErrorSet();
+ comptime testErrorSet();
+}
+
+fn testErrorSet() void {
+ const TestErrorSet = error{
+ First,
+ Second,
+ Third,
+ };
+
+ const error_set_info = @typeInfo(TestErrorSet);
+ expect(error_set_info == .ErrorSet);
+ expect(error_set_info.ErrorSet.?.len == 3);
+ expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First"));
+
+ const error_union_info = @typeInfo(TestErrorSet!usize);
+ expect(error_union_info == .ErrorUnion);
+ expect(error_union_info.ErrorUnion.error_set == TestErrorSet);
+ expect(error_union_info.ErrorUnion.payload == usize);
+
+ const global_info = @typeInfo(anyerror);
+ expect(global_info == .ErrorSet);
+ expect(global_info.ErrorSet == null);
+}
+
+test "type info: enum info" {
+ testEnum();
+ comptime testEnum();
+}
+
+fn testEnum() void {
+ const Os = enum {
+ Windows,
+ Macos,
+ Linux,
+ FreeBSD,
+ };
+
+ const os_info = @typeInfo(Os);
+ expect(os_info == .Enum);
+ expect(os_info.Enum.layout == .Auto);
+ expect(os_info.Enum.fields.len == 4);
+ expect(mem.eql(u8, os_info.Enum.fields[1].name, "Macos"));
+ expect(os_info.Enum.fields[3].value == 3);
+ expect(os_info.Enum.tag_type == u2);
+ expect(os_info.Enum.decls.len == 0);
+}
+
+test "type info: union info" {
+ testUnion();
+ comptime testUnion();
+}
+
+fn testUnion() void {
+ const typeinfo_info = @typeInfo(TypeInfo);
+ expect(typeinfo_info == .Union);
+ expect(typeinfo_info.Union.layout == .Auto);
+ expect(typeinfo_info.Union.tag_type.? == TypeId);
+ expect(typeinfo_info.Union.fields.len == 25);
+ expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
+ expect(typeinfo_info.Union.decls.len == 22);
+
+ const TestNoTagUnion = union {
+ Foo: void,
+ Bar: u32,
+ };
+
+ const notag_union_info = @typeInfo(TestNoTagUnion);
+ expect(notag_union_info == .Union);
+ expect(notag_union_info.Union.tag_type == null);
+ expect(notag_union_info.Union.layout == .Auto);
+ expect(notag_union_info.Union.fields.len == 2);
+ expect(notag_union_info.Union.fields[0].alignment == @alignOf(void));
+ expect(notag_union_info.Union.fields[1].field_type == u32);
+ expect(notag_union_info.Union.fields[1].alignment == @alignOf(u32));
+
+ const TestExternUnion = extern union {
+ foo: *c_void,
+ };
+
+ const extern_union_info = @typeInfo(TestExternUnion);
+ expect(extern_union_info.Union.layout == .Extern);
+ expect(extern_union_info.Union.tag_type == null);
+ expect(extern_union_info.Union.fields[0].field_type == *c_void);
+}
+
+test "type info: struct info" {
+ testStruct();
+ comptime testStruct();
+}
+
+fn testStruct() void {
+ const unpacked_struct_info = @typeInfo(TestUnpackedStruct);
+ expect(unpacked_struct_info.Struct.is_tuple == false);
+ expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32));
+ expect(unpacked_struct_info.Struct.fields[0].default_value.? == 4);
+ expectEqualStrings("foobar", unpacked_struct_info.Struct.fields[1].default_value.?);
+
+ const struct_info = @typeInfo(TestStruct);
+ expect(struct_info == .Struct);
+ expect(struct_info.Struct.is_tuple == false);
+ expect(struct_info.Struct.layout == .Packed);
+ expect(struct_info.Struct.fields.len == 4);
+ expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize));
+ expect(struct_info.Struct.fields[2].field_type == *TestStruct);
+ expect(struct_info.Struct.fields[2].default_value == null);
+ expect(struct_info.Struct.fields[3].default_value.? == 4);
+ expect(struct_info.Struct.fields[3].alignment == 1);
+ expect(struct_info.Struct.decls.len == 2);
+ expect(struct_info.Struct.decls[0].is_pub);
+ expect(!struct_info.Struct.decls[0].data.Fn.is_extern);
+ expect(struct_info.Struct.decls[0].data.Fn.lib_name == null);
+ expect(struct_info.Struct.decls[0].data.Fn.return_type == void);
+ expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void);
+}
+
+const TestUnpackedStruct = struct {
+ fieldA: u32 = 4,
+ fieldB: *const [6:0]u8 = "foobar",
+};
+
+const TestStruct = packed struct {
+ fieldA: usize align(2 * @alignOf(usize)),
+ fieldB: void,
+ fieldC: *Self,
+ fieldD: u32 = 4,
+
+ pub fn foo(self: *const Self) void {}
+ const Self = @This();
+};
+
+test "type info: opaque info" {
+ testOpaque();
+ comptime testOpaque();
+}
+
+fn testOpaque() void {
+ const Foo = opaque {
+ const A = 1;
+ fn b() void {}
+ };
+
+ const foo_info = @typeInfo(Foo);
+ expect(foo_info.Opaque.decls.len == 2);
+}
+
+test "type info: function type info" {
+ // wasm doesn't support align attributes on functions
+ if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest;
+ testFunction();
+ comptime testFunction();
+}
+
+fn testFunction() void {
+ const fn_info = @typeInfo(@TypeOf(foo));
+ expect(fn_info == .Fn);
+ // TODO Fix this before merging the branch
+ //expect(fn_info.Fn.alignment > 0);
+ expect(fn_info.Fn.calling_convention == .C);
+ expect(!fn_info.Fn.is_generic);
+ expect(fn_info.Fn.args.len == 2);
+ expect(fn_info.Fn.is_var_args);
+ expect(fn_info.Fn.return_type.? == usize);
+ const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
+ expect(fn_aligned_info.Fn.alignment == 4);
+
+ const test_instance: TestStruct = undefined;
+ const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo));
+ expect(bound_fn_info == .BoundFn);
+ expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);
+}
+
+extern fn foo(a: usize, b: bool, ...) callconv(.C) usize;
+extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize;
+
+test "typeInfo with comptime parameter in struct fn def" {
+ const S = struct {
+ pub fn func(comptime x: f32) void {}
+ };
+ comptime var info = @typeInfo(S);
+}
+
+test "type info: vectors" {
+ testVector();
+ comptime testVector();
+}
+
+fn testVector() void {
+ const vec_info = @typeInfo(std.meta.Vector(4, i32));
+ expect(vec_info == .Vector);
+ expect(vec_info.Vector.len == 4);
+ expect(vec_info.Vector.child == i32);
+}
+
+test "type info: anyframe and anyframe->T" {
+ testAnyFrame();
+ comptime testAnyFrame();
+}
+
+fn testAnyFrame() void {
+ {
+ const anyframe_info = @typeInfo(anyframe->i32);
+ expect(anyframe_info == .AnyFrame);
+ expect(anyframe_info.AnyFrame.child.? == i32);
+ }
+
+ {
+ const anyframe_info = @typeInfo(anyframe);
+ expect(anyframe_info == .AnyFrame);
+ expect(anyframe_info.AnyFrame.child == null);
+ }
+}
+
+test "type info: pass to function" {
+ _ = passTypeInfo(@typeInfo(void));
+ _ = comptime passTypeInfo(@typeInfo(void));
+}
+
+fn passTypeInfo(comptime info: TypeInfo) type {
+ return void;
+}
+
+test "type info: TypeId -> TypeInfo impl cast" {
+ _ = passTypeInfo(TypeId.Void);
+ _ = comptime passTypeInfo(TypeId.Void);
+}
+
+test "type info: extern fns with and without lib names" {
+ const S = struct {
+ extern fn bar1() void;
+ extern "cool" fn bar2() void;
+ };
+ const info = @typeInfo(S);
+ comptime {
+ for (info.Struct.decls) |decl| {
+ if (std.mem.eql(u8, decl.name, "bar1")) {
+ expect(decl.data.Fn.lib_name == null);
+ } else {
+ expectEqualStrings("cool", decl.data.Fn.lib_name.?);
+ }
+ }
+ }
+}
+
+test "data field is a compile-time value" {
+ const S = struct {
+ const Bar = @as(isize, -1);
+ };
+ comptime expect(@typeInfo(S).Struct.decls[0].data.Var == isize);
+}
+
+test "sentinel of opaque pointer type" {
+ const c_void_info = @typeInfo(*c_void);
+ expect(c_void_info.Pointer.sentinel == null);
+}
+
+test "@typeInfo does not force declarations into existence" {
+ const S = struct {
+ x: i32,
+
+ fn doNotReferenceMe() void {
+ @compileError("test failed");
+ }
+ };
+ comptime expect(@typeInfo(S).Struct.fields.len == 1);
+}
+
+test "defaut value for a var-typed field" {
+ const S = struct { x: anytype };
+ expect(@typeInfo(S).Struct.fields[0].default_value == null);
+}
+
+fn add(a: i32, b: i32) i32 {
+ return a + b;
+}
+
+test "type info for async frames" {
+ switch (@typeInfo(@Frame(add))) {
+ .Frame => |frame| {
+ expect(frame.function == add);
+ },
+ else => unreachable,
+ }
+}
+
+test "type info: value is correctly copied" {
+ comptime {
+ var ptrInfo = @typeInfo([]u32);
+ ptrInfo.Pointer.size = .One;
+ expect(@typeInfo([]u32).Pointer.size == .Slice);
+ }
+}
+
+test "Declarations are returned in declaration order" {
+ const S = struct {
+ const a = 1;
+ const b = 2;
+ const c = 3;
+ const d = 4;
+ const e = 5;
+ };
+ const d = @typeInfo(S).Struct.decls;
+ expect(std.mem.eql(u8, d[0].name, "a"));
+ expect(std.mem.eql(u8, d[1].name, "b"));
+ expect(std.mem.eql(u8, d[2].name, "c"));
+ expect(std.mem.eql(u8, d[3].name, "d"));
+ expect(std.mem.eql(u8, d[4].name, "e"));
+}
+
+test "Struct.is_tuple" {
+ expect(@typeInfo(@TypeOf(.{0})).Struct.is_tuple);
+ expect(!@typeInfo(@TypeOf(.{ .a = 0 })).Struct.is_tuple);
+}
+
+test "StructField.is_comptime" {
+ const info = @typeInfo(struct { x: u8 = 3, comptime y: u32 = 5 }).Struct;
+ expect(!info.fields[0].is_comptime);
+ expect(info.fields[1].is_comptime);
+}
+
+test "typeInfo resolves usingnamespace declarations" {
+ const A = struct {
+ pub const f1 = 42;
+ };
+
+ const B = struct {
+ const f0 = 42;
+ usingnamespace A;
+ };
+
+ expect(@typeInfo(B).Struct.decls.len == 2);
+ //a
+}
diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig
new file mode 100644
index 0000000000..1abefe5b5e
--- /dev/null
+++ b/test/behavior/typename.zig
@@ -0,0 +1,7 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqualSlices = std.testing.expectEqualSlices;
+
+test "slice" {
+ expectEqualSlices(u8, "[]u8", @typeName([]u8));
+}
diff --git a/test/behavior/undefined.zig b/test/behavior/undefined.zig
new file mode 100644
index 0000000000..114b0262b1
--- /dev/null
+++ b/test/behavior/undefined.zig
@@ -0,0 +1,69 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const mem = std.mem;
+
+fn initStaticArray() [10]i32 {
+ var array: [10]i32 = undefined;
+ array[0] = 1;
+ array[4] = 2;
+ array[7] = 3;
+ array[9] = 4;
+ return array;
+}
+const static_array = initStaticArray();
+test "init static array to undefined" {
+ expect(static_array[0] == 1);
+ expect(static_array[4] == 2);
+ expect(static_array[7] == 3);
+ expect(static_array[9] == 4);
+
+ comptime {
+ expect(static_array[0] == 1);
+ expect(static_array[4] == 2);
+ expect(static_array[7] == 3);
+ expect(static_array[9] == 4);
+ }
+}
+
+const Foo = struct {
+ x: i32,
+
+ fn setFooXMethod(foo: *Foo) void {
+ foo.x = 3;
+ }
+};
+
+fn setFooX(foo: *Foo) void {
+ foo.x = 2;
+}
+
+test "assign undefined to struct" {
+ comptime {
+ var foo: Foo = undefined;
+ setFooX(&foo);
+ expect(foo.x == 2);
+ }
+ {
+ var foo: Foo = undefined;
+ setFooX(&foo);
+ expect(foo.x == 2);
+ }
+}
+
+test "assign undefined to struct with method" {
+ comptime {
+ var foo: Foo = undefined;
+ foo.setFooXMethod();
+ expect(foo.x == 3);
+ }
+ {
+ var foo: Foo = undefined;
+ foo.setFooXMethod();
+ expect(foo.x == 3);
+ }
+}
+
+test "type name of undefined" {
+ const x = undefined;
+ expect(mem.eql(u8, @typeName(@TypeOf(x)), "(undefined)"));
+}
diff --git a/test/behavior/underscore.zig b/test/behavior/underscore.zig
new file mode 100644
index 0000000000..516d33a4eb
--- /dev/null
+++ b/test/behavior/underscore.zig
@@ -0,0 +1,28 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "ignore lval with underscore" {
+ _ = false;
+}
+
+test "ignore lval with underscore (for loop)" {
+ for ([_]void{}) |_, i| {
+ for ([_]void{}) |_, j| {
+ break;
+ }
+ break;
+ }
+}
+
+test "ignore lval with underscore (while loop)" {
+ while (optionalReturnError()) |_| {
+ while (optionalReturnError()) |_| {
+ break;
+ } else |_| {}
+ break;
+ } else |_| {}
+}
+
+fn optionalReturnError() !?u32 {
+ return error.optionalReturnError;
+}
diff --git a/test/behavior/union.zig b/test/behavior/union.zig
new file mode 100644
index 0000000000..e46b6bb6b9
--- /dev/null
+++ b/test/behavior/union.zig
@@ -0,0 +1,806 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const Tag = std.meta.Tag;
+
+const Value = union(enum) {
+ Int: u64,
+ Array: [9]u8,
+};
+
+const Agg = struct {
+ val1: Value,
+ val2: Value,
+};
+
+const v1 = Value{ .Int = 1234 };
+const v2 = Value{ .Array = [_]u8{3} ** 9 };
+
+const err = @as(anyerror!Agg, Agg{
+ .val1 = v1,
+ .val2 = v2,
+});
+
+const array = [_]Value{
+ v1,
+ v2,
+ v1,
+ v2,
+};
+
+test "unions embedded in aggregate types" {
+ switch (array[1]) {
+ Value.Array => |arr| expect(arr[4] == 3),
+ else => unreachable,
+ }
+ switch ((err catch unreachable).val1) {
+ Value.Int => |x| expect(x == 1234),
+ else => unreachable,
+ }
+}
+
+const Foo = union {
+ float: f64,
+ int: i32,
+};
+
+test "basic unions" {
+ var foo = Foo{ .int = 1 };
+ expect(foo.int == 1);
+ foo = Foo{ .float = 12.34 };
+ expect(foo.float == 12.34);
+}
+
+test "comptime union field access" {
+ comptime {
+ var foo = Foo{ .int = 0 };
+ expect(foo.int == 0);
+
+ foo = Foo{ .float = 42.42 };
+ expect(foo.float == 42.42);
+ }
+}
+
+test "init union with runtime value" {
+ var foo: Foo = undefined;
+
+ setFloat(&foo, 12.34);
+ expect(foo.float == 12.34);
+
+ setInt(&foo, 42);
+ expect(foo.int == 42);
+}
+
+fn setFloat(foo: *Foo, x: f64) void {
+ foo.* = Foo{ .float = x };
+}
+
+fn setInt(foo: *Foo, x: i32) void {
+ foo.* = Foo{ .int = x };
+}
+
+const FooExtern = extern union {
+ float: f64,
+ int: i32,
+};
+
+test "basic extern unions" {
+ var foo = FooExtern{ .int = 1 };
+ expect(foo.int == 1);
+ foo.float = 12.34;
+ expect(foo.float == 12.34);
+}
+
+const Letter = enum {
+ A,
+ B,
+ C,
+};
+const Payload = union(Letter) {
+ A: i32,
+ B: f64,
+ C: bool,
+};
+
+test "union with specified enum tag" {
+ doTest();
+ comptime doTest();
+}
+
+fn doTest() void {
+ expect(bar(Payload{ .A = 1234 }) == -10);
+}
+
+fn bar(value: Payload) i32 {
+ expect(@as(Letter, value) == Letter.A);
+ return switch (value) {
+ Payload.A => |x| return x - 1244,
+ Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21,
+ Payload.C => |x| if (x) @as(i32, 30) else 31,
+ };
+}
+
+const MultipleChoice = union(enum(u32)) {
+ A = 20,
+ B = 40,
+ C = 60,
+ D = 1000,
+};
+test "simple union(enum(u32))" {
+ var x = MultipleChoice.C;
+ expect(x == MultipleChoice.C);
+ expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60);
+}
+
+const MultipleChoice2 = union(enum(u32)) {
+ Unspecified1: i32,
+ A: f32 = 20,
+ Unspecified2: void,
+ B: bool = 40,
+ Unspecified3: i32,
+ C: i8 = 60,
+ Unspecified4: void,
+ D: void = 1000,
+ Unspecified5: i32,
+};
+
+test "union(enum(u32)) with specified and unspecified tag values" {
+ comptime expect(Tag(Tag(MultipleChoice2)) == u32);
+ testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
+ comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
+}
+
+fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void {
+ expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60);
+ expect(1123 == switch (x) {
+ MultipleChoice2.A => 1,
+ MultipleChoice2.B => 2,
+ MultipleChoice2.C => |v| @as(i32, 1000) + v,
+ MultipleChoice2.D => 4,
+ MultipleChoice2.Unspecified1 => 5,
+ MultipleChoice2.Unspecified2 => 6,
+ MultipleChoice2.Unspecified3 => 7,
+ MultipleChoice2.Unspecified4 => 8,
+ MultipleChoice2.Unspecified5 => 9,
+ });
+}
+
+const ExternPtrOrInt = extern union {
+ ptr: *u8,
+ int: u64,
+};
+test "extern union size" {
+ comptime expect(@sizeOf(ExternPtrOrInt) == 8);
+}
+
+const PackedPtrOrInt = packed union {
+ ptr: *u8,
+ int: u64,
+};
+test "extern union size" {
+ comptime expect(@sizeOf(PackedPtrOrInt) == 8);
+}
+
+const ZeroBits = union {
+ OnlyField: void,
+};
+test "union with only 1 field which is void should be zero bits" {
+ comptime expect(@sizeOf(ZeroBits) == 0);
+}
+
+const TheTag = enum {
+ A,
+ B,
+ C,
+};
+const TheUnion = union(TheTag) {
+ A: i32,
+ B: i32,
+ C: i32,
+};
+test "union field access gives the enum values" {
+ expect(TheUnion.A == TheTag.A);
+ expect(TheUnion.B == TheTag.B);
+ expect(TheUnion.C == TheTag.C);
+}
+
+test "cast union to tag type of union" {
+ testCastUnionToTag(TheUnion{ .B = 1234 });
+ comptime testCastUnionToTag(TheUnion{ .B = 1234 });
+}
+
+fn testCastUnionToTag(x: TheUnion) void {
+ expect(@as(TheTag, x) == TheTag.B);
+}
+
+test "cast tag type of union to union" {
+ var x: Value2 = Letter2.B;
+ expect(@as(Letter2, x) == Letter2.B);
+}
+const Letter2 = enum {
+ A,
+ B,
+ C,
+};
+const Value2 = union(Letter2) {
+ A: i32,
+ B,
+ C,
+};
+
+test "implicit cast union to its tag type" {
+ var x: Value2 = Letter2.B;
+ expect(x == Letter2.B);
+ giveMeLetterB(x);
+}
+fn giveMeLetterB(x: Letter2) void {
+ expect(x == Value2.B);
+}
+
+pub const PackThis = union(enum) {
+ Invalid: bool,
+ StringLiteral: u2,
+};
+
+test "constant packed union" {
+ testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});
+}
+
+fn testConstPackedUnion(expected_tokens: []const PackThis) void {
+ expect(expected_tokens[0].StringLiteral == 1);
+}
+
+test "switch on union with only 1 field" {
+ var r: PartialInst = undefined;
+ r = PartialInst.Compiled;
+ switch (r) {
+ PartialInst.Compiled => {
+ var z: PartialInstWithPayload = undefined;
+ z = PartialInstWithPayload{ .Compiled = 1234 };
+ switch (z) {
+ PartialInstWithPayload.Compiled => |x| {
+ expect(x == 1234);
+ return;
+ },
+ }
+ },
+ }
+ unreachable;
+}
+
+const PartialInst = union(enum) {
+ Compiled,
+};
+
+const PartialInstWithPayload = union(enum) {
+ Compiled: i32,
+};
+
+test "access a member of tagged union with conflicting enum tag name" {
+ const Bar = union(enum) {
+ A: A,
+ B: B,
+
+ const A = u8;
+ const B = void;
+ };
+
+ comptime expect(Bar.A == u8);
+}
+
+test "tagged union initialization with runtime void" {
+ expect(testTaggedUnionInit({}));
+}
+
+const TaggedUnionWithAVoid = union(enum) {
+ A,
+ B: i32,
+};
+
+fn testTaggedUnionInit(x: anytype) bool {
+ const y = TaggedUnionWithAVoid{ .A = x };
+ return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
+}
+
+pub const UnionEnumNoPayloads = union(enum) {
+ A,
+ B,
+};
+
+test "tagged union with no payloads" {
+ const a = UnionEnumNoPayloads{ .B = {} };
+ switch (a) {
+ Tag(UnionEnumNoPayloads).A => @panic("wrong"),
+ Tag(UnionEnumNoPayloads).B => {},
+ }
+}
+
+test "union with only 1 field casted to its enum type" {
+ const Literal = union(enum) {
+ Number: f64,
+ Bool: bool,
+ };
+
+ const Expr = union(enum) {
+ Literal: Literal,
+ };
+
+ var e = Expr{ .Literal = Literal{ .Bool = true } };
+ const ExprTag = Tag(Expr);
+ comptime expect(Tag(ExprTag) == u0);
+ var t = @as(ExprTag, e);
+ expect(t == Expr.Literal);
+}
+
+test "union with only 1 field casted to its enum type which has enum value specified" {
+ const Literal = union(enum) {
+ Number: f64,
+ Bool: bool,
+ };
+
+ const ExprTag = enum(comptime_int) {
+ Literal = 33,
+ };
+
+ const Expr = union(ExprTag) {
+ Literal: Literal,
+ };
+
+ var e = Expr{ .Literal = Literal{ .Bool = true } };
+ comptime expect(Tag(ExprTag) == comptime_int);
+ var t = @as(ExprTag, e);
+ expect(t == Expr.Literal);
+ expect(@enumToInt(t) == 33);
+ comptime expect(@enumToInt(t) == 33);
+}
+
+test "@enumToInt works on unions" {
+ const Bar = union(enum) {
+ A: bool,
+ B: u8,
+ C,
+ };
+
+ const a = Bar{ .A = true };
+ var b = Bar{ .B = undefined };
+ var c = Bar.C;
+ expect(@enumToInt(a) == 0);
+ expect(@enumToInt(b) == 1);
+ expect(@enumToInt(c) == 2);
+}
+
+const Attribute = union(enum) {
+ A: bool,
+ B: u8,
+};
+
+fn setAttribute(attr: Attribute) void {}
+
+fn Setter(attr: Attribute) type {
+ return struct {
+ fn set() void {
+ setAttribute(attr);
+ }
+ };
+}
+
+test "comptime union field value equality" {
+ const a0 = Setter(Attribute{ .A = false });
+ const a1 = Setter(Attribute{ .A = true });
+ const a2 = Setter(Attribute{ .A = false });
+
+ const b0 = Setter(Attribute{ .B = 5 });
+ const b1 = Setter(Attribute{ .B = 9 });
+ const b2 = Setter(Attribute{ .B = 5 });
+
+ expect(a0 == a0);
+ expect(a1 == a1);
+ expect(a0 == a2);
+
+ expect(b0 == b0);
+ expect(b1 == b1);
+ expect(b0 == b2);
+
+ expect(a0 != b0);
+ expect(a0 != a1);
+ expect(b0 != b1);
+}
+
+test "return union init with void payload" {
+ const S = struct {
+ fn entry() void {
+ expect(func().state == State.one);
+ }
+ const Outer = union(enum) {
+ state: State,
+ };
+ const State = union(enum) {
+ one: void,
+ two: u32,
+ };
+ fn func() Outer {
+ return Outer{ .state = State{ .one = {} } };
+ }
+ };
+ S.entry();
+ comptime S.entry();
+}
+
+test "@unionInit can modify a union type" {
+ const UnionInitEnum = union(enum) {
+ Boolean: bool,
+ Byte: u8,
+ };
+
+ var value: UnionInitEnum = undefined;
+
+ value = @unionInit(UnionInitEnum, "Boolean", true);
+ expect(value.Boolean == true);
+ value.Boolean = false;
+ expect(value.Boolean == false);
+
+ value = @unionInit(UnionInitEnum, "Byte", 2);
+ expect(value.Byte == 2);
+ value.Byte = 3;
+ expect(value.Byte == 3);
+}
+
+test "@unionInit can modify a pointer value" {
+ const UnionInitEnum = union(enum) {
+ Boolean: bool,
+ Byte: u8,
+ };
+
+ var value: UnionInitEnum = undefined;
+ var value_ptr = &value;
+
+ value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true);
+ expect(value.Boolean == true);
+
+ value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2);
+ expect(value.Byte == 2);
+}
+
+test "union no tag with struct member" {
+ const Struct = struct {};
+ const Union = union {
+ s: Struct,
+ pub fn foo(self: *@This()) void {}
+ };
+ var u = Union{ .s = Struct{} };
+ u.foo();
+}
+
+fn testComparison() void {
+ var x = Payload{ .A = 42 };
+ expect(x == .A);
+ expect(x != .B);
+ expect(x != .C);
+ expect((x == .B) == false);
+ expect((x == .C) == false);
+ expect((x != .A) == false);
+}
+
+test "comparison between union and enum literal" {
+ testComparison();
+ comptime testComparison();
+}
+
+test "packed union generates correctly aligned LLVM type" {
+ const U = packed union {
+ f1: fn () void,
+ f2: u32,
+ };
+ var foo = [_]U{
+ U{ .f1 = doTest },
+ U{ .f2 = 0 },
+ };
+ foo[0].f1();
+}
+
+test "union with one member defaults to u0 tag type" {
+ const U0 = union(enum) {
+ X: u32,
+ };
+ comptime expect(Tag(Tag(U0)) == u0);
+}
+
+test "union with comptime_int tag" {
+ const Union = union(enum(comptime_int)) {
+ X: u32,
+ Y: u16,
+ Z: u8,
+ };
+ comptime expect(Tag(Tag(Union)) == comptime_int);
+}
+
+test "extern union doesn't trigger field check at comptime" {
+ const U = extern union {
+ x: u32,
+ y: u8,
+ };
+
+ const x = U{ .x = 0x55AAAA55 };
+ comptime expect(x.y == 0x55);
+}
+
+const Foo1 = union(enum) {
+ f: struct {
+ x: usize,
+ },
+};
+var glbl: Foo1 = undefined;
+
+test "global union with single field is correctly initialized" {
+ glbl = Foo1{
+ .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
+ };
+ expect(glbl.f.x == 123);
+}
+
+pub const FooUnion = union(enum) {
+ U0: usize,
+ U1: u8,
+};
+
+var glbl_array: [2]FooUnion = undefined;
+
+test "initialize global array of union" {
+ glbl_array[1] = FooUnion{ .U1 = 2 };
+ glbl_array[0] = FooUnion{ .U0 = 1 };
+ expect(glbl_array[0].U0 == 1);
+ expect(glbl_array[1].U1 == 2);
+}
+
+test "anonymous union literal syntax" {
+ const S = struct {
+ const Number = union {
+ int: i32,
+ float: f64,
+ };
+
+ fn doTheTest() void {
+ var i: Number = .{ .int = 42 };
+ var f = makeNumber();
+ expect(i.int == 42);
+ expect(f.float == 12.34);
+ }
+
+ fn makeNumber() Number {
+ return .{ .float = 12.34 };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "update the tag value for zero-sized unions" {
+ const S = union(enum) {
+ U0: void,
+ U1: void,
+ };
+ var x = S{ .U0 = {} };
+ expect(x == .U0);
+ x = S{ .U1 = {} };
+ expect(x == .U1);
+}
+
+test "function call result coerces from tagged union to the tag" {
+ const S = struct {
+ const Arch = union(enum) {
+ One,
+ Two: usize,
+ };
+
+ const ArchTag = Tag(Arch);
+
+ fn doTheTest() void {
+ var x: ArchTag = getArch1();
+ expect(x == .One);
+
+ var y: ArchTag = getArch2();
+ expect(y == .Two);
+ }
+
+ pub fn getArch1() Arch {
+ return .One;
+ }
+
+ pub fn getArch2() Arch {
+ return .{ .Two = 99 };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "0-sized extern union definition" {
+ const U = extern union {
+ a: void,
+ const f = 1;
+ };
+
+ expect(U.f == 1);
+}
+
+test "union initializer generates padding only if needed" {
+ const U = union(enum) {
+ A: u24,
+ };
+
+ var v = U{ .A = 532 };
+ expect(v.A == 532);
+}
+
+test "runtime tag name with single field" {
+ const U = union(enum) {
+ A: i32,
+ };
+
+ var v = U{ .A = 42 };
+ expect(std.mem.eql(u8, @tagName(v), "A"));
+}
+
+test "cast from anonymous struct to union" {
+ const S = struct {
+ const U = union(enum) {
+ A: u32,
+ B: []const u8,
+ C: void,
+ };
+ fn doTheTest() void {
+ var y: u32 = 42;
+ const t0 = .{ .A = 123 };
+ const t1 = .{ .B = "foo" };
+ const t2 = .{ .C = {} };
+ const t3 = .{ .A = y };
+ const x0: U = t0;
+ var x1: U = t1;
+ const x2: U = t2;
+ var x3: U = t3;
+ expect(x0.A == 123);
+ expect(std.mem.eql(u8, x1.B, "foo"));
+ expect(x2 == .C);
+ expect(x3.A == y);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "cast from pointer to anonymous struct to pointer to union" {
+ const S = struct {
+ const U = union(enum) {
+ A: u32,
+ B: []const u8,
+ C: void,
+ };
+ fn doTheTest() void {
+ var y: u32 = 42;
+ const t0 = &.{ .A = 123 };
+ const t1 = &.{ .B = "foo" };
+ const t2 = &.{ .C = {} };
+ const t3 = &.{ .A = y };
+ const x0: *const U = t0;
+ var x1: *const U = t1;
+ const x2: *const U = t2;
+ var x3: *const U = t3;
+ expect(x0.A == 123);
+ expect(std.mem.eql(u8, x1.B, "foo"));
+ expect(x2.* == .C);
+ expect(x3.A == y);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "method call on an empty union" {
+ const S = struct {
+ const MyUnion = union(MyUnionTag) {
+ pub const MyUnionTag = enum { X1, X2 };
+ X1: [0]u8,
+ X2: [0]u8,
+
+ pub fn useIt(self: *@This()) bool {
+ return true;
+ }
+ };
+
+ fn doTheTest() void {
+ var u = MyUnion{ .X1 = [0]u8{} };
+ expect(u.useIt());
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "switching on non exhaustive union" {
+ const S = struct {
+ const E = enum(u8) {
+ a,
+ b,
+ _,
+ };
+ const U = union(E) {
+ a: i32,
+ b: u32,
+ };
+ fn doTheTest() void {
+ var a = U{ .a = 2 };
+ switch (a) {
+ .a => |val| expect(val == 2),
+ .b => unreachable,
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "containers with single-field enums" {
+ const S = struct {
+ const A = union(enum) { f1 };
+ const B = union(enum) { f1: void };
+ const C = struct { a: A };
+ const D = struct { a: B };
+
+ fn doTheTest() void {
+ var array1 = [1]A{A{ .f1 = {} }};
+ var array2 = [1]B{B{ .f1 = {} }};
+ expect(array1[0] == .f1);
+ expect(array2[0] == .f1);
+
+ var struct1 = C{ .a = A{ .f1 = {} } };
+ var struct2 = D{ .a = B{ .f1 = {} } };
+ expect(struct1.a == .f1);
+ expect(struct2.a == .f1);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "@unionInit on union w/ tag but no fields" {
+ const S = struct {
+ const Type = enum(u8) { no_op = 105 };
+
+ const Data = union(Type) {
+ no_op: void,
+
+ pub fn decode(buf: []const u8) Data {
+ return @unionInit(Data, "no_op", {});
+ }
+ };
+
+ comptime {
+ expect(@sizeOf(Data) != 0);
+ }
+
+ fn doTheTest() void {
+ var data: Data = .{ .no_op = .{} };
+ var o = Data.decode(&[_]u8{});
+ expectEqual(Type.no_op, o);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "union enum type gets a separate scope" {
+ const S = struct {
+ const U = union(enum) {
+ a: u8,
+ const foo = 1;
+ };
+
+ fn doTheTest() void {
+ expect(!@hasDecl(Tag(U), "foo"));
+ }
+ };
+
+ S.doTheTest();
+}
diff --git a/test/behavior/usingnamespace.zig b/test/behavior/usingnamespace.zig
new file mode 100644
index 0000000000..a44bf1bbc3
--- /dev/null
+++ b/test/behavior/usingnamespace.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+
+fn Foo(comptime T: type) type {
+ return struct {
+ usingnamespace T;
+ };
+}
+
+test "usingnamespace inside a generic struct" {
+ const std2 = Foo(std);
+ const testing2 = Foo(std.testing);
+ std2.testing.expect(true);
+ testing2.expect(true);
+}
+
+usingnamespace struct {
+ pub const foo = 42;
+};
+
+test "usingnamespace does not redeclare an imported variable" {
+ comptime std.testing.expect(foo == 42);
+}
diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig
new file mode 100644
index 0000000000..eae8f8f888
--- /dev/null
+++ b/test/behavior/var_args.zig
@@ -0,0 +1,83 @@
+const expect = @import("std").testing.expect;
+
+fn add(args: anytype) i32 {
+ var sum = @as(i32, 0);
+ {
+ comptime var i: usize = 0;
+ inline while (i < args.len) : (i += 1) {
+ sum += args[i];
+ }
+ }
+ return sum;
+}
+
+test "add arbitrary args" {
+ expect(add(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10);
+ expect(add(.{@as(i32, 1234)}) == 1234);
+ expect(add(.{}) == 0);
+}
+
+fn readFirstVarArg(args: anytype) void {
+ const value = args[0];
+}
+
+test "send void arg to var args" {
+ readFirstVarArg(.{{}});
+}
+
+test "pass args directly" {
+ expect(addSomeStuff(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10);
+ expect(addSomeStuff(.{@as(i32, 1234)}) == 1234);
+ expect(addSomeStuff(.{}) == 0);
+}
+
+fn addSomeStuff(args: anytype) i32 {
+ return add(args);
+}
+
+test "runtime parameter before var args" {
+ expect(extraFn(10, .{}) == 0);
+ expect(extraFn(10, .{false}) == 1);
+ expect(extraFn(10, .{ false, true }) == 2);
+
+ comptime {
+ expect(extraFn(10, .{}) == 0);
+ expect(extraFn(10, .{false}) == 1);
+ expect(extraFn(10, .{ false, true }) == 2);
+ }
+}
+
+fn extraFn(extra: u32, args: anytype) usize {
+ if (args.len >= 1) {
+ expect(args[0] == false);
+ }
+ if (args.len >= 2) {
+ expect(args[1] == true);
+ }
+ return args.len;
+}
+
+const foos = [_]fn (anytype) bool{
+ foo1,
+ foo2,
+};
+
+fn foo1(args: anytype) bool {
+ return true;
+}
+fn foo2(args: anytype) bool {
+ return false;
+}
+
+test "array of var args functions" {
+ expect(foos[0](.{}));
+ expect(!foos[1](.{}));
+}
+
+test "pass zero length array to var args param" {
+ doNothingWithFirstArg(.{""});
+}
+
+fn doNothingWithFirstArg(args: anytype) void {
+ const a = args[0];
+}
diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig
new file mode 100644
index 0000000000..5035a824c7
--- /dev/null
+++ b/test/behavior/vector.zig
@@ -0,0 +1,655 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const mem = std.mem;
+const math = std.math;
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectApproxEqRel = std.testing.expectApproxEqRel;
+const Vector = std.meta.Vector;
+
+test "implicit cast vector to array - bool" {
+ const S = struct {
+ fn doTheTest() void {
+ const a: Vector(4, bool) = [_]bool{ true, false, true, false };
+ const result_array: [4]bool = a;
+ expect(mem.eql(bool, &result_array, &[4]bool{ true, false, true, false }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector wrap operators" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
+ var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
+ expect(mem.eql(i32, &@as([4]i32, v +% x), &[4]i32{ -2147483648, 2147483645, 33, 44 }));
+ expect(mem.eql(i32, &@as([4]i32, v -% x), &[4]i32{ 2147483646, 2147483647, 27, 36 }));
+ expect(mem.eql(i32, &@as([4]i32, v *% x), &[4]i32{ 2147483647, 2, 90, 160 }));
+ var z: Vector(4, i32) = [4]i32{ 1, 2, 3, -2147483648 };
+ expect(mem.eql(i32, &@as([4]i32, -%z), &[4]i32{ -1, -2, -3, -2147483648 }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector bin compares with mem.eql" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
+ var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 30, 4 };
+ expect(mem.eql(bool, &@as([4]bool, v == x), &[4]bool{ false, false, true, false }));
+ expect(mem.eql(bool, &@as([4]bool, v != x), &[4]bool{ true, true, false, true }));
+ expect(mem.eql(bool, &@as([4]bool, v < x), &[4]bool{ false, true, false, false }));
+ expect(mem.eql(bool, &@as([4]bool, v > x), &[4]bool{ true, false, false, true }));
+ expect(mem.eql(bool, &@as([4]bool, v <= x), &[4]bool{ false, true, true, false }));
+ expect(mem.eql(bool, &@as([4]bool, v >= x), &[4]bool{ true, false, true, true }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector int operators" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
+ var x: Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
+ expect(mem.eql(i32, &@as([4]i32, v + x), &[4]i32{ 11, 22, 33, 44 }));
+ expect(mem.eql(i32, &@as([4]i32, v - x), &[4]i32{ 9, 18, 27, 36 }));
+ expect(mem.eql(i32, &@as([4]i32, v * x), &[4]i32{ 10, 40, 90, 160 }));
+ expect(mem.eql(i32, &@as([4]i32, -v), &[4]i32{ -10, -20, -30, -40 }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector float operators" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, f32) = [4]f32{ 10, 20, 30, 40 };
+ var x: Vector(4, f32) = [4]f32{ 1, 2, 3, 4 };
+ expect(mem.eql(f32, &@as([4]f32, v + x), &[4]f32{ 11, 22, 33, 44 }));
+ expect(mem.eql(f32, &@as([4]f32, v - x), &[4]f32{ 9, 18, 27, 36 }));
+ expect(mem.eql(f32, &@as([4]f32, v * x), &[4]f32{ 10, 40, 90, 160 }));
+ expect(mem.eql(f32, &@as([4]f32, -x), &[4]f32{ -1, -2, -3, -4 }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector bit operators" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 };
+ var x: Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 };
+ expect(mem.eql(u8, &@as([4]u8, v ^ x), &[4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 }));
+ expect(mem.eql(u8, &@as([4]u8, v | x), &[4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 }));
+ expect(mem.eql(u8, &@as([4]u8, v & x), &[4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "implicit cast vector to array" {
+ const S = struct {
+ fn doTheTest() void {
+ var a: Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
+ var result_array: [4]i32 = a;
+ result_array = a;
+ expect(mem.eql(i32, &result_array, &[4]i32{ 1, 2, 3, 4 }));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "array to vector" {
+ var foo: f32 = 3.14;
+ var arr = [4]f32{ foo, 1.5, 0.0, 0.0 };
+ var vec: Vector(4, f32) = arr;
+}
+
+test "vector casts of sizes not divisable by 8" {
+ // https://github.com/ziglang/zig/issues/3563
+ if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest;
+
+ const S = struct {
+ fn doTheTest() void {
+ {
+ var v: Vector(4, u3) = [4]u3{ 5, 2, 3, 0 };
+ var x: [4]u3 = v;
+ expect(mem.eql(u3, &x, &@as([4]u3, v)));
+ }
+ {
+ var v: Vector(4, u2) = [4]u2{ 1, 2, 3, 0 };
+ var x: [4]u2 = v;
+ expect(mem.eql(u2, &x, &@as([4]u2, v)));
+ }
+ {
+ var v: Vector(4, u1) = [4]u1{ 1, 0, 1, 0 };
+ var x: [4]u1 = v;
+ expect(mem.eql(u1, &x, &@as([4]u1, v)));
+ }
+ {
+ var v: Vector(4, bool) = [4]bool{ false, false, true, false };
+ var x: [4]bool = v;
+ expect(mem.eql(bool, &x, &@as([4]bool, v)));
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector @splat" {
+ const S = struct {
+ fn testForT(comptime N: comptime_int, v: anytype) void {
+ const T = @TypeOf(v);
+ var vec = @splat(N, v);
+ expectEqual(Vector(N, T), @TypeOf(vec));
+ var as_array = @as([N]T, vec);
+ for (as_array) |elem| expectEqual(v, elem);
+ }
+ fn doTheTest() void {
+ // Splats with multiple-of-8 bit types that fill a 128bit vector.
+ testForT(16, @as(u8, 0xEE));
+ testForT(8, @as(u16, 0xBEEF));
+ testForT(4, @as(u32, 0xDEADBEEF));
+ testForT(2, @as(u64, 0xCAFEF00DDEADBEEF));
+
+ testForT(8, @as(f16, 3.1415));
+ testForT(4, @as(f32, 3.1415));
+ testForT(2, @as(f64, 3.1415));
+
+ // Same but fill more than 128 bits.
+ testForT(16 * 2, @as(u8, 0xEE));
+ testForT(8 * 2, @as(u16, 0xBEEF));
+ testForT(4 * 2, @as(u32, 0xDEADBEEF));
+ testForT(2 * 2, @as(u64, 0xCAFEF00DDEADBEEF));
+
+ testForT(8 * 2, @as(f16, 3.1415));
+ testForT(4 * 2, @as(f32, 3.1415));
+ testForT(2 * 2, @as(f64, 3.1415));
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "load vector elements via comptime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [_]i32{ 1, 2, 3, undefined };
+ expect(v[0] == 1);
+ expect(v[1] == 2);
+ expect(loadv(&v[2]) == 3);
+ }
+ fn loadv(ptr: anytype) i32 {
+ return ptr.*;
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "store vector elements via comptime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [_]i32{ 1, 5, 3, undefined };
+
+ v[2] = 42;
+ expect(v[1] == 5);
+ v[3] = -364;
+ expect(v[2] == 42);
+ expect(-364 == v[3]);
+
+ storev(&v[0], 100);
+ expect(v[0] == 100);
+ }
+ fn storev(ptr: anytype, x: i32) void {
+ ptr.* = x;
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "load vector elements via runtime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [_]i32{ 1, 2, 3, undefined };
+ var i: u32 = 0;
+ expect(v[i] == 1);
+ i += 1;
+ expect(v[i] == 2);
+ i += 1;
+ expect(v[i] == 3);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "store vector elements via runtime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: Vector(4, i32) = [_]i32{ 1, 5, 3, undefined };
+ var i: u32 = 2;
+ v[i] = 1;
+ expect(v[1] == 5);
+ expect(v[2] == 1);
+ i += 1;
+ v[i] = -364;
+ expect(-364 == v[3]);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "initialize vector which is a struct field" {
+ const Vec4Obj = struct {
+ data: Vector(4, f32),
+ };
+
+ const S = struct {
+ fn doTheTest() void {
+ var foo = Vec4Obj{
+ .data = [_]f32{ 1, 2, 3, 4 },
+ };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector comparison operators" {
+ const S = struct {
+ fn doTheTest() void {
+ {
+ const v1: Vector(4, bool) = [_]bool{ true, false, true, false };
+ const v2: Vector(4, bool) = [_]bool{ false, true, false, true };
+ expectEqual(@splat(4, true), v1 == v1);
+ expectEqual(@splat(4, false), v1 == v2);
+ expectEqual(@splat(4, true), v1 != v2);
+ expectEqual(@splat(4, false), v2 != v2);
+ }
+ {
+ const v1 = @splat(4, @as(u32, 0xc0ffeeee));
+ const v2: Vector(4, c_uint) = v1;
+ const v3 = @splat(4, @as(u32, 0xdeadbeef));
+ expectEqual(@splat(4, true), v1 == v2);
+ expectEqual(@splat(4, false), v1 == v3);
+ expectEqual(@splat(4, true), v1 != v3);
+ expectEqual(@splat(4, false), v1 != v2);
+ }
+ {
+ // Comptime-known LHS/RHS
+ var v1: @Vector(4, u32) = [_]u32{ 2, 1, 2, 1 };
+ const v2 = @splat(4, @as(u32, 2));
+ const v3: @Vector(4, bool) = [_]bool{ true, false, true, false };
+ expectEqual(v3, v1 == v2);
+ expectEqual(v3, v2 == v1);
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector division operators" {
+ const S = struct {
+ fn doTheTestDiv(comptime T: type, x: Vector(4, T), y: Vector(4, T)) void {
+ if (!comptime std.meta.trait.isSignedInt(T)) {
+ const d0 = x / y;
+ for (@as([4]T, d0)) |v, i| {
+ expectEqual(x[i] / y[i], v);
+ }
+ }
+ const d1 = @divExact(x, y);
+ for (@as([4]T, d1)) |v, i| {
+ expectEqual(@divExact(x[i], y[i]), v);
+ }
+ const d2 = @divFloor(x, y);
+ for (@as([4]T, d2)) |v, i| {
+ expectEqual(@divFloor(x[i], y[i]), v);
+ }
+ const d3 = @divTrunc(x, y);
+ for (@as([4]T, d3)) |v, i| {
+ expectEqual(@divTrunc(x[i], y[i]), v);
+ }
+ }
+
+ fn doTheTestMod(comptime T: type, x: Vector(4, T), y: Vector(4, T)) void {
+ if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) {
+ const r0 = x % y;
+ for (@as([4]T, r0)) |v, i| {
+ expectEqual(x[i] % y[i], v);
+ }
+ }
+ const r1 = @mod(x, y);
+ for (@as([4]T, r1)) |v, i| {
+ expectEqual(@mod(x[i], y[i]), v);
+ }
+ const r2 = @rem(x, y);
+ for (@as([4]T, r2)) |v, i| {
+ expectEqual(@rem(x[i], y[i]), v);
+ }
+ }
+
+ fn doTheTest() void {
+ // https://github.com/ziglang/zig/issues/4952
+ if (builtin.target.os.tag != .windows) {
+ doTheTestDiv(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, -1.0, -2.0 });
+ }
+
+ doTheTestDiv(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, -1.0, -2.0 });
+ doTheTestDiv(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, -1.0, -2.0 });
+
+ // https://github.com/ziglang/zig/issues/4952
+ if (builtin.target.os.tag != .windows) {
+ doTheTestMod(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, 0.5, 3.0 });
+ }
+ doTheTestMod(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, 0.5, 3.0 });
+ doTheTestMod(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, 0.5, 3.0 });
+
+ doTheTestDiv(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, -1, -2 });
+ doTheTestDiv(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, -1, -2 });
+ doTheTestDiv(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, -1, -2 });
+ doTheTestDiv(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, -1, -2 });
+
+ doTheTestMod(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, 4, 8 });
+ doTheTestMod(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, 4, 8 });
+ doTheTestMod(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, 4, 8 });
+ doTheTestMod(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, 4, 8 });
+
+ doTheTestDiv(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 });
+ doTheTestDiv(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
+ doTheTestDiv(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
+ doTheTestDiv(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });
+
+ doTheTestMod(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 });
+ doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
+ doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
+ doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector bitwise not operator" {
+ const S = struct {
+ fn doTheTestNot(comptime T: type, x: Vector(4, T)) void {
+ var y = ~x;
+ for (@as([4]T, y)) |v, i| {
+ expectEqual(~x[i], v);
+ }
+ }
+ fn doTheTest() void {
+ doTheTestNot(u8, [_]u8{ 0, 2, 4, 255 });
+ doTheTestNot(u16, [_]u16{ 0, 2, 4, 255 });
+ doTheTestNot(u32, [_]u32{ 0, 2, 4, 255 });
+ doTheTestNot(u64, [_]u64{ 0, 2, 4, 255 });
+
+ doTheTestNot(u8, [_]u8{ 0, 2, 4, 255 });
+ doTheTestNot(u16, [_]u16{ 0, 2, 4, 255 });
+ doTheTestNot(u32, [_]u32{ 0, 2, 4, 255 });
+ doTheTestNot(u64, [_]u64{ 0, 2, 4, 255 });
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector shift operators" {
+ // TODO investigate why this fails when cross-compiled to wasm.
+ if (builtin.target.os.tag == .wasi) return error.SkipZigTest;
+
+ const S = struct {
+ fn doTheTestShift(x: anytype, y: anytype) void {
+ const N = @typeInfo(@TypeOf(x)).Array.len;
+ const TX = @typeInfo(@TypeOf(x)).Array.child;
+ const TY = @typeInfo(@TypeOf(y)).Array.child;
+
+ var xv = @as(Vector(N, TX), x);
+ var yv = @as(Vector(N, TY), y);
+
+ var z0 = xv >> yv;
+ for (@as([N]TX, z0)) |v, i| {
+ expectEqual(x[i] >> y[i], v);
+ }
+ var z1 = xv << yv;
+ for (@as([N]TX, z1)) |v, i| {
+ expectEqual(x[i] << y[i], v);
+ }
+ }
+ fn doTheTestShiftExact(x: anytype, y: anytype, dir: enum { Left, Right }) void {
+ const N = @typeInfo(@TypeOf(x)).Array.len;
+ const TX = @typeInfo(@TypeOf(x)).Array.child;
+ const TY = @typeInfo(@TypeOf(y)).Array.child;
+
+ var xv = @as(Vector(N, TX), x);
+ var yv = @as(Vector(N, TY), y);
+
+ var z = if (dir == .Left) @shlExact(xv, yv) else @shrExact(xv, yv);
+ for (@as([N]TX, z)) |v, i| {
+ const check = if (dir == .Left) x[i] << y[i] else x[i] >> y[i];
+ expectEqual(check, v);
+ }
+ }
+ fn doTheTest() void {
+ doTheTestShift([_]u8{ 0, 2, 4, math.maxInt(u8) }, [_]u3{ 2, 0, 2, 7 });
+ doTheTestShift([_]u16{ 0, 2, 4, math.maxInt(u16) }, [_]u4{ 2, 0, 2, 15 });
+ doTheTestShift([_]u24{ 0, 2, 4, math.maxInt(u24) }, [_]u5{ 2, 0, 2, 23 });
+ doTheTestShift([_]u32{ 0, 2, 4, math.maxInt(u32) }, [_]u5{ 2, 0, 2, 31 });
+ doTheTestShift([_]u64{ 0xfe, math.maxInt(u64) }, [_]u6{ 0, 63 });
+
+ doTheTestShift([_]i8{ 0, 2, 4, math.maxInt(i8) }, [_]u3{ 2, 0, 2, 7 });
+ doTheTestShift([_]i16{ 0, 2, 4, math.maxInt(i16) }, [_]u4{ 2, 0, 2, 7 });
+ doTheTestShift([_]i24{ 0, 2, 4, math.maxInt(i24) }, [_]u5{ 2, 0, 2, 7 });
+ doTheTestShift([_]i32{ 0, 2, 4, math.maxInt(i32) }, [_]u5{ 2, 0, 2, 7 });
+ doTheTestShift([_]i64{ 0xfe, math.maxInt(i64) }, [_]u6{ 0, 63 });
+
+ doTheTestShiftExact([_]u8{ 0, 1, 1 << 7, math.maxInt(u8) ^ 1 }, [_]u3{ 4, 0, 7, 1 }, .Right);
+ doTheTestShiftExact([_]u16{ 0, 1, 1 << 15, math.maxInt(u16) ^ 1 }, [_]u4{ 4, 0, 15, 1 }, .Right);
+ doTheTestShiftExact([_]u24{ 0, 1, 1 << 23, math.maxInt(u24) ^ 1 }, [_]u5{ 4, 0, 23, 1 }, .Right);
+ doTheTestShiftExact([_]u32{ 0, 1, 1 << 31, math.maxInt(u32) ^ 1 }, [_]u5{ 4, 0, 31, 1 }, .Right);
+ doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 63, 0 }, .Right);
+
+ doTheTestShiftExact([_]u8{ 0, 1, 1, math.maxInt(u8) ^ (1 << 7) }, [_]u3{ 4, 0, 7, 1 }, .Left);
+ doTheTestShiftExact([_]u16{ 0, 1, 1, math.maxInt(u16) ^ (1 << 15) }, [_]u4{ 4, 0, 15, 1 }, .Left);
+ doTheTestShiftExact([_]u24{ 0, 1, 1, math.maxInt(u24) ^ (1 << 23) }, [_]u5{ 4, 0, 23, 1 }, .Left);
+ doTheTestShiftExact([_]u32{ 0, 1, 1, math.maxInt(u32) ^ (1 << 31) }, [_]u5{ 4, 0, 31, 1 }, .Left);
+ doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 0, 63 }, .Left);
+ }
+ };
+
+ switch (builtin.target.cpu.arch) {
+ .i386,
+ .aarch64,
+ .aarch64_be,
+ .aarch64_32,
+ .arm,
+ .armeb,
+ .thumb,
+ .thumbeb,
+ .mips,
+ .mipsel,
+ .mips64,
+ .mips64el,
+ .riscv64,
+ .sparcv9,
+ => {
+ // LLVM miscompiles on this architecture
+ // https://github.com/ziglang/zig/issues/4951
+ return error.SkipZigTest;
+ },
+ else => {},
+ }
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "vector reduce operation" {
+ const S = struct {
+ fn doTheTestReduce(comptime op: std.builtin.ReduceOp, x: anytype, expected: anytype) void {
+ const N = @typeInfo(@TypeOf(x)).Array.len;
+ const TX = @typeInfo(@TypeOf(x)).Array.child;
+
+ // wasmtime: unknown import: `env::fminf` has not been defined
+ // https://github.com/ziglang/zig/issues/8131
+ switch (builtin.target.cpu.arch) {
+ .wasm32 => switch (@typeInfo(TX)) {
+ .Float => switch (op) {
+ .Min,
+ .Max,
+ => return,
+ else => {},
+ },
+ else => {},
+ },
+ else => {},
+ }
+
+ var r = @reduce(op, @as(Vector(N, TX), x));
+ switch (@typeInfo(TX)) {
+ .Int, .Bool => expectEqual(expected, r),
+ .Float => {
+ const expected_nan = math.isNan(expected);
+ const got_nan = math.isNan(r);
+
+ if (expected_nan and got_nan) {
+ // Do this check explicitly as two NaN values are never
+ // equal.
+ } else {
+ expectApproxEqRel(expected, r, math.sqrt(math.epsilon(TX)));
+ }
+ },
+ else => unreachable,
+ }
+ }
+ fn doTheTest() void {
+ doTheTestReduce(.Add, [4]i16{ -9, -99, -999, -9999 }, @as(i32, -11106));
+ doTheTestReduce(.Add, [4]u16{ 9, 99, 999, 9999 }, @as(u32, 11106));
+ doTheTestReduce(.Add, [4]i32{ -9, -99, -999, -9999 }, @as(i32, -11106));
+ doTheTestReduce(.Add, [4]u32{ 9, 99, 999, 9999 }, @as(u32, 11106));
+ doTheTestReduce(.Add, [4]i64{ -9, -99, -999, -9999 }, @as(i64, -11106));
+ doTheTestReduce(.Add, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 11106));
+ doTheTestReduce(.Add, [4]i128{ -9, -99, -999, -9999 }, @as(i128, -11106));
+ doTheTestReduce(.Add, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 11106));
+ doTheTestReduce(.Add, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 42.9));
+ doTheTestReduce(.Add, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 42.9));
+ doTheTestReduce(.Add, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 42.9));
+
+ doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false));
+ doTheTestReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0));
+ doTheTestReduce(.And, [4]u16{ 0xffff, 0xff55, 0xaaff, 0x1010 }, @as(u16, 0x10));
+ doTheTestReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010));
+ doTheTestReduce(.And, [4]u64{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u64, 0x1010));
+
+ doTheTestReduce(.Min, [4]i16{ -1, 2, 3, 4 }, @as(i16, -1));
+ doTheTestReduce(.Min, [4]u16{ 1, 2, 3, 4 }, @as(u16, 1));
+ doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386));
+ doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9));
+
+ // LLVM 11 ERROR: Cannot select type
+ // https://github.com/ziglang/zig/issues/7138
+ if (builtin.target.cpu.arch != .aarch64) {
+ doTheTestReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386));
+ doTheTestReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9));
+ }
+
+ doTheTestReduce(.Min, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, -386));
+ doTheTestReduce(.Min, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 9));
+ doTheTestReduce(.Min, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, -100.0));
+ doTheTestReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0));
+ doTheTestReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0));
+
+ doTheTestReduce(.Max, [4]i16{ -1, 2, 3, 4 }, @as(i16, 4));
+ doTheTestReduce(.Max, [4]u16{ 1, 2, 3, 4 }, @as(u16, 4));
+ doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567));
+ doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999));
+
+ // LLVM 11 ERROR: Cannot select type
+ // https://github.com/ziglang/zig/issues/7138
+ if (builtin.target.cpu.arch != .aarch64) {
+ doTheTestReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567));
+ doTheTestReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999));
+ }
+
+ doTheTestReduce(.Max, [4]i128{ 1234567, -386, 0, 3 }, @as(i128, 1234567));
+ doTheTestReduce(.Max, [4]u128{ 99, 9999, 9, 99999 }, @as(u128, 99999));
+ doTheTestReduce(.Max, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, 10.0e9));
+ doTheTestReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9));
+ doTheTestReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9));
+
+ doTheTestReduce(.Mul, [4]i16{ -1, 2, 3, 4 }, @as(i16, -24));
+ doTheTestReduce(.Mul, [4]u16{ 1, 2, 3, 4 }, @as(u16, 24));
+ doTheTestReduce(.Mul, [4]i32{ -9, -99, -999, 999 }, @as(i32, -889218891));
+ doTheTestReduce(.Mul, [4]u32{ 1, 2, 3, 4 }, @as(u32, 24));
+ doTheTestReduce(.Mul, [4]i64{ 9, 99, 999, 9999 }, @as(i64, 8900199891));
+ doTheTestReduce(.Mul, [4]u64{ 9, 99, 999, 9999 }, @as(u64, 8900199891));
+ doTheTestReduce(.Mul, [4]i128{ -9, -99, -999, 9999 }, @as(i128, -8900199891));
+ doTheTestReduce(.Mul, [4]u128{ 9, 99, 999, 9999 }, @as(u128, 8900199891));
+ doTheTestReduce(.Mul, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 58430.7));
+ doTheTestReduce(.Mul, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 58430.7));
+ doTheTestReduce(.Mul, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 58430.7));
+
+ doTheTestReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true));
+ doTheTestReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1));
+ doTheTestReduce(.Or, [4]u16{ 0xff00, 0xff00, 0xf0, 0xf }, ~@as(u16, 0));
+ doTheTestReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0));
+ doTheTestReduce(.Or, [4]u64{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u64, 0xffffffff));
+ doTheTestReduce(.Or, [4]u128{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u128, 0xffffffff));
+
+ doTheTestReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true));
+ doTheTestReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1));
+ doTheTestReduce(.Xor, [4]u16{ 0x0000, 0x3333, 0x8888, 0x4444 }, ~@as(u16, 0));
+ doTheTestReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0));
+ doTheTestReduce(.Xor, [4]u64{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u64, 0xffffffff));
+ doTheTestReduce(.Xor, [4]u128{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, @as(u128, 0xffffffff));
+
+ // Test the reduction on vectors containing NaNs.
+ const f16_nan = math.nan(f16);
+ const f32_nan = math.nan(f32);
+ const f64_nan = math.nan(f64);
+
+ doTheTestReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
+ doTheTestReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);
+ doTheTestReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);
+
+ // LLVM 11 ERROR: Cannot select type
+ // https://github.com/ziglang/zig/issues/7138
+ if (false) {
+ doTheTestReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
+ doTheTestReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);
+ doTheTestReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);
+
+ doTheTestReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
+ doTheTestReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);
+ doTheTestReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);
+ }
+
+ doTheTestReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
+ doTheTestReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);
+ doTheTestReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/void.zig b/test/behavior/void.zig
new file mode 100644
index 0000000000..80df9fe4f9
--- /dev/null
+++ b/test/behavior/void.zig
@@ -0,0 +1,40 @@
+const expect = @import("std").testing.expect;
+
+const Foo = struct {
+ a: void,
+ b: i32,
+ c: void,
+};
+
+test "compare void with void compile time known" {
+ comptime {
+ const foo = Foo{
+ .a = {},
+ .b = 1,
+ .c = {},
+ };
+ expect(foo.a == {});
+ }
+}
+
+test "iterate over a void slice" {
+ var j: usize = 0;
+ for (times(10)) |_, i| {
+ expect(i == j);
+ j += 1;
+ }
+}
+
+fn times(n: usize) []const void {
+ return @as([*]void, undefined)[0..n];
+}
+
+test "void optional" {
+ var x: ?void = {};
+ expect(x != null);
+}
+
+test "void array as a local variable initializer" {
+ var x = [_]void{{}} ** 1004;
+ var y = x[0];
+}
diff --git a/test/behavior/wasm.zig b/test/behavior/wasm.zig
new file mode 100644
index 0000000000..24557ee19b
--- /dev/null
+++ b/test/behavior/wasm.zig
@@ -0,0 +1,8 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "memory size and grow" {
+ var prev = @wasmMemorySize(0);
+ expect(prev == @wasmMemoryGrow(0, 1));
+ expect(prev + 1 == @wasmMemorySize(0));
+}
diff --git a/test/behavior/while.zig b/test/behavior/while.zig
new file mode 100644
index 0000000000..c9207396f7
--- /dev/null
+++ b/test/behavior/while.zig
@@ -0,0 +1,289 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "while loop" {
+ var i: i32 = 0;
+ while (i < 4) {
+ i += 1;
+ }
+ expect(i == 4);
+ expect(whileLoop1() == 1);
+}
+fn whileLoop1() i32 {
+ return whileLoop2();
+}
+fn whileLoop2() i32 {
+ while (true) {
+ return 1;
+ }
+}
+
+test "static eval while" {
+ expect(static_eval_while_number == 1);
+}
+const static_eval_while_number = staticWhileLoop1();
+fn staticWhileLoop1() i32 {
+ return whileLoop2();
+}
+fn staticWhileLoop2() i32 {
+ while (true) {
+ return 1;
+ }
+}
+
+test "continue and break" {
+ runContinueAndBreakTest();
+ 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;
+ }
+ 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;
+ {
+ var i: i32 = 0;
+ while (i < 10) : (i += 1) {
+ if (i == 5) continue;
+ sum += i;
+ }
+ }
+ expect(sum == 40);
+}
+
+test "while with else" {
+ var sum: i32 = 0;
+ var i: i32 = 0;
+ var got_else: i32 = 0;
+ while (i < 10) : (i += 1) {
+ sum += 1;
+ } else {
+ got_else += 1;
+ }
+ expect(sum == 10);
+ expect(got_else == 1);
+}
+
+test "while with optional as condition" {
+ numbers_left = 10;
+ var sum: i32 = 0;
+ while (getNumberOrNull()) |value| {
+ sum += value;
+ }
+ 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;
+ expect(got_else == 0);
+ } else {
+ got_else += 1;
+ }
+ expect(sum == 45);
+ 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| {
+ expect(err == error.OutOfNumbers);
+ got_else += 1;
+ }
+ expect(sum == 45);
+ 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);
+ 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);
+ expect(result == 10);
+}
+
+test "while on error union with else result follow else prong" {
+ const result = while (returnError()) |value| {
+ break value;
+ } else |err|
+ @as(i32, 2);
+ expect(result == 2);
+}
+
+test "while on error union with else result follow break prong" {
+ const result = while (returnSuccess(10)) |value| {
+ break value;
+ } else |err|
+ @as(i32, 2);
+ 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);
+ 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);
+ 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();
+}
+
+fn testContinueOuter() void {
+ var i: usize = 0;
+ outer: while (i < 10) : (i += 1) {
+ while (true) {
+ continue :outer;
+ }
+ }
+}
+
+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;
+ expect(ok);
+ }
+ };
+ S.entry(true, false);
+ comptime 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;
+ expect(ok);
+ }
+ };
+ S.entry(true, false);
+ comptime 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;
+ expect(ok);
+ }
+ };
+ S.entry(true, false);
+ comptime S.entry(true, false);
+}
+
+test "while copies its payload" {
+ const S = struct {
+ fn doTheTest() void {
+ var tmp: ?i32 = 10;
+ while (tmp) |value| {
+ // Modify the original variable
+ tmp = null;
+ expect(value == 10);
+ }
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig
new file mode 100644
index 0000000000..785a1729dc
--- /dev/null
+++ b/test/behavior/widening.zig
@@ -0,0 +1,39 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const mem = std.mem;
+
+test "integer widening" {
+ var a: u8 = 250;
+ var b: u16 = a;
+ var c: u32 = b;
+ var d: u64 = c;
+ var e: u64 = d;
+ var f: u128 = e;
+ expect(f == a);
+}
+
+test "implicit unsigned integer to signed integer" {
+ var a: u8 = 250;
+ var b: i16 = a;
+ expect(b == 250);
+}
+
+test "float widening" {
+ var a: f16 = 12.34;
+ var b: f32 = a;
+ var c: f64 = b;
+ var d: f128 = c;
+ expect(a == b);
+ expect(b == c);
+ expect(c == d);
+}
+
+test "float widening f16 to f128" {
+ // TODO https://github.com/ziglang/zig/issues/3282
+ if (@import("builtin").target.cpu.arch == .aarch64) return error.SkipZigTest;
+ if (@import("builtin").target.cpu.arch == .powerpc64le) return error.SkipZigTest;
+
+ var x: f16 = 12.34;
+ var y: f128 = x;
+ expect(x == y);
+}