diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-04-29 15:54:04 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-04-29 15:54:04 -0700 |
| commit | 4307436b9945f814ff5731981df1d19febf3ba0a (patch) | |
| tree | efaaec94a41d632d45f255d464d9787eb02c4c9b /test/behavior/eval.zig | |
| parent | 5a02c938dafdf2bb11b2350b6ad3161ef93744f0 (diff) | |
| download | zig-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/eval.zig')
| -rw-r--r-- | test/behavior/eval.zig | 832 |
1 files changed, 832 insertions, 0 deletions
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(); + } +} |
