diff options
| author | Takeshi Yoneda <takeshi@tetrate.io> | 2021-08-09 14:39:26 +0900 |
|---|---|---|
| committer | Takeshi Yoneda <takeshi@tetrate.io> | 2021-08-09 14:39:26 +0900 |
| commit | 97560cd915008f04addc2c30af087aa89c162b02 (patch) | |
| tree | 8aed12c207ff84cc256a0c78955c23b61129ba22 /test | |
| parent | 7814a2bd4a3ec22cd9548c622f7dc837dba968f7 (diff) | |
| parent | 799fedf612aa8742c446b015c12d21707a1dbec0 (diff) | |
| download | zig-97560cd915008f04addc2c30af087aa89c162b02.tar.gz zig-97560cd915008f04addc2c30af087aa89c162b02.zip | |
Merge remote-tracking branch 'origin' into libc-wasi-test
Diffstat (limited to 'test')
33 files changed, 2181 insertions, 1462 deletions
diff --git a/test/behavior.zig b/test/behavior.zig index 0055638335..a800b38458 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -2,11 +2,14 @@ const builtin = @import("builtin"); test { // Tests that pass for both. - {} + _ = @import("behavior/bool.zig"); + _ = @import("behavior/basic.zig"); + _ = @import("behavior/generics.zig"); + _ = @import("behavior/eval.zig"); + _ = @import("behavior/pointers.zig"); + _ = @import("behavior/if.zig"); - if (builtin.zig_is_stage2) { - // Tests that only pass for stage2. - } else { + if (!builtin.zig_is_stage2) { // Tests that only pass for stage1. _ = @import("behavior/align.zig"); _ = @import("behavior/alignof.zig"); @@ -20,7 +23,6 @@ test { _ = @import("behavior/bit_shifting.zig"); _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bitreverse.zig"); - _ = @import("behavior/bool.zig"); _ = @import("behavior/bugs/1025.zig"); _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1111.zig"); @@ -89,35 +91,37 @@ test { _ = @import("behavior/enum.zig"); _ = @import("behavior/enum_with_members.zig"); _ = @import("behavior/error.zig"); - _ = @import("behavior/eval.zig"); + _ = @import("behavior/eval_stage1.zig"); _ = @import("behavior/field_parent_ptr.zig"); _ = @import("behavior/floatop.zig"); _ = @import("behavior/fn.zig"); _ = @import("behavior/fn_in_struct_in_comptime.zig"); _ = @import("behavior/fn_delegation.zig"); _ = @import("behavior/for.zig"); - _ = @import("behavior/generics.zig"); + _ = @import("behavior/generics_stage1.zig"); _ = @import("behavior/hasdecl.zig"); _ = @import("behavior/hasfield.zig"); - _ = @import("behavior/if.zig"); + _ = @import("behavior/if_stage1.zig"); _ = @import("behavior/import.zig"); _ = @import("behavior/incomplete_struct_param_tld.zig"); _ = @import("behavior/inttoptr.zig"); _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/math.zig"); + _ = @import("behavior/maximum_minimum.zig"); _ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/misc.zig"); _ = @import("behavior/muladd.zig"); _ = @import("behavior/namespace_depends_on_compile_var.zig"); _ = @import("behavior/null.zig"); _ = @import("behavior/optional.zig"); - _ = @import("behavior/pointers.zig"); + _ = @import("behavior/pointers_stage1.zig"); _ = @import("behavior/popcount.zig"); _ = @import("behavior/ptrcast.zig"); _ = @import("behavior/pub_enum.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("behavior/reflection.zig"); _ = @import("behavior/shuffle.zig"); + _ = @import("behavior/select.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); _ = @import("behavior/slice.zig"); _ = @import("behavior/slice_sentinel_comptime.zig"); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig new file mode 100644 index 0000000000..1372dfaeeb --- /dev/null +++ b/test/behavior/basic.zig @@ -0,0 +1,164 @@ +const std = @import("std"); +const mem = std.mem; +const expect = std.testing.expect; + +// normal comment + +/// this is a documentation comment +/// doc comment line 2 +fn emptyFunctionWithComments() void {} + +test "empty function with comments" { + emptyFunctionWithComments(); +} + +test "truncate" { + try expect(testTruncate(0x10fd) == 0xfd); + comptime try expect(testTruncate(0x10fd) == 0xfd); +} +fn testTruncate(x: u32) u8 { + return @truncate(u8, x); +} + +const g1: i32 = 1233 + 1; +var g2: i32 = 0; + +test "global variables" { + try expect(g2 == 0); + g2 = g1; + try expect(g2 == 1234); +} + +test "comptime keyword on expressions" { + const x: i32 = comptime x: { + break :x 1 + 2 + 3; + }; + try expect(x == comptime 6); +} + +test "type equality" { + try expect(*const u8 != *u8); +} + +test "pointer dereferencing" { + var x = @as(i32, 3); + const y = &x; + + y.* += 1; + + try expect(x == 4); + try expect(y.* == 4); +} + +test "const expression eval handling of variables" { + var x = true; + while (x) { + x = false; + } +} + +test "character literals" { + try expect('\'' == single_quote); +} +const single_quote = '\''; + +test "non const ptr to aliased type" { + const int = i32; + try expect(?*int == ?*i32); +} + +test "cold function" { + thisIsAColdFn(); + comptime thisIsAColdFn(); +} + +fn thisIsAColdFn() void { + @setCold(true); +} + +test "unicode escape in character literal" { + var a: u24 = '\u{01f4a9}'; + try expect(a == 128169); +} + +test "unicode character in character literal" { + try expect('💩' == 128169); +} + +fn first4KeysOfHomeRow() []const u8 { + return "aoeu"; +} + +test "return string from function" { + try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); +} + +test "hex escape" { + try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); +} + +test "multiline string" { + const s1 = + \\one + \\two) + \\three + ; + const s2 = "one\ntwo)\nthree"; + try expect(mem.eql(u8, s1, s2)); +} + +test "multiline string comments at start" { + const s1 = + //\\one + \\two) + \\three + ; + const s2 = "two)\nthree"; + try expect(mem.eql(u8, s1, s2)); +} + +test "multiline string comments at end" { + const s1 = + \\one + \\two) + //\\three + ; + const s2 = "one\ntwo)"; + try expect(mem.eql(u8, s1, s2)); +} + +test "multiline string comments in middle" { + const s1 = + \\one + //\\two) + \\three + ; + const s2 = "one\nthree"; + try 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"; + try expect(mem.eql(u8, s1, s2)); +} + +test "call result of if else expression" { + try expect(mem.eql(u8, f2(true), "a")); + try 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"; +} diff --git a/test/behavior/bool.zig b/test/behavior/bool.zig index dfa02a6bfd..447f9e02e6 100644 --- a/test/behavior/bool.zig +++ b/test/behavior/bool.zig @@ -33,3 +33,47 @@ test "compile time bool not" { try expect(not_global_f); try expect(!not_global_t); } + +test "short circuit" { + try testShortCircuit(false, true); + comptime try 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: { + try expect(f); + break :x f; + }) { + hit_1 = t; + } + if (f or x: { + hit_2 = t; + break :x f; + }) { + try expect(f); + } + + if (t and x: { + hit_3 = t; + break :x f; + }) { + try expect(f); + } + if (f and x: { + try expect(f); + break :x f; + }) { + try expect(f); + } else { + hit_4 = t; + } + try expect(hit_1); + try expect(hit_2); + try expect(hit_3); + try expect(hit_4); +} diff --git a/test/behavior/bugs/6456.zig b/test/behavior/bugs/6456.zig index 8078ab147f..44fdfd69ba 100644 --- a/test/behavior/bugs/6456.zig +++ b/test/behavior/bugs/6456.zig @@ -13,7 +13,7 @@ test "issue 6456" { comptime { var fields: []const StructField = &[0]StructField{}; - var it = std.mem.tokenize(text, "\n"); + var it = std.mem.tokenize(u8, text, "\n"); while (it.next()) |name| { fields = fields ++ &[_]StructField{StructField{ .alignment = 0, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 566ed74e2d..a97aab7bb3 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -63,146 +63,12 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { return result; } -test "statically initialized list" { - try expect(static_point_list[0].x == 1); - try expect(static_point_list[0].y == 2); - try expect(static_point_list[1].x == 3); - try 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" { - try expect(static_vec3.data[2] == 1.0); - try 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; try expect(@sizeOf(@TypeOf(array)) == 20); } const array_size: u8 = 20; -test "constant struct with negation" { - try 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; - try 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; - try expect(y[3] == 4); -} -const st_init_arr_lit_x = [_]u8{ - 1, - 2, - 3, - 4, -}; - -test "const slice" { - comptime { - const a = "1234567890"; - try expect(a.len == 10); - const b = a[1..2]; - try expect(b.len == 1); - try expect(b[0] == '2'); - } -} - -test "try to trick eval with runtime if" { - try 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; - _ = result; - } - 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; - _ = result; - } - comptime { - try expect(i == 2); - } -} - fn max(comptime T: type, a: T, b: T) T { if (T == bool) { return a or b; @@ -229,52 +95,6 @@ test "inlined block and runtime block phi" { } } -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" { - try expect(performFn('t', 1) == 6); - try expect(performFn('o', 0) == 1); - try expect(performFn('w', 99) == 99); -} - test "eval @setRuntimeSafety at compile-time" { const result = comptime fnWithSetRuntimeSafety(); try expect(result == 1234); @@ -285,90 +105,6 @@ fn fnWithSetRuntimeSafety() i32 { return 1234; } -test "eval @setFloatMode at compile-time" { - const result = comptime fnWithFloatMode(); - try 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" { - try expect(bound_fn() == 1237); -} - -test "ptr to local array argument at comptime" { - comptime { - var bytes: [10]u8 = undefined; - modifySomeBytes(bytes[0..]); - try expect(bytes[0] == 'a'); - try 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" { - try expect(foo_ref.name[0] == 'a'); - foo_ref.name = "b"; - try 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" { - try expect(global_array[5] == 5 * 5); - try 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; @@ -377,231 +113,6 @@ test "compile-time downcast when the bits fit" { } } -const hi1 = "hi"; -const hi2 = hi1; -test "const global shares pointer with other same one" { - try assertEqualPtrs(&hi1[0], &hi2[0]); - comptime try expect(&hi1[0] == &hi2[0]); -} -fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void { - try 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; - } - try expect(sum == 500500); - } -} - -test "float literal at compile time not lossy" { - try expect(16777216.0 + 1.0 == 16777217.0); - try expect(9007199254740992.0 + 1.0 == 9007199254740993.0); -} - -test "f32 at compile time is lossy" { - try expect(@as(f32, 1 << 24) + 1 == 1 << 24); -} - -test "f64 at compile time is lossy" { - try expect(@as(f64, 1 << 53) + 1 == 1 << 53); -} - -test "f128 at compile time is lossy" { - try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); -} - -test { - comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192); -} - -pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { - _ = field_name; - return struct { - pub const Node = struct {}; - }; -} - -test "string literal used as comptime slice is memoized" { - const a = "link"; - const b = "link"; - comptime try expect(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); - comptime try expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); -} - -test "comptime slice of undefined pointer of length 0" { - const slice1 = @as([*]i32, undefined)[0..0]; - try expect(slice1.len == 0); - const slice2 = @as([*]i32, undefined)[100..100]; - try 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..]); - try expect(s[0] == 0x1020304); - try expect(s[1] == 0x5060708); - try expect(s[2] == 0x90a0b0c); - try expect(s[3] == 0xd0e0f10); -} - -test "comptime function with the same args is memoized" { - comptime { - try expect(MakeType(i32) == MakeType(i32)); - try 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); - try 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" { - try expect(doesAlotT(u32, 2) == 2); -} - -test "comptime slice of slice preserves comptime var" { - comptime { - var buff: [10]u8 = undefined; - buff[0..][0..][0] = 1; - try 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; - try 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 }; - try expect(foo.read_x() == 1); - foo.x = 2; - try 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; - try 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; - }; - - try expect(ct_shifted == rt_shifted); -} - -test "comptime shl" { - var a: u128 = 3; - var b: u7 = 63; - var c: u128 = 3 << 63; - try expectEqual(a << b, c); -} - -test "runtime 128 bit integer division" { - var a: u128 = 152313999999999991610955792383; - var b: u128 = 10000000000000000000; - var c = a / b; - try 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; - try expect(diamond_info.version == 0); - try expect(res.version == 1); - } -} - test "pointer to type" { comptime { var T: type = i32; @@ -614,233 +125,8 @@ test "pointer to type" { } } -test "slice of type" { - comptime { - var types_array = [_]type{ i32, f64, type }; - for (types_array) |T, i| { - switch (i) { - 0 => try expect(T == i32), - 1 => try expect(T == f64), - 2 => try expect(T == type), - else => unreachable, - } - } - for (types_array[0..]) |T, i| { - switch (i) { - 0 => try expect(T == i32), - 1 => try expect(T == f64), - 2 => try 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; - try 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(); - try expect(b == 2); -} - -test "@tagName of @typeInfo" { - const str = @tagName(@typeInfo(u8)); - try 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" { - try 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) try expect(x); - if (i == 1) try 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; - } - try 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 { - _ = T; -} - -test "zero extend from u0 to u1" { - var zero_u0: u0 = 0; - var zero_u1: u1 = zero_u0; - try expect(zero_u1 == 0); -} - -test "bit shift a u1" { - var x: u1 = 1; - var y = x << 0; - try 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]; - - try expect(sliceA[1] == 2); - try 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; - try expect(str.len == 1); - try expect(std.mem.eql(u8, str, "1")); - - const str2 = bs ++ ""; - try expect(str2.len == 1); - try expect(std.mem.eql(u8, str2, "1")); -} - -test "comptime bitwise operators" { - comptime { - try expect(3 & 1 == 1); - try expect(3 & -1 == 3); - try expect(-3 & -1 == -3); - try expect(3 | -1 == -1); - try expect(-3 | -1 == -1); - try expect(3 ^ -1 == -4); - try expect(-3 ^ -1 == 2); - try expect(~@as(i8, -1) == 0); - try expect(~@as(i128, -1) == 0); - try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff); - } -} - -test "*align(1) u16 is the same as *align(1:0:2) u16" { - comptime { - try expect(*align(1:0:2) u16 == *align(1) u16); - try expect(*align(2:0:2) u16 == *u16); - } -} - -test "array concatenation forces comptime" { - var a = oneItem(3) ++ oneItem(4); - try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 })); -} - -test "array multiplication forces comptime" { - var a = oneItem(3) ** scalar(2); - try 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; - try 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 { - _ = self; - } - }; - - const DynamicLinker = struct { - buffer: [255]u8 = undefined, - }; - }; - - comptime { - S.CrossTarget.parse(); - S.CrossTarget.parse(); - } -} diff --git a/test/behavior/eval_stage1.zig b/test/behavior/eval_stage1.zig new file mode 100644 index 0000000000..3599d5a477 --- /dev/null +++ b/test/behavior/eval_stage1.zig @@ -0,0 +1,717 @@ +const std = @import("std"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; + +test "statically initialized list" { + try expect(static_point_list[0].x == 1); + try expect(static_point_list[0].y == 2); + try expect(static_point_list[1].x == 3); + try 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" { + try expect(static_vec3.data[2] == 1.0); + try 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 struct with negation" { + try 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; + try 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; + try expect(y[3] == 4); +} +const st_init_arr_lit_x = [_]u8{ + 1, + 2, + 3, + 4, +}; + +test "const slice" { + comptime { + const a = "1234567890"; + try expect(a.len == 10); + const b = a[1..2]; + try expect(b.len == 1); + try expect(b[0] == '2'); + } +} + +test "try to trick eval with runtime if" { + try 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; + _ = result; + } + 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; + _ = result; + } + comptime { + try expect(i == 2); + } +} + +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" { + try expect(performFn('t', 1) == 6); + try expect(performFn('o', 0) == 1); + try expect(performFn('w', 99) == 99); +} + +test "eval @setFloatMode at compile-time" { + const result = comptime fnWithFloatMode(); + try 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" { + try expect(bound_fn() == 1237); +} + +test "ptr to local array argument at comptime" { + comptime { + var bytes: [10]u8 = undefined; + modifySomeBytes(bytes[0..]); + try expect(bytes[0] == 'a'); + try 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" { + try expect(foo_ref.name[0] == 'a'); + foo_ref.name = "b"; + try 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" { + try expect(global_array[5] == 5 * 5); + try 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; +}; + +const hi1 = "hi"; +const hi2 = hi1; +test "const global shares pointer with other same one" { + try assertEqualPtrs(&hi1[0], &hi2[0]); + comptime try expect(&hi1[0] == &hi2[0]); +} +fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void { + try 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; + } + try expect(sum == 500500); + } +} + +test "float literal at compile time not lossy" { + try expect(16777216.0 + 1.0 == 16777217.0); + try expect(9007199254740992.0 + 1.0 == 9007199254740993.0); +} + +test "f32 at compile time is lossy" { + try expect(@as(f32, 1 << 24) + 1 == 1 << 24); +} + +test "f64 at compile time is lossy" { + try expect(@as(f64, 1 << 53) + 1 == 1 << 53); +} + +test "f128 at compile time is lossy" { + try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); +} + +test { + comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192); +} + +pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { + _ = field_name; + return struct { + pub const Node = struct {}; + }; +} + +test "string literal used as comptime slice is memoized" { + const a = "link"; + const b = "link"; + comptime try expect(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime try expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); +} + +test "comptime slice of undefined pointer of length 0" { + const slice1 = @as([*]i32, undefined)[0..0]; + try expect(slice1.len == 0); + const slice2 = @as([*]i32, undefined)[100..100]; + try 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..]); + try expect(s[0] == 0x1020304); + try expect(s[1] == 0x5060708); + try expect(s[2] == 0x90a0b0c); + try expect(s[3] == 0xd0e0f10); +} + +test "comptime function with the same args is memoized" { + comptime { + try expect(MakeType(i32) == MakeType(i32)); + try 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); + try 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" { + try expect(doesAlotT(u32, 2) == 2); +} + +test "comptime slice of slice preserves comptime var" { + comptime { + var buff: [10]u8 = undefined; + buff[0..][0..][0] = 1; + try 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; + try 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 }; + try expect(foo.read_x() == 1); + foo.x = 2; + try 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; + try 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; + }; + + try expect(ct_shifted == rt_shifted); +} + +test "comptime shl" { + var a: u128 = 3; + var b: u7 = 63; + var c: u128 = 3 << 63; + try expectEqual(a << b, c); +} + +test "runtime 128 bit integer division" { + var a: u128 = 152313999999999991610955792383; + var b: u128 = 10000000000000000000; + var c = a / b; + try 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; + try expect(diamond_info.version == 0); + try expect(res.version == 1); + } +} + +test "slice of type" { + comptime { + var types_array = [_]type{ i32, f64, type }; + for (types_array) |T, i| { + switch (i) { + 0 => try expect(T == i32), + 1 => try expect(T == f64), + 2 => try expect(T == type), + else => unreachable, + } + } + for (types_array[0..]) |T, i| { + switch (i) { + 0 => try expect(T == i32), + 1 => try expect(T == f64), + 2 => try 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; + try 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(); + try expect(b == 2); +} + +test "@tagName of @typeInfo" { + const str = @tagName(@typeInfo(u8)); + try 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" { + try 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) try expect(x); + if (i == 1) try 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; + } + try 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 { + _ = T; +} + +test "zero extend from u0 to u1" { + var zero_u0: u0 = 0; + var zero_u1: u1 = zero_u0; + try expect(zero_u1 == 0); +} + +test "bit shift a u1" { + var x: u1 = 1; + var y = x << 0; + try 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]; + + try expect(sliceA[1] == 2); + try 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; + try expect(str.len == 1); + try expect(std.mem.eql(u8, str, "1")); + + const str2 = bs ++ ""; + try expect(str2.len == 1); + try expect(std.mem.eql(u8, str2, "1")); +} + +test "comptime bitwise operators" { + comptime { + try expect(3 & 1 == 1); + try expect(3 & -1 == 3); + try expect(-3 & -1 == -3); + try expect(3 | -1 == -1); + try expect(-3 | -1 == -1); + try expect(3 ^ -1 == -4); + try expect(-3 ^ -1 == 2); + try expect(~@as(i8, -1) == 0); + try expect(~@as(i128, -1) == 0); + try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff); + } +} + +test "*align(1) u16 is the same as *align(1:0:2) u16" { + comptime { + try expect(*align(1:0:2) u16 == *align(1) u16); + try expect(*align(2:0:2) u16 == *u16); + } +} + +test "array concatenation forces comptime" { + var a = oneItem(3) ++ oneItem(4); + try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 })); +} + +test "array multiplication forces comptime" { + var a = oneItem(3) ** scalar(2); + try 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 "comptime assign int to optional int" { + comptime { + var x: ?i32 = null; + x = 2; + x.? *= 10; + try 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 { + _ = self; + } + }; + + const DynamicLinker = struct { + buffer: [255]u8 = undefined, + }; + }; + + comptime { + S.CrossTarget.parse(); + S.CrossTarget.parse(); + } +} diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 104752607a..67fb1def8b 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -1,11 +1,29 @@ const std = @import("std"); +const builtin = @import("builtin"); const testing = std.testing; const expect = testing.expect; const expectEqual = testing.expectEqual; +test "one param, explicit comptime" { + var x: usize = 0; + x += checkSize(i32); + x += checkSize(bool); + x += checkSize(bool); + try expect(x == 6); +} + +fn checkSize(comptime T: type) usize { + return @sizeOf(T); +} + test "simple generic fn" { try expect(max(i32, 3, -1) == 3); - try expect(max(f32, 0.123, 0.456) == 0.456); + try expect(max(u8, 1, 100) == 100); + if (!builtin.zig_is_stage2) { + // TODO: stage2 is incorrectly emitting the following: + // error: cast of value 1.23e-01 to type 'f32' loses information + try expect(max(f32, 0.123, 0.456) == 0.456); + } try expect(add(2, 3) == 5); } @@ -40,130 +58,23 @@ test "fn with comptime args" { try expect(sameButWithFloats(0.43, 0.49) == 0.49); } -test "var params" { +test "anytype params" { try expect(max_i32(12, 34) == 34); try expect(max_f64(1.2, 3.4) == 3.4); + comptime { + try expect(max_i32(12, 34) == 34); + try expect(max_f64(1.2, 3.4) == 3.4); + } } -test { - comptime try expect(max_i32(12, 34) == 34); - comptime try expect(max_f64(1.2, 3.4) == 3.4); -} - -fn max_var(a: anytype, b: anytype) @TypeOf(a + b) { +fn max_anytype(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); + return max_anytype(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; - try expect(list.prealloc_items.len == 8); - try 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, - }; - try expect(a1.value == 13); - try expect(a1.value == a1.getVal()); - try 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" { - try 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" { - try 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" { - try expect(getFirstByte(u8, &[_]u8{13}) == 13); - try 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" { - try expect(foos[0](true)); - try 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 { - try 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; - try S.f(u8, &x); + return max_anytype(a, b); } diff --git a/test/behavior/generics_stage1.zig b/test/behavior/generics_stage1.zig new file mode 100644 index 0000000000..4b768a5b4f --- /dev/null +++ b/test/behavior/generics_stage1.zig @@ -0,0 +1,110 @@ +const std = @import("std"); +const testing = std.testing; +const expect = testing.expect; +const expectEqual = testing.expectEqual; + +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; + try expect(list.prealloc_items.len == 8); + try 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, + }; + try expect(a1.value == 13); + try expect(a1.value == a1.getVal()); + try 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" { + try 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" { + try 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" { + try expect(getFirstByte(u8, &[_]u8{13}) == 13); + try 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" { + try expect(foos[0](true)); + try 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 { + try 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; + try S.f(u8, &x); +} diff --git a/test/behavior/if.zig b/test/behavior/if.zig index e8c84f4570..191d4817df 100644 --- a/test/behavior/if.zig +++ b/test/behavior/if.zig @@ -65,45 +65,3 @@ test "labeled break inside comptime if inside runtime if" { } try 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; - try 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; - try expect(x == 2); - - var b = true; - const y: i32 = if (b) 1 else 2; - try expect(y == 1); - } - }; - try S.doTheTest(false); - comptime try S.doTheTest(false); -} - -test "while copies its payload" { - const S = struct { - fn doTheTest() !void { - var tmp: ?i32 = 10; - if (tmp) |value| { - // Modify the original variable - tmp = null; - try expectEqual(@as(i32, 10), value); - } else unreachable; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} diff --git a/test/behavior/if_stage1.zig b/test/behavior/if_stage1.zig new file mode 100644 index 0000000000..36500fbaee --- /dev/null +++ b/test/behavior/if_stage1.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; + +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; + try 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; + try expect(x == 2); + + var b = true; + const y: i32 = if (b) 1 else 2; + try expect(y == 1); + } + }; + try S.doTheTest(false); + comptime try S.doTheTest(false); +} + +test "while copies its payload" { + const S = struct { + fn doTheTest() !void { + var tmp: ?i32 = 10; + if (tmp) |value| { + // Modify the original variable + tmp = null; + try expectEqual(@as(i32, 10), value); + } else unreachable; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig new file mode 100644 index 0000000000..5fef818f2b --- /dev/null +++ b/test/behavior/maximum_minimum.zig @@ -0,0 +1,58 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const mem = std.mem; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const Vector = std.meta.Vector; + +test "@maximum" { + const S = struct { + fn doTheTest() !void { + try expectEqual(@as(i32, 10), @maximum(@as(i32, -3), @as(i32, 10))); + try expectEqual(@as(f32, 3.2), @maximum(@as(f32, 3.2), @as(f32, 0.68))); + + var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; + var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; + var x = @maximum(a, b); + try expect(mem.eql(i32, &@as([4]i32, x), &[4]i32{ 2147483647, 2147483647, 30, 40 })); + + var c: Vector(4, f32) = [4]f32{ 0, 0.4, -2.4, 7.8 }; + var d: Vector(4, f32) = [4]f32{ -0.23, 0.42, -0.64, 0.9 }; + var y = @maximum(c, d); + try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ 0, 0.42, -0.64, 7.8 })); + + var e: Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 }; + var f: Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 }; + var z = @maximum(e, f); + try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 })); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@minimum" { + const S = struct { + fn doTheTest() !void { + try expectEqual(@as(i32, -3), @minimum(@as(i32, -3), @as(i32, 10))); + try expectEqual(@as(f32, 0.68), @minimum(@as(f32, 3.2), @as(f32, 0.68))); + + var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; + var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; + var x = @minimum(a, b); + try expect(mem.eql(i32, &@as([4]i32, x), &[4]i32{ 1, -2, 3, 4 })); + + var c: Vector(4, f32) = [4]f32{ 0, 0.4, -2.4, 7.8 }; + var d: Vector(4, f32) = [4]f32{ -0.23, 0.42, -0.64, 0.9 }; + var y = @minimum(c, d); + try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ -0.23, 0.4, -2.4, 0.9 })); + + var e: Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 }; + var f: Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 }; + var z = @maximum(e, f); + try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 })); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/misc.zig b/test/behavior/misc.zig index 6fabbf487b..466be00bd3 100644 --- a/test/behavior/misc.zig +++ b/test/behavior/misc.zig @@ -5,94 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings; 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" { - try testShortCircuit(false, true); - comptime try 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: { - try expect(f); - break :x f; - }) { - hit_1 = t; - } - if (f or x: { - hit_2 = t; - break :x f; - }) { - try expect(f); - } - - if (t and x: { - hit_3 = t; - break :x f; - }) { - try expect(f); - } - if (f and x: { - try expect(f); - break :x f; - }) { - try expect(f); - } else { - hit_4 = t; - } - try expect(hit_1); - try expect(hit_2); - try expect(hit_3); - try expect(hit_4); -} - -test "truncate" { - try expect(testTruncate(0x10fd) == 0xfd); -} -fn testTruncate(x: u32) u8 { - return @truncate(u8, x); -} - -fn first4KeysOfHomeRow() []const u8 { - return "aoeu"; -} - -test "return string from function" { - try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); -} - -const g1: i32 = 1233 + 1; -var g2: i32 = 0; - -test "global variables" { - try expect(g2 == 0); - g2 = g1; - try expect(g2 == 1234); -} - test "memcpy and memset intrinsics" { var foo: [20]u8 = undefined; var bar: [20]u8 = undefined; @@ -103,13 +15,6 @@ test "memcpy and memset intrinsics" { if (bar[11] != 'A') unreachable; } -test "builtin static eval" { - const x: i32 = comptime x: { - break :x 1 + 2 + 3; - }; - try expect(x == comptime 6); -} - test "slicing" { var array: [20]i32 = undefined; @@ -135,10 +40,6 @@ test "constant equal function pointers" { fn emptyFn() void {} -test "hex escape" { - try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); -} - test "string concatenation" { try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } @@ -157,59 +58,7 @@ test "string escapes" { try expectEqualStrings("\u{1234}\u{069}\u{1}", "\xe1\x88\xb4\x69\x01"); } -test "multiline string" { - const s1 = - \\one - \\two) - \\three - ; - const s2 = "one\ntwo)\nthree"; - try expect(mem.eql(u8, s1, s2)); -} - -test "multiline string comments at start" { - const s1 = - //\\one - \\two) - \\three - ; - const s2 = "two)\nthree"; - try expect(mem.eql(u8, s1, s2)); -} - -test "multiline string comments at end" { - const s1 = - \\one - \\two) - //\\three - ; - const s2 = "one\ntwo)"; - try expect(mem.eql(u8, s1, s2)); -} - -test "multiline string comments in middle" { - const s1 = - \\one - //\\two) - \\three - ; - const s2 = "one\nthree"; - try 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"; - try expect(mem.eql(u8, s1, s2)); -} - -test "multiline C string" { +test "multiline string literal is null terminated" { const s1 = \\one \\two) @@ -219,10 +68,6 @@ test "multiline C string" { try expect(std.cstr.cmp(s1, s2) == 0); } -test "type equality" { - try 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); @@ -258,17 +103,6 @@ fn testCastUndefined(x: []const u8) void { _ = x; } -test "cast small unsigned to larger signed" { - try expect(castSmallUnsignedToLargerSigned1(200) == @as(i16, 200)); - try 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" { try expect(outer() == 1234); } @@ -279,37 +113,6 @@ fn outer() i64 { return inner(); } -test "pointer dereferencing" { - var x = @as(i32, 3); - const y = &x; - - y.* += 1; - - try expect(x == 4); - try expect(y.* == 4); -} - -test "call result of if else expression" { - try expect(mem.eql(u8, f2(true), "a")); - try 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" { try test3_1(test3_foo); try test3_2(test3_bar); @@ -348,11 +151,6 @@ fn test3_2(f: Test3Foo) !void { } } -test "character literals" { - try expect('\'' == single_quote); -} -const single_quote = '\''; - test "take address of parameter" { try testTakeAddressOfParameter(12.34); } @@ -401,11 +199,6 @@ fn testPointerToVoidReturnType2() *const void { return &test_pointer_to_void_return_type_x; } -test "non const ptr to aliased type" { - const int = i32; - try expect(?*int == ?*i32); -} - test "array 2D const double ptr" { const rect_2d_vertexes = [_][1]f32{ [_]f32{1.0}, @@ -420,46 +213,6 @@ fn testArray2DConstDoublePtr(ptr: *const f32) !void { try 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 { - try expect(mem.eql(u8, @typeName(i64), "i64")); - try expect(mem.eql(u8, @typeName(*usize), "*usize")); - // https://github.com/ziglang/zig/issues/675 - try expect(mem.eql(u8, "behavior.misc.TypeFromFn(u8)", @typeName(TypeFromFn(u8)))); - try expect(mem.eql(u8, @typeName(Struct), "Struct")); - try expect(mem.eql(u8, @typeName(Union), "Union")); - try expect(mem.eql(u8, @typeName(Enum), "Enum")); - } -} - -fn TypeFromFn(comptime T: type) type { - _ = T; - return struct {}; -} - test "double implicit cast in same expression" { var x = @as(i32, @as(u16, nine())); try expect(x == 9); @@ -535,15 +288,6 @@ test "function closes over local const" { try expect(x == 1); } -test "cold function" { - thisIsAColdFn(); - comptime thisIsAColdFn(); -} - -fn thisIsAColdFn() void { - @setCold(true); -} - const PackedStruct = packed struct { a: u8, b: u8, @@ -657,15 +401,6 @@ test "thread local variable" { try expect(S.t == 1235); } -test "unicode escape in character literal" { - var a: u24 = '\u{01f4a9}'; - try expect(a == 128169); -} - -test "unicode character in character literal" { - try expect('💩' == 128169); -} - test "result location zero sized array inside struct field implicit cast to slice" { const E = struct { entries: []u32, diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index bb95d3c219..4fcd78b1d6 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -15,22 +15,6 @@ fn testDerefPtr() !void { try expect(x == 1235); } -const Foo1 = struct { - x: void, -}; - -test "dereference pointer again" { - try testDerefPtrOneVal(); - comptime try testDerefPtrOneVal(); -} - -fn testDerefPtrOneVal() !void { - // Foo1 satisfies the OnePossibleValueYes criteria - const x = &Foo1{ .x = {} }; - const y = x.*; - try expect(@TypeOf(y.x) == void); -} - test "pointer arithmetic" { var ptr: [*]const u8 = "abcd"; @@ -60,288 +44,3 @@ test "double pointer parsing" { 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; - if (false) { - ptr; - ptr2; - } -} - -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; - try expect(y == 12); -} - -test "C pointer comparison and arithmetic" { - const S = struct { - fn doTheTest() !void { - var ptr1: [*c]u32 = 0; - var ptr2 = ptr1 + 10; - try expect(ptr1 == 0); - try expect(ptr1 >= 0); - try expect(ptr1 <= 0); - // expect(ptr1 < 1); - // expect(ptr1 < one); - // expect(1 > ptr1); - // expect(one > ptr1); - try expect(ptr1 < ptr2); - try expect(ptr2 > ptr1); - try expect(ptr2 >= 40); - try expect(ptr2 == 40); - try expect(ptr2 <= 40); - ptr2 -= 10; - try expect(ptr1 == ptr2); - } - }; - try S.doTheTest(); - comptime try 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; - try expect(@TypeOf(x1) == [*c]u8); - try expect(@TypeOf(x2) == [*c]u8); - try expect(@TypeOf(x3) == [*c]u8); - try 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; - try expect(c_ptr.*.* == 'a'); - ptr_opt_many_ptr = c_ptr; - try expect(ptr_opt_many_ptr.*.?[1] == 'o'); -} - -test "implicit cast error unions with non-optional to optional pointer" { - const S = struct { - fn doTheTest() !void { - try expectError(error.Fail, foo()); - } - fn foo() anyerror!?*u8 { - return bar() orelse error.Fail; - } - fn bar() ?*u8 { - return null; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "initialize const optional C pointer to null" { - const a: ?[*c]i32 = null; - try expect(a == null); - comptime try expect(a == null); -} - -test "compare equality of optional and non-optional pointer" { - const a = @intToPtr(*const usize, 0x12345678); - const b = @intToPtr(?*usize, 0x12345678); - try expect(a == b); - try expect(b == a); -} - -test "allowzero pointer and slice" { - var ptr = @intToPtr([*]allowzero i32, 0); - var opt_ptr: ?[*]allowzero i32 = ptr; - try expect(opt_ptr != null); - try expect(@ptrToInt(ptr) == 0); - var runtime_zero: usize = 0; - var slice = ptr[runtime_zero..10]; - comptime try expect(@TypeOf(slice) == []allowzero i32); - try expect(@ptrToInt(&slice[5]) == 20); - - comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); - comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); -} - -test "assign null directly to C pointer and test null equality" { - var x: [*c]i32 = null; - try expect(x == null); - try expect(null == x); - try expect(!(x != null)); - try expect(!(null != x)); - if (x) |same_x| { - _ = same_x; - @panic("fail"); - } - var otherx: i32 = undefined; - try expect((x orelse &otherx) == &otherx); - - const y: [*c]i32 = null; - comptime try expect(y == null); - comptime try expect(null == y); - comptime try expect(!(y != null)); - comptime try expect(!(null != y)); - if (y) |same_y| { - _ = same_y; - @panic("fail"); - } - const othery: i32 = undefined; - comptime try expect((y orelse &othery) == &othery); - - var n: i32 = 1234; - var x1: [*c]i32 = &n; - try expect(!(x1 == null)); - try expect(!(null == x1)); - try expect(x1 != null); - try expect(null != x1); - try expect(x1.?.* == 1234); - if (x1) |same_x1| { - try expect(same_x1.* == 1234); - } else { - @panic("fail"); - } - try expect((x1 orelse &otherx) == x1); - - const nc: i32 = 1234; - const y1: [*c]const i32 = &nc; - comptime try expect(!(y1 == null)); - comptime try expect(!(null == y1)); - comptime try expect(y1 != null); - comptime try expect(null != y1); - comptime try expect(y1.?.* == 1234); - if (y1) |same_y1| { - try expect(same_y1.* == 1234); - } else { - @compileError("fail"); - } - comptime try 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); - try expect(std.mem.eql(u8, std.mem.spanZ(zero_ptr_again), "hello")); - } - }; - try S.doTheTest(); - comptime try 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; - try expect(ptr[4] == std.math.minInt(i32)); - } - }; - try S.doTheTest(); - comptime try 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 }; - try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731 - } - }; - try S.doTheTest(); - comptime try 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 }; - try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731 - } - }; - try S.doTheTest(); - comptime try 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 }; - try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731 - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "pointer to array at fixed address" { - const array = @intToPtr(*volatile [1]u32, 0x10); - // Silly check just to reference `array` - try expect(@ptrToInt(&array[0]) == 0x10); -} - -test "pointer arithmetic affects the alignment" { - { - var ptr: [*]align(8) u32 = undefined; - var x: usize = 1; - - try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8); - const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4 - try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4); - const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8 - try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8); - const ptr3 = ptr + 0; // no-op - try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); - const ptr4 = ptr + x; // runtime-known addend - try 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 - try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1); - const ptr2 = ptr + x; // runtime-known addend - try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1); - const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8 - try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); - const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4 - try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); - } -} - -test "@ptrToInt on null optional at comptime" { - { - const pointer = @intToPtr(?*u8, 0x000); - const x = @ptrToInt(pointer); - _ = x; - comptime try expect(0 == @ptrToInt(pointer)); - } - { - const pointer = @intToPtr(?*u8, 0xf00); - comptime try expect(0xf00 == @ptrToInt(pointer)); - } -} - -test "indexing array with sentinel returns correct type" { - var s: [:0]const u8 = "abc"; - try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0]))); -} diff --git a/test/behavior/pointers_stage1.zig b/test/behavior/pointers_stage1.zig new file mode 100644 index 0000000000..aea123a5c3 --- /dev/null +++ b/test/behavior/pointers_stage1.zig @@ -0,0 +1,305 @@ +const std = @import("std"); +const testing = std.testing; +const expect = testing.expect; +const expectError = testing.expectError; + +const Foo1 = struct { + x: void, +}; + +test "dereference pointer again" { + try testDerefPtrOneVal(); + comptime try testDerefPtrOneVal(); +} + +fn testDerefPtrOneVal() !void { + // Foo1 satisfies the OnePossibleValueYes criteria + const x = &Foo1{ .x = {} }; + const y = x.*; + try expect(@TypeOf(y.x) == void); +} + +test "assigning integer to C pointer" { + var x: i32 = 0; + var ptr: [*c]u8 = 0; + var ptr2: [*c]u8 = x; + if (false) { + ptr; + ptr2; + } +} + +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; + try expect(y == 12); +} + +test "C pointer comparison and arithmetic" { + const S = struct { + fn doTheTest() !void { + var ptr1: [*c]u32 = 0; + var ptr2 = ptr1 + 10; + try expect(ptr1 == 0); + try expect(ptr1 >= 0); + try expect(ptr1 <= 0); + // expect(ptr1 < 1); + // expect(ptr1 < one); + // expect(1 > ptr1); + // expect(one > ptr1); + try expect(ptr1 < ptr2); + try expect(ptr2 > ptr1); + try expect(ptr2 >= 40); + try expect(ptr2 == 40); + try expect(ptr2 <= 40); + ptr2 -= 10; + try expect(ptr1 == ptr2); + } + }; + try S.doTheTest(); + comptime try 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; + try expect(@TypeOf(x1) == [*c]u8); + try expect(@TypeOf(x2) == [*c]u8); + try expect(@TypeOf(x3) == [*c]u8); + try 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; + try expect(c_ptr.*.* == 'a'); + ptr_opt_many_ptr = c_ptr; + try expect(ptr_opt_many_ptr.*.?[1] == 'o'); +} + +test "implicit cast error unions with non-optional to optional pointer" { + const S = struct { + fn doTheTest() !void { + try expectError(error.Fail, foo()); + } + fn foo() anyerror!?*u8 { + return bar() orelse error.Fail; + } + fn bar() ?*u8 { + return null; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "initialize const optional C pointer to null" { + const a: ?[*c]i32 = null; + try expect(a == null); + comptime try expect(a == null); +} + +test "compare equality of optional and non-optional pointer" { + const a = @intToPtr(*const usize, 0x12345678); + const b = @intToPtr(?*usize, 0x12345678); + try expect(a == b); + try expect(b == a); +} + +test "allowzero pointer and slice" { + var ptr = @intToPtr([*]allowzero i32, 0); + var opt_ptr: ?[*]allowzero i32 = ptr; + try expect(opt_ptr != null); + try expect(@ptrToInt(ptr) == 0); + var runtime_zero: usize = 0; + var slice = ptr[runtime_zero..10]; + comptime try expect(@TypeOf(slice) == []allowzero i32); + try expect(@ptrToInt(&slice[5]) == 20); + + comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); + comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); +} + +test "assign null directly to C pointer and test null equality" { + var x: [*c]i32 = null; + try expect(x == null); + try expect(null == x); + try expect(!(x != null)); + try expect(!(null != x)); + if (x) |same_x| { + _ = same_x; + @panic("fail"); + } + var otherx: i32 = undefined; + try expect((x orelse &otherx) == &otherx); + + const y: [*c]i32 = null; + comptime try expect(y == null); + comptime try expect(null == y); + comptime try expect(!(y != null)); + comptime try expect(!(null != y)); + if (y) |same_y| { + _ = same_y; + @panic("fail"); + } + const othery: i32 = undefined; + comptime try expect((y orelse &othery) == &othery); + + var n: i32 = 1234; + var x1: [*c]i32 = &n; + try expect(!(x1 == null)); + try expect(!(null == x1)); + try expect(x1 != null); + try expect(null != x1); + try expect(x1.?.* == 1234); + if (x1) |same_x1| { + try expect(same_x1.* == 1234); + } else { + @panic("fail"); + } + try expect((x1 orelse &otherx) == x1); + + const nc: i32 = 1234; + const y1: [*c]const i32 = &nc; + comptime try expect(!(y1 == null)); + comptime try expect(!(null == y1)); + comptime try expect(y1 != null); + comptime try expect(null != y1); + comptime try expect(y1.?.* == 1234); + if (y1) |same_y1| { + try expect(same_y1.* == 1234); + } else { + @compileError("fail"); + } + comptime try 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); + try expect(std.mem.eql(u8, std.mem.spanZ(zero_ptr_again), "hello")); + } + }; + try S.doTheTest(); + comptime try 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; + try expect(ptr[4] == std.math.minInt(i32)); + } + }; + try S.doTheTest(); + comptime try 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 }; + try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731 + } + }; + try S.doTheTest(); + comptime try 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 }; + try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731 + } + }; + try S.doTheTest(); + comptime try 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 }; + try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731 + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "pointer to array at fixed address" { + const array = @intToPtr(*volatile [1]u32, 0x10); + // Silly check just to reference `array` + try expect(@ptrToInt(&array[0]) == 0x10); +} + +test "pointer arithmetic affects the alignment" { + { + var ptr: [*]align(8) u32 = undefined; + var x: usize = 1; + + try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8); + const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4 + try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4); + const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8 + try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8); + const ptr3 = ptr + 0; // no-op + try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); + const ptr4 = ptr + x; // runtime-known addend + try 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 + try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1); + const ptr2 = ptr + x; // runtime-known addend + try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1); + const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8 + try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); + const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4 + try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); + } +} + +test "@ptrToInt on null optional at comptime" { + { + const pointer = @intToPtr(?*u8, 0x000); + const x = @ptrToInt(pointer); + _ = x; + comptime try expect(0 == @ptrToInt(pointer)); + } + { + const pointer = @intToPtr(?*u8, 0xf00); + comptime try expect(0xf00 == @ptrToInt(pointer)); + } +} + +test "indexing array with sentinel returns correct type" { + var s: [:0]const u8 = "abc"; + try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0]))); +} diff --git a/test/behavior/select.zig b/test/behavior/select.zig new file mode 100644 index 0000000000..5c69094413 --- /dev/null +++ b/test/behavior/select.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const mem = std.mem; +const expect = std.testing.expect; +const Vector = std.meta.Vector; + +test "@select" { + const S = struct { + fn doTheTest() !void { + var a: Vector(4, bool) = [4]bool{ true, false, true, false }; + var b: Vector(4, i32) = [4]i32{ -1, 4, 999, -31 }; + var c: Vector(4, i32) = [4]i32{ -5, 1, 0, 1234 }; + var abc = @select(i32, a, b, c); + try expect(mem.eql(i32, &@as([4]i32, abc), &[4]i32{ -1, 1, 999, 1234 })); + + var x: Vector(4, bool) = [4]bool{ false, false, false, true }; + var y: Vector(4, f32) = [4]f32{ 0.001, 33.4, 836, -3381.233 }; + var z: Vector(4, f32) = [4]f32{ 0.0, 312.1, -145.9, 9993.55 }; + var xyz = @select(f32, x, y, z); + try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/translate_c_macros.h b/test/behavior/translate_c_macros.h index 6f458684c7..bff4cdfe23 100644 --- a/test/behavior/translate_c_macros.h +++ b/test/behavior/translate_c_macros.h @@ -18,3 +18,15 @@ struct Foo { #define SIZE_OF_FOO sizeof(struct Foo) #define MAP_FAILED ((void *) -1) + +#define IGNORE_ME_1(x) ((void)(x)) +#define IGNORE_ME_2(x) ((const void)(x)) +#define IGNORE_ME_3(x) ((volatile void)(x)) +#define IGNORE_ME_4(x) ((const volatile void)(x)) +#define IGNORE_ME_5(x) ((volatile const void)(x)) + +#define IGNORE_ME_6(x) (void)(x) +#define IGNORE_ME_7(x) (const void)(x) +#define IGNORE_ME_8(x) (volatile void)(x) +#define IGNORE_ME_9(x) (const volatile void)(x) +#define IGNORE_ME_10(x) (volatile const void)(x) diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 8de06ae8ea..0daf4cec90 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -24,3 +24,16 @@ test "reference to a struct type" { test "cast negative integer to pointer" { try expectEqual(@intToPtr(?*c_void, @bitCast(usize, @as(isize, -1))), h.MAP_FAILED); } + +test "casting to void with a macro" { + h.IGNORE_ME_1(42); + h.IGNORE_ME_2(42); + h.IGNORE_ME_3(42); + h.IGNORE_ME_4(42); + h.IGNORE_ME_5(42); + h.IGNORE_ME_6(42); + h.IGNORE_ME_7(42); + h.IGNORE_ME_8(42); + h.IGNORE_ME_9(42); + h.IGNORE_ME_10(42); +} diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index bf8464244b..4d87e0030c 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -2,6 +2,113 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqualSlices = std.testing.expectEqualSlices; -test "slice" { - try expectEqualSlices(u8, "[]u8", @typeName([]u8)); +// Most tests here can be comptime but use runtime so that a stacktrace +// can show failure location. +// +// Note certain results of `@typeName()` expect `behavior.zig` to be the +// root file. Running a test against this file as root will result in +// failures. + +// CAUTION: this test is source-location sensitive. +test "anon fn param - source-location sensitive" { + // https://github.com/ziglang/zig/issues/9339 + try expectEqualSlices(u8, @typeName(TypeFromFn(struct {})), "behavior.typename.TypeFromFn(behavior.typename.struct:15:52)"); + try expectEqualSlices(u8, @typeName(TypeFromFn(union { unused: u8 })), "behavior.typename.TypeFromFn(behavior.typename.union:16:52)"); + try expectEqualSlices(u8, @typeName(TypeFromFn(enum { unused })), "behavior.typename.TypeFromFn(behavior.typename.enum:17:52)"); + + try expectEqualSlices( + u8, + @typeName(TypeFromFn3(struct {}, union { unused: u8 }, enum { unused })), + "behavior.typename.TypeFromFn3(behavior.typename.struct:21:31,behavior.typename.union:21:42,behavior.typename.enum:21:64)", + ); +} + +// CAUTION: this test is source-location sensitive. +test "anon field init" { + const Foo = .{ + .T1 = struct {}, + .T2 = union { unused: u8 }, + .T3 = enum { unused }, + }; + + try expectEqualSlices(u8, @typeName(Foo.T1), "behavior.typename.struct:29:15"); + try expectEqualSlices(u8, @typeName(Foo.T2), "behavior.typename.union:30:15"); + try expectEqualSlices(u8, @typeName(Foo.T3), "behavior.typename.enum:31:15"); +} + +test "basic" { + try expectEqualSlices(u8, @typeName(i64), "i64"); + try expectEqualSlices(u8, @typeName(*usize), "*usize"); + try expectEqualSlices(u8, @typeName([]u8), "[]u8"); +} + +test "top level decl" { + try expectEqualSlices(u8, @typeName(A_Struct), "A_Struct"); + try expectEqualSlices(u8, @typeName(A_Union), "A_Union"); + try expectEqualSlices(u8, @typeName(A_Enum), "A_Enum"); + + // regular fn, without error + try expectEqualSlices(u8, @typeName(@TypeOf(regular)), "fn() void"); + // regular fn inside struct, with error + try expectEqualSlices(u8, @typeName(@TypeOf(B.doTest)), "fn() @typeInfo(@typeInfo(@TypeOf(behavior.typename.B.doTest)).Fn.return_type.?).ErrorUnion.error_set!void"); + // generic fn + try expectEqualSlices(u8, @typeName(@TypeOf(TypeFromFn)), "fn(type) anytype"); +} + +const A_Struct = struct {}; +const A_Union = union { + unused: u8, +}; +const A_Enum = enum { + unused, +}; + +fn regular() void {} + +test "fn body decl" { + try B.doTest(); +} + +const B = struct { + fn doTest() !void { + const B_Struct = struct {}; + const B_Union = union { + unused: u8, + }; + const B_Enum = enum { + unused, + }; + + try expectEqualSlices(u8, @typeName(B_Struct), "B_Struct"); + try expectEqualSlices(u8, @typeName(B_Union), "B_Union"); + try expectEqualSlices(u8, @typeName(B_Enum), "B_Enum"); + } +}; + +test "fn param" { + // https://github.com/ziglang/zig/issues/675 + try expectEqualSlices(u8, @typeName(TypeFromFn(u8)), "behavior.typename.TypeFromFn(u8)"); + try expectEqualSlices(u8, @typeName(TypeFromFn(A_Struct)), "behavior.typename.TypeFromFn(behavior.typename.A_Struct)"); + try expectEqualSlices(u8, @typeName(TypeFromFn(A_Union)), "behavior.typename.TypeFromFn(behavior.typename.A_Union)"); + try expectEqualSlices(u8, @typeName(TypeFromFn(A_Enum)), "behavior.typename.TypeFromFn(behavior.typename.A_Enum)"); + + try expectEqualSlices(u8, @typeName(TypeFromFn2(u8, bool)), "behavior.typename.TypeFromFn2(u8,bool)"); +} + +fn TypeFromFn(comptime T: type) type { + _ = T; + return struct {}; +} + +fn TypeFromFn2(comptime T1: type, comptime T2: type) type { + _ = T1; + _ = T2; + return struct {}; +} + +fn TypeFromFn3(comptime T1: type, comptime T2: type, comptime T3: type) type { + _ = T1; + _ = T2; + _ = T3; + return struct {}; } diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index dfa70ac4b2..19abf767b8 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -37,3 +37,14 @@ test "float widening f16 to f128" { var y: f128 = x; try expect(x == y); } + +test "cast small unsigned to larger signed" { + try expect(castSmallUnsignedToLargerSigned1(200) == @as(i16, 200)); + try expect(castSmallUnsignedToLargerSigned2(9999) == @as(i64, 9999)); +} +fn castSmallUnsignedToLargerSigned1(x: u8) i16 { + return x; +} +fn castSmallUnsignedToLargerSigned2(x: u16) i64 { + return x; +} diff --git a/test/cases.zig b/test/cases.zig index 39d6128e8c..6510329238 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -1182,10 +1182,11 @@ pub fn addCases(ctx: *TestContext) !void { var case = ctx.obj("extern variable has no type", linux_x64); case.addError( \\comptime { - \\ _ = foo; + \\ const x = foo + foo; + \\ _ = x; \\} \\extern var foo: i32; - , &[_][]const u8{":2:9: error: unable to resolve comptime value"}); + , &[_][]const u8{":2:15: error: unable to resolve comptime value"}); case.addError( \\export fn entry() void { \\ _ = foo; @@ -1571,7 +1572,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ const x = asm volatile ("syscall" \\ : [o] "{rax}" (-> number) \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) + \\ [arg1] "{rdi}" (60) \\ : "rcx", "r11", "memory" \\ ); \\ _ = x; diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 4953896636..2e579b06c7 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1243,7 +1243,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\} , ""); - // See __builtin_alloca_with_align comment in std.c.builtins + // See __builtin_alloca_with_align comment in std.zig.c_builtins cases.add("use of unimplemented builtin in unused function does not prevent compilation", \\#include <stdlib.h> \\void unused() { @@ -1659,4 +1659,94 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("__builtin_choose_expr (unchosen expression is not evaluated)", + \\#include <stdlib.h> + \\int main(void) { + \\ int x = 0.0; + \\ int y = 0.0; + \\ int res; + \\ res = __builtin_choose_expr(1, 1, x / y); + \\ if (res != 1) abort(); + \\ res = __builtin_choose_expr(0, x / y, 2); + \\ if (res != 2) abort(); + \\ return 0; + \\} + , ""); + + // TODO: add isnan check for long double once bitfield support is added + // (needed for x86_64-windows-gnu) + // TODO: add isinf check for long double once std.math.isInf supports c_longdouble + cases.add("NAN and INFINITY", + \\#include <math.h> + \\#include <stdint.h> + \\#include <stdlib.h> + \\union uf { uint32_t u; float f; }; + \\#define CHECK_NAN(STR, VAL) { \ + \\ union uf unpack = {.f = __builtin_nanf(STR)}; \ + \\ if (!isnan(unpack.f)) abort(); \ + \\ if (unpack.u != VAL) abort(); \ + \\} + \\int main(void) { + \\ float f_nan = NAN; + \\ if (!isnan(f_nan)) abort(); + \\ double d_nan = NAN; + \\ if (!isnan(d_nan)) abort(); + \\ CHECK_NAN("0", 0x7FC00000); + \\ CHECK_NAN("", 0x7FC00000); + \\ CHECK_NAN("1", 0x7FC00001); + \\ CHECK_NAN("0x7FC00000", 0x7FC00000); + \\ CHECK_NAN("0x7FC0000F", 0x7FC0000F); + \\ CHECK_NAN("0x7FC000F0", 0x7FC000F0); + \\ CHECK_NAN("0x7FC00F00", 0x7FC00F00); + \\ CHECK_NAN("0x7FC0F000", 0x7FC0F000); + \\ CHECK_NAN("0x7FCF0000", 0x7FCF0000); + \\ CHECK_NAN("0xFFFFFFFF", 0x7FFFFFFF); + \\ float f_inf = INFINITY; + \\ if (!isinf(f_inf)) abort(); + \\ double d_inf = INFINITY; + \\ if (!isinf(d_inf)) abort(); + \\ return 0; + \\} + , ""); + + cases.add("signed array subscript. Issue #8556", + \\#include <stdint.h> + \\#include <stdlib.h> + \\#define TEST_NEGATIVE(type) { type x = -1; if (ptr[x] != 42) abort(); } + \\#define TEST_UNSIGNED(type) { type x = 2; if (arr[x] != 42) abort(); } + \\int main(void) { + \\ int arr[] = {40, 41, 42, 43}; + \\ int *ptr = arr + 3; + \\ if (ptr[-1] != 42) abort(); + \\ TEST_NEGATIVE(int); + \\ TEST_NEGATIVE(long); + \\ TEST_NEGATIVE(long long); + \\ TEST_NEGATIVE(int64_t); + \\ TEST_NEGATIVE(__int128); + \\ TEST_UNSIGNED(unsigned); + \\ TEST_UNSIGNED(unsigned long); + \\ TEST_UNSIGNED(unsigned long long); + \\ TEST_UNSIGNED(uint64_t); + \\ TEST_UNSIGNED(size_t); + \\ TEST_UNSIGNED(unsigned __int128); + \\ return 0; + \\} + , ""); + + cases.add("Ensure side-effects only evaluated once for signed array indices", + \\#include <stdlib.h> + \\int main(void) { + \\ int foo[] = {1, 2, 3, 4}; + \\ int *p = foo; + \\ int idx = 1; + \\ if ((++p)[--idx] != 2) abort(); + \\ if (p != foo + 1) abort(); + \\ if (idx != 0) abort(); + \\ if ((p++)[idx++] != 2) abort(); + \\ if (p != foo + 2) abort(); + \\ if (idx != 1) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index a3b8c9a8c6..0182462716 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -61,7 +61,20 @@ struct SmallStructInts { uint8_t c; uint8_t d; }; + void zig_small_struct_ints(struct SmallStructInts); +struct SmallStructInts zig_ret_small_struct_ints(); + +struct MedStructMixed { + uint32_t a; + float b; + float c; + uint32_t d; +}; + +void zig_med_struct_mixed(struct MedStructMixed); +struct MedStructMixed zig_ret_med_struct_mixed(); + struct SplitStructInts { uint64_t a; @@ -70,6 +83,14 @@ struct SplitStructInts { }; void zig_split_struct_ints(struct SplitStructInts); +struct SplitStructMixed { + uint64_t a; + uint8_t b; + float c; +}; +void zig_split_struct_mixed(struct SplitStructMixed); +struct SplitStructMixed zig_ret_split_struct_mixed(); + struct BigStruct zig_big_struct_both(struct BigStruct); typedef struct Vector3 { @@ -122,6 +143,16 @@ void run_c_tests(void) { } { + struct MedStructMixed s = {1234, 100.0f, 1337.0f}; + zig_med_struct_mixed(s); + } + + { + struct SplitStructMixed s = {1234, 100, 1337.0f}; + zig_split_struct_mixed(s); + } + + { struct BigStruct s = {30, 31, 32, 33, 34}; struct BigStruct res = zig_big_struct_both(s); assert_or_panic(res.a == 20); @@ -230,6 +261,44 @@ void c_small_struct_ints(struct SmallStructInts x) { assert_or_panic(x.b == 2); assert_or_panic(x.c == 3); assert_or_panic(x.d == 4); + + struct SmallStructInts y = zig_ret_small_struct_ints(); + + assert_or_panic(y.a == 1); + assert_or_panic(y.b == 2); + assert_or_panic(y.c == 3); + assert_or_panic(y.d == 4); +} + +struct SmallStructInts c_ret_small_struct_ints() { + struct SmallStructInts s = { + .a = 1, + .b = 2, + .c = 3, + .d = 4, + }; + return s; +} + +void c_med_struct_mixed(struct MedStructMixed x) { + assert_or_panic(x.a == 1234); + assert_or_panic(x.b == 100.0f); + assert_or_panic(x.c == 1337.0f); + + struct MedStructMixed y = zig_ret_med_struct_mixed(); + + assert_or_panic(y.a == 1234); + assert_or_panic(y.b == 100.0f); + assert_or_panic(y.c == 1337.0f); +} + +struct MedStructMixed c_ret_med_struct_mixed() { + struct MedStructMixed s = { + .a = 1234, + .b = 100.0, + .c = 1337.0, + }; + return s; } void c_split_struct_ints(struct SplitStructInts x) { @@ -238,6 +307,26 @@ void c_split_struct_ints(struct SplitStructInts x) { assert_or_panic(x.c == 1337); } +void c_split_struct_mixed(struct SplitStructMixed x) { + assert_or_panic(x.a == 1234); + assert_or_panic(x.b == 100); + assert_or_panic(x.c == 1337.0f); + struct SplitStructMixed y = zig_ret_split_struct_mixed(); + + assert_or_panic(y.a == 1234); + assert_or_panic(y.b == 100); + assert_or_panic(y.c == 1337.0f); +} + +struct SplitStructMixed c_ret_split_struct_mixed() { + struct SplitStructMixed s = { + .a = 1234, + .b = 100, + .c = 1337.0f, + }; + return s; +} + struct BigStruct c_big_struct_both(struct BigStruct x) { assert_or_panic(x.a == 1); assert_or_panic(x.b == 2); diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index ed5cad7c56..b8137334ec 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const print = std.debug.print; const expect = std.testing.expect; extern fn run_c_tests() void; @@ -170,6 +171,34 @@ export fn zig_big_union(x: BigUnion) void { expect(x.a.e == 5) catch @panic("test failure"); } +const MedStructMixed = extern struct { + a: u32, + b: f32, + c: f32, + d: u32 = 0, +}; +extern fn c_med_struct_mixed(MedStructMixed) void; +extern fn c_ret_med_struct_mixed() MedStructMixed; + +test "C ABI medium struct of ints and floats" { + var s = MedStructMixed{ + .a = 1234, + .b = 100.0, + .c = 1337.0, + }; + c_med_struct_mixed(s); + var s2 = c_ret_med_struct_mixed(); + expect(s2.a == 1234) catch @panic("test failure"); + expect(s2.b == 100.0) catch @panic("test failure"); + expect(s2.c == 1337.0) catch @panic("test failure"); +} + +export fn zig_med_struct_mixed(x: MedStructMixed) void { + expect(x.a == 1234) catch @panic("test failure"); + expect(x.b == 100.0) catch @panic("test failure"); + expect(x.c == 1337.0) catch @panic("test failure"); +} + const SmallStructInts = extern struct { a: u8, b: u8, @@ -177,6 +206,7 @@ const SmallStructInts = extern struct { d: u8, }; extern fn c_small_struct_ints(SmallStructInts) void; +extern fn c_ret_small_struct_ints() SmallStructInts; test "C ABI small struct of ints" { var s = SmallStructInts{ @@ -186,6 +216,11 @@ test "C ABI small struct of ints" { .d = 4, }; c_small_struct_ints(s); + var s2 = c_ret_small_struct_ints(); + expect(s2.a == 1) catch @panic("test failure"); + expect(s2.b == 2) catch @panic("test failure"); + expect(s2.c == 3) catch @panic("test failure"); + expect(s2.d == 4) catch @panic("test failure"); } export fn zig_small_struct_ints(x: SmallStructInts) void { @@ -217,6 +252,33 @@ export fn zig_split_struct_ints(x: SplitStructInt) void { expect(x.c == 1337) catch @panic("test failure"); } +const SplitStructMixed = extern struct { + a: u64, + b: u8, + c: f32, +}; +extern fn c_split_struct_mixed(SplitStructMixed) void; +extern fn c_ret_split_struct_mixed() SplitStructMixed; + +test "C ABI split struct of ints and floats" { + var s = SplitStructMixed{ + .a = 1234, + .b = 100, + .c = 1337.0, + }; + c_split_struct_mixed(s); + var s2 = c_ret_split_struct_mixed(); + expect(s2.a == 1234) catch @panic("test failure"); + expect(s2.b == 100) catch @panic("test failure"); + expect(s2.c == 1337.0) catch @panic("test failure"); +} + +export fn zig_split_struct_mixed(x: SplitStructMixed) void { + expect(x.a == 1234) catch @panic("test failure"); + expect(x.b == 100) catch @panic("test failure"); + expect(x.c == 1337.0) catch @panic("test failure"); +} + extern fn c_big_struct_both(BigStruct) BigStruct; test "C ABI sret and byval together" { @@ -315,6 +377,31 @@ export fn zig_ret_i64() i64 { return -1; } +export fn zig_ret_small_struct_ints() SmallStructInts { + return .{ + .a = 1, + .b = 2, + .c = 3, + .d = 4, + }; +} + +export fn zig_ret_med_struct_mixed() MedStructMixed { + return .{ + .a = 1234, + .b = 100.0, + .c = 1337.0, + }; +} + +export fn zig_ret_split_struct_mixed() SplitStructMixed { + return .{ + .a = 1234, + .b = 100, + .c = 1337.0, + }; +} + extern fn c_ret_bool() bool; extern fn c_ret_u8() u8; extern fn c_ret_u16() u16; diff --git a/test/stage2/arm.zig b/test/stage2/arm.zig index 6b4f569757..103b058a54 100644 --- a/test/stage2/arm.zig +++ b/test/stage2/arm.zig @@ -300,6 +300,28 @@ pub fn addCases(ctx: *TestContext) !void { } { + var case = ctx.exe("enums", linux_arm); + case.addCompareOutput( + \\const Number = enum { one, two, three }; + \\ + \\pub fn main() void { + \\ var x: Number = .one; + \\ var y = Number.two; + \\ var z = @intToEnum(Number, 2); + \\ assert(@enumToInt(x) == 0); + \\ assert(@enumToInt(y) == 1); + \\ assert(@enumToInt(z) == 2); + \\} + \\ + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + } + + { var case = ctx.exe("recursive fibonacci", linux_arm); case.addCompareOutput( \\pub fn main() void { diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 6427e2e3b8..aa1022257b 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -49,7 +49,7 @@ pub fn addCases(ctx: *TestContext) !void { \\export fn foo() callconv(y) c_int { \\ return 0; \\} - \\var y: i32 = 1234; + \\var y: @import("std").builtin.CallingConvention = .C; , &.{ ":2:22: error: unable to resolve comptime value", ":5:26: error: unable to resolve comptime value", @@ -240,6 +240,10 @@ pub fn addCases(ctx: *TestContext) !void { if (host_supports_custom_stack_size) { var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{}); + // TODO when adding result location support to function calls, revisit this test + // case. It can go back to what it was before, with `y` being comptime known. + // Because the ret_ptr will passed in with the inline fn call, and there will + // only be 1 store to it, and it will be comptime known. case.addCompareOutput( \\pub export fn main() i32 { \\ @setEvalBranchQuota(1001); @@ -247,7 +251,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ return y - 1; \\} \\ - \\fn rec(n: usize) callconv(.Inline) usize { + \\inline fn rec(n: i32) i32 { \\ if (n <= 1) return n; \\ return rec(n - 1); \\} diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig index f746be99d2..ab400f0a53 100644 --- a/test/stage2/wasm.zig +++ b/test/stage2/wasm.zig @@ -114,6 +114,27 @@ pub fn addCases(ctx: *TestContext) !void { , "25\n"); case.addCompareOutput( + \\pub export fn _start() i32 { + \\ var i: i32 = 2147483647; + \\ return i +% 1; + \\} + , "-2147483648\n"); + + case.addCompareOutput( + \\pub export fn _start() i32 { + \\ var i: i4 = 7; + \\ return i +% 1; + \\} + , "0\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var i: u8 = 255; + \\ return i +% 1; + \\} + , "0\n"); + + case.addCompareOutput( \\pub export fn _start() u32 { \\ var i: u32 = 5; \\ i += 20; @@ -134,6 +155,27 @@ pub fn addCases(ctx: *TestContext) !void { , "15\n"); case.addCompareOutput( + \\pub export fn _start() i32 { + \\ var i: i32 = -2147483648; + \\ return i -% 1; + \\} + , "2147483647\n"); + + case.addCompareOutput( + \\pub export fn _start() i32 { + \\ var i: i7 = -64; + \\ return i -% 1; + \\} + , "63\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var i: u4 = 0; + \\ return i -% 1; + \\} + , "15\n"); + + case.addCompareOutput( \\pub export fn _start() u32 { \\ var i: u32 = 5; \\ i -= 3; @@ -158,6 +200,27 @@ pub fn addCases(ctx: *TestContext) !void { , "350\n"); case.addCompareOutput( + \\pub export fn _start() i32 { + \\ var i: i32 = 2147483647; + \\ return i *% 2; + \\} + , "-2\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var i: u3 = 3; + \\ return i *% 3; + \\} + , "1\n"); + + case.addCompareOutput( + \\pub export fn _start() i32 { + \\ var i: i4 = 3; + \\ return i *% 3; + \\} + , "1\n"); + + case.addCompareOutput( \\pub export fn _start() u32 { \\ var i: u32 = 352; \\ i /= 7; // i = 50 @@ -612,4 +675,69 @@ pub fn addCases(ctx: *TestContext) !void { \\} , "42\n"); } + + { + var case = ctx.exe("wasm integer widening", wasi); + + case.addCompareOutput( + \\pub export fn _start() u64 { + \\ var x: u32 = 5; + \\ return x; + \\} + , "5\n"); + } + + { + var case = ctx.exe("wasm optionals", wasi); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var x: ?u32 = 5; + \\ var y: u32 = 0; + \\ if (x) |val| { + \\ y = val; + \\ } + \\ return y; + \\} + , "5\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var x: ?u32 = null; + \\ var y: u32 = 0; + \\ if (x) |val| { + \\ y = val; + \\ } + \\ return y; + \\} + , "0\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var x: ?u32 = 5; + \\ return x.?; + \\} + , "5\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var x: u32 = 5; + \\ var y: ?u32 = x; + \\ return y.?; + \\} + , "5\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var val: ?u32 = 5; + \\ while (val) |*v| { + \\ v.* -= 1; + \\ if (v.* == 2) { + \\ val = null; + \\ } + \\ } + \\ return 0; + \\} + , "0\n"); + } } diff --git a/test/standalone.zig b/test/standalone.zig index a483097f4a..9a32701ddd 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -37,6 +37,8 @@ pub fn addCases(cases: *tests.StandaloneContext) void { if (std.Target.current.os.tag == .linux) { cases.addBuildFile("test/standalone/pie/build.zig", .{}); } + // Try to build and run an Objective-C executable. + cases.addBuildFile("test/standalone/objc/build.zig", .{ .build_modes = true, .requires_macos_sdk = true }); // Ensure the development tools are buildable. cases.add("tools/gen_spirv_spec.zig"); diff --git a/test/standalone/objc/Foo.h b/test/standalone/objc/Foo.h new file mode 100644 index 0000000000..05cb7df39b --- /dev/null +++ b/test/standalone/objc/Foo.h @@ -0,0 +1,7 @@ +#import <Foundation/Foundation.h> + +@interface Foo : NSObject + +- (NSString *)name; + +@end diff --git a/test/standalone/objc/Foo.m b/test/standalone/objc/Foo.m new file mode 100644 index 0000000000..6fc9b1edf0 --- /dev/null +++ b/test/standalone/objc/Foo.m @@ -0,0 +1,11 @@ +#import "Foo.h" + +@implementation Foo + +- (NSString *)name +{ + NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; + return str; +} + +@end diff --git a/test/standalone/objc/build.zig b/test/standalone/objc/build.zig new file mode 100644 index 0000000000..7fdec514e7 --- /dev/null +++ b/test/standalone/objc/build.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const Builder = std.build.Builder; +const CrossTarget = std.zig.CrossTarget; + +fn isRunnableTarget(t: CrossTarget) bool { + // TODO I think we might be able to run this on Linux via Darling. + // Add a check for that here, and return true if Darling is available. + if (t.isNative() and t.getOsTag() == .macos) + return true + else + return false; +} + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + const target = b.standardTargetOptions(.{}); + + const test_step = b.step("test", "Test the program"); + + const exe = b.addExecutable("test", null); + b.default_step.dependOn(&exe.step); + exe.addIncludeDir("."); + exe.addCSourceFile("Foo.m", &[0][]const u8{}); + exe.addCSourceFile("test.m", &[0][]const u8{}); + exe.setBuildMode(mode); + exe.setTarget(target); + exe.linkLibC(); + // TODO when we figure out how to ship framework stubs for cross-compilation, + // populate paths to the sysroot here. + exe.linkFramework("Foundation"); + + if (isRunnableTarget(target)) { + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); + } +} diff --git a/test/standalone/objc/test.m b/test/standalone/objc/test.m new file mode 100644 index 0000000000..3c81316788 --- /dev/null +++ b/test/standalone/objc/test.m @@ -0,0 +1,12 @@ +#import "Foo.h" +#import <assert.h> + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + Foo *foo = [[Foo alloc] init]; + NSString *result = [foo name]; + assert([result isEqualToString:@"Zig"]); + return 0; + } +} diff --git a/test/tests.zig b/test/tests.zig index d83701ff6a..a427bbefeb 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -391,7 +391,14 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: return cases.step; } -pub fn addStandaloneTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode, skip_non_native: bool, target: std.zig.CrossTarget) *build.Step { +pub fn addStandaloneTests( + b: *build.Builder, + test_filter: ?[]const u8, + modes: []const Mode, + skip_non_native: bool, + enable_macos_sdk: bool, + target: std.zig.CrossTarget, +) *build.Step { const cases = b.allocator.create(StandaloneContext) catch unreachable; cases.* = StandaloneContext{ .b = b, @@ -400,6 +407,7 @@ pub fn addStandaloneTests(b: *build.Builder, test_filter: ?[]const u8, modes: [] .test_filter = test_filter, .modes = modes, .skip_non_native = skip_non_native, + .enable_macos_sdk = enable_macos_sdk, .target = target, }; @@ -768,7 +776,7 @@ pub const StackTracesContext = struct { var buf = ArrayList(u8).init(b.allocator); defer buf.deinit(); if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1]; - var it = mem.split(stderr, "\n"); + var it = mem.split(u8, stderr, "\n"); process_lines: while (it.next()) |line| { if (line.len == 0) continue; @@ -839,6 +847,7 @@ pub const StandaloneContext = struct { test_filter: ?[]const u8, modes: []const Mode, skip_non_native: bool, + enable_macos_sdk: bool, target: std.zig.CrossTarget, pub fn addC(self: *StandaloneContext, root_src: []const u8) void { @@ -849,9 +858,15 @@ pub const StandaloneContext = struct { self.addAllArgs(root_src, false); } - pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8, features: struct { build_modes: bool = false, cross_targets: bool = false }) void { + pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8, features: struct { + build_modes: bool = false, + cross_targets: bool = false, + requires_macos_sdk: bool = false, + }) void { const b = self.b; + if (features.requires_macos_sdk and !self.enable_macos_sdk) return; + const annotated_case_name = b.fmt("build {s}", .{build_file}); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; diff --git a/test/translate_c.zig b/test/translate_c.zig index 1ceaf15e5c..6ddc2107ee 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3461,11 +3461,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal); }); - // See __builtin_alloca_with_align comment in std.c.builtins + // See __builtin_alloca_with_align comment in std.zig.c_builtins cases.add("demote un-implemented builtins", \\#define FOO(X) __builtin_alloca_with_align((X), 8) , &[_][]const u8{ - \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins"); + \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.zig.c_builtins"); }); cases.add("null sentinel arrays when initialized from string literal. Issue #8256", @@ -3630,4 +3630,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub const FOO = @import("std").zig.c_translation.Macros.U_SUFFIX; }); + + cases.add("Simple array access of pointer with non-negative integer constant", + \\void foo(int *p) { + \\ p[0]; + \\ p[1]; + \\} + , &[_][]const u8{ + \\_ = p[@intCast(c_uint, @as(c_int, 0))]; + , + \\_ = p[@intCast(c_uint, @as(c_int, 1))]; + }); } |
