| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); |
| 3 | const assert = std.debug.assert; |
| 4 | const expect = std.testing.expect; |
| 5 | const expectEqual = std.testing.expectEqual; |
| 6 | |
| 7 | test "super basic invocations" { |
| 8 | const foo = struct { |
| 9 | fn foo() i32 { |
| 10 | return 1234; |
| 11 | } |
| 12 | }.foo; |
| 13 | try expect(@call(.auto, foo, .{}) == 1234); |
| 14 | comptime assert(@call(.always_inline, foo, .{}) == 1234); |
| 15 | { |
| 16 | // comptime call without comptime keyword |
| 17 | const result = @call(.compile_time, foo, .{}) == 1234; |
| 18 | comptime assert(result); |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | test "basic invocations" { |
| 23 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 25 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 26 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 27 | |
| 28 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support tail call modifiers |
| 29 | |
| 30 | const foo = struct { |
| 31 | fn foo(_: i32) i32 { |
| 32 | return 1234; |
| 33 | } |
| 34 | }.foo; |
| 35 | try expect(@call(.auto, foo, .{1}) == 1234); |
| 36 | comptime { |
| 37 | // comptime calls with supported modifiers |
| 38 | try expect(@call(.auto, foo, .{2}) == 1234); |
| 39 | try expect(@call(.no_suspend, foo, .{3}) == 1234); |
| 40 | try expect(@call(.always_tail, foo, .{4}) == 1234); |
| 41 | try expect(@call(.always_inline, foo, .{5}) == 1234); |
| 42 | } |
| 43 | // comptime call without comptime keyword |
| 44 | const result = @call(.compile_time, foo, .{6}) == 1234; |
| 45 | comptime assert(result); |
| 46 | // runtime calls of comptime-known function |
| 47 | try expect(@call(.no_suspend, foo, .{7}) == 1234); |
| 48 | try expect(@call(.never_tail, foo, .{8}) == 1234); |
| 49 | try expect(@call(.never_inline, foo, .{9}) == 1234); |
| 50 | // CBE does not support attributes on runtime functions |
| 51 | if (builtin.zig_backend != .stage2_c) { |
| 52 | // runtime calls of non comptime-known function |
| 53 | var alias_foo = &foo; |
| 54 | _ = &alias_foo; |
| 55 | try expect(@call(.no_suspend, alias_foo, .{10}) == 1234); |
| 56 | try expect(@call(.never_tail, alias_foo, .{11}) == 1234); |
| 57 | try expect(@call(.never_inline, alias_foo, .{12}) == 1234); |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | test "tuple parameters" { |
| 62 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 63 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 64 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 65 | |
| 66 | const add = struct { |
| 67 | fn add(a: i32, b: i32) i32 { |
| 68 | return a + b; |
| 69 | } |
| 70 | }.add; |
| 71 | var a: i32 = 12; |
| 72 | var b: i32 = 34; |
| 73 | _ = .{ &a, &b }; |
| 74 | try expect(@call(.auto, add, .{ a, 34 }) == 46); |
| 75 | try expect(@call(.auto, add, .{ 12, b }) == 46); |
| 76 | try expect(@call(.auto, add, .{ a, b }) == 46); |
| 77 | try expect(@call(.auto, add, .{ 12, 34 }) == 46); |
| 78 | if (false) { |
| 79 | comptime assert(@call(.auto, add, .{ 12, 34 }) == 46); // TODO |
| 80 | } |
| 81 | try expect(comptime @call(.auto, add, .{ 12, 34 }) == 46); |
| 82 | { |
| 83 | const separate_args0 = .{ a, b }; |
| 84 | const separate_args1 = .{ a, 34 }; |
| 85 | const separate_args2 = .{ 12, 34 }; |
| 86 | const separate_args3 = .{ 12, b }; |
| 87 | try expect(@call(.always_inline, add, separate_args0) == 46); |
| 88 | try expect(@call(.always_inline, add, separate_args1) == 46); |
| 89 | try expect(@call(.always_inline, add, separate_args2) == 46); |
| 90 | try expect(@call(.always_inline, add, separate_args3) == 46); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | test "result location of function call argument through runtime condition and struct init" { |
| 95 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 96 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 97 | |
| 98 | const E = enum { a, b }; |
| 99 | const S = struct { |
| 100 | e: E, |
| 101 | }; |
| 102 | const namespace = struct { |
| 103 | fn foo(s: S) !void { |
| 104 | try expect(s.e == .b); |
| 105 | } |
| 106 | }; |
| 107 | var runtime = true; |
| 108 | _ = &runtime; |
| 109 | try namespace.foo(.{ |
| 110 | .e = if (!runtime) .a else .b, |
| 111 | }); |
| 112 | } |
| 113 | |
| 114 | test "function call with 40 arguments" { |
| 115 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 116 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 117 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 118 | |
| 119 | const S = struct { |
| 120 | fn doTheTest(thirty_nine: i32) !void { |
| 121 | const result = add( |
| 122 | 0, |
| 123 | 1, |
| 124 | 2, |
| 125 | 3, |
| 126 | 4, |
| 127 | 5, |
| 128 | 6, |
| 129 | 7, |
| 130 | 8, |
| 131 | 9, |
| 132 | 10, |
| 133 | 11, |
| 134 | 12, |
| 135 | 13, |
| 136 | 14, |
| 137 | 15, |
| 138 | 16, |
| 139 | 17, |
| 140 | 18, |
| 141 | 19, |
| 142 | 20, |
| 143 | 21, |
| 144 | 22, |
| 145 | 23, |
| 146 | 24, |
| 147 | 25, |
| 148 | 26, |
| 149 | 27, |
| 150 | 28, |
| 151 | 29, |
| 152 | 30, |
| 153 | 31, |
| 154 | 32, |
| 155 | 33, |
| 156 | 34, |
| 157 | 35, |
| 158 | 36, |
| 159 | 37, |
| 160 | 38, |
| 161 | thirty_nine, |
| 162 | 40, |
| 163 | ); |
| 164 | try expect(result == 820); |
| 165 | try expect(thirty_nine == 39); |
| 166 | } |
| 167 | |
| 168 | fn add( |
| 169 | a0: i32, |
| 170 | a1: i32, |
| 171 | a2: i32, |
| 172 | a3: i32, |
| 173 | a4: i32, |
| 174 | a5: i32, |
| 175 | a6: i32, |
| 176 | a7: i32, |
| 177 | a8: i32, |
| 178 | a9: i32, |
| 179 | a10: i32, |
| 180 | a11: i32, |
| 181 | a12: i32, |
| 182 | a13: i32, |
| 183 | a14: i32, |
| 184 | a15: i32, |
| 185 | a16: i32, |
| 186 | a17: i32, |
| 187 | a18: i32, |
| 188 | a19: i32, |
| 189 | a20: i32, |
| 190 | a21: i32, |
| 191 | a22: i32, |
| 192 | a23: i32, |
| 193 | a24: i32, |
| 194 | a25: i32, |
| 195 | a26: i32, |
| 196 | a27: i32, |
| 197 | a28: i32, |
| 198 | a29: i32, |
| 199 | a30: i32, |
| 200 | a31: i32, |
| 201 | a32: i32, |
| 202 | a33: i32, |
| 203 | a34: i32, |
| 204 | a35: i32, |
| 205 | a36: i32, |
| 206 | a37: i32, |
| 207 | a38: i32, |
| 208 | a39: i32, |
| 209 | a40: i32, |
| 210 | ) i32 { |
| 211 | return a0 + |
| 212 | a1 + |
| 213 | a2 + |
| 214 | a3 + |
| 215 | a4 + |
| 216 | a5 + |
| 217 | a6 + |
| 218 | a7 + |
| 219 | a8 + |
| 220 | a9 + |
| 221 | a10 + |
| 222 | a11 + |
| 223 | a12 + |
| 224 | a13 + |
| 225 | a14 + |
| 226 | a15 + |
| 227 | a16 + |
| 228 | a17 + |
| 229 | a18 + |
| 230 | a19 + |
| 231 | a20 + |
| 232 | a21 + |
| 233 | a22 + |
| 234 | a23 + |
| 235 | a24 + |
| 236 | a25 + |
| 237 | a26 + |
| 238 | a27 + |
| 239 | a28 + |
| 240 | a29 + |
| 241 | a30 + |
| 242 | a31 + |
| 243 | a32 + |
| 244 | a33 + |
| 245 | a34 + |
| 246 | a35 + |
| 247 | a36 + |
| 248 | a37 + |
| 249 | a38 + |
| 250 | a39 + |
| 251 | a40; |
| 252 | } |
| 253 | }; |
| 254 | try S.doTheTest(39); |
| 255 | } |
| 256 | |
| 257 | test "arguments to comptime parameters generated in comptime blocks" { |
| 258 | const S = struct { |
| 259 | fn fortyTwo() i32 { |
| 260 | return 42; |
| 261 | } |
| 262 | |
| 263 | fn foo(comptime x: i32) void { |
| 264 | if (x != 42) @compileError("bad"); |
| 265 | } |
| 266 | }; |
| 267 | S.foo(S.fortyTwo()); |
| 268 | } |
| 269 | |
| 270 | test "forced tail call" { |
| 271 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 272 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 273 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 274 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 275 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 276 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 277 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 278 | |
| 279 | if (builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_c) { |
| 280 | if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) { |
| 281 | return error.SkipZigTest; |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls |
| 286 | |
| 287 | const S = struct { |
| 288 | fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 { |
| 289 | if (n == 0) return a; |
| 290 | if (n == 1) return b; |
| 291 | return @call( |
| 292 | .always_tail, |
| 293 | fibonacciTailInternal, |
| 294 | .{ n - 1, b, a + b }, |
| 295 | ); |
| 296 | } |
| 297 | |
| 298 | fn fibonacciTail(n: u16) u16 { |
| 299 | return fibonacciTailInternal(n, 0, 1); |
| 300 | } |
| 301 | }; |
| 302 | try expect(S.fibonacciTail(10) == 55); |
| 303 | } |
| 304 | |
| 305 | test "inline call preserves tail call" { |
| 306 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 307 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 308 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 309 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 310 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 311 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 312 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 313 | |
| 314 | if (builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_c) { |
| 315 | if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) { |
| 316 | return error.SkipZigTest; |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls |
| 321 | |
| 322 | const max_depth = 1000; |
| 323 | const S = struct { |
| 324 | var a: u16 = 0; |
| 325 | fn foo() void { |
| 326 | return bar(); |
| 327 | } |
| 328 | |
| 329 | inline fn bar() void { |
| 330 | if (a == max_depth) return; |
| 331 | // Stack overflow if not tail called |
| 332 | var buf: [100_000]u16 = undefined; |
| 333 | buf[a] = a; |
| 334 | a += 1; |
| 335 | return @call(.always_tail, foo, .{}); |
| 336 | } |
| 337 | }; |
| 338 | S.foo(); |
| 339 | try expect(S.a == max_depth); |
| 340 | } |
| 341 | |
| 342 | test "inline call doesn't re-evaluate non generic struct" { |
| 343 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 344 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 345 | |
| 346 | const S = struct { |
| 347 | fn foo(f: struct { a: u8, b: u8 }) !void { |
| 348 | try expect(f.a == 123); |
| 349 | try expect(f.b == 45); |
| 350 | } |
| 351 | }; |
| 352 | const ArgTuple = std.meta.ArgsTuple(@TypeOf(S.foo)); |
| 353 | try @call(.always_inline, S.foo, ArgTuple{.{ .a = 123, .b = 45 }}); |
| 354 | try comptime @call(.always_inline, S.foo, ArgTuple{.{ .a = 123, .b = 45 }}); |
| 355 | } |
| 356 | |
| 357 | test "Enum constructed by @Enum passed as generic argument" { |
| 358 | const S = struct { |
| 359 | const E = std.meta.FieldEnum(struct { |
| 360 | prev_pos: bool, |
| 361 | pos: bool, |
| 362 | vel: bool, |
| 363 | damp_vel: bool, |
| 364 | acc: bool, |
| 365 | rgba: bool, |
| 366 | prev_scale: bool, |
| 367 | scale: bool, |
| 368 | prev_rotation: bool, |
| 369 | rotation: bool, |
| 370 | angular_vel: bool, |
| 371 | alive: bool, |
| 372 | }); |
| 373 | fn foo(comptime a: E, b: u32) !void { |
| 374 | try expect(@backingInt(a) == b); |
| 375 | } |
| 376 | }; |
| 377 | inline for (@typeInfo(S.E).@"enum".field_names, 0..) |_, i| { |
| 378 | try S.foo(@as(S.E, @fromBackingInt(@intCast(i))), i); |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | test "generic function with generic function parameter" { |
| 383 | const S = struct { |
| 384 | fn f(comptime a: fn (anytype) anyerror!void, b: anytype) anyerror!void { |
| 385 | try a(b); |
| 386 | } |
| 387 | fn g(a: anytype) anyerror!void { |
| 388 | try expect(a == 123); |
| 389 | } |
| 390 | }; |
| 391 | try S.f(S.g, 123); |
| 392 | } |
| 393 | |
| 394 | test "recursive inline call with comptime known argument" { |
| 395 | const S = struct { |
| 396 | inline fn foo(x: i32) i32 { |
| 397 | if (x <= 0) { |
| 398 | return 0; |
| 399 | } else { |
| 400 | return x * 2 + foo(x - 1); |
| 401 | } |
| 402 | } |
| 403 | }; |
| 404 | |
| 405 | try expect(S.foo(4) == 20); |
| 406 | } |
| 407 | |
| 408 | test "inline while with @call" { |
| 409 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 410 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 411 | |
| 412 | const S = struct { |
| 413 | fn inc(a: *u32) void { |
| 414 | a.* += 1; |
| 415 | } |
| 416 | }; |
| 417 | var a: u32 = 0; |
| 418 | comptime var i = 0; |
| 419 | inline while (i < 10) : (i += 1) { |
| 420 | @call(.auto, S.inc, .{&a}); |
| 421 | } |
| 422 | try expect(a == 10); |
| 423 | } |
| 424 | |
| 425 | test "method call as parameter type" { |
| 426 | const S = struct { |
| 427 | fn foo(x: anytype, y: @TypeOf(x).Inner()) @TypeOf(y) { |
| 428 | return y; |
| 429 | } |
| 430 | fn Inner() type { |
| 431 | return u64; |
| 432 | } |
| 433 | }; |
| 434 | try expectEqual(@as(u64, 123), S.foo(S{}, 123)); |
| 435 | try expectEqual(@as(u64, 500), S.foo(S{}, 500)); |
| 436 | } |
| 437 | |
| 438 | test "non-anytype generic parameters provide result type" { |
| 439 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 440 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 441 | |
| 442 | const S = struct { |
| 443 | fn f(comptime T: type, y: T) !void { |
| 444 | try expectEqual(@as(T, 123), y); |
| 445 | } |
| 446 | |
| 447 | fn g(x: anytype, y: @TypeOf(x)) !void { |
| 448 | try expectEqual(@as(@TypeOf(x), 0x222), y); |
| 449 | } |
| 450 | }; |
| 451 | |
| 452 | var rt_u16: u16 = 123; |
| 453 | var rt_u32: u32 = 0x10000222; |
| 454 | _ = .{ &rt_u16, &rt_u32 }; |
| 455 | |
| 456 | try S.f(u8, @intCast(rt_u16)); |
| 457 | try S.f(u8, @intCast(123)); |
| 458 | |
| 459 | try S.g(rt_u16, @truncate(rt_u32)); |
| 460 | try S.g(rt_u16, @truncate(0x10000222)); |
| 461 | |
| 462 | try comptime S.f(u8, @intCast(123)); |
| 463 | try comptime S.g(@as(u16, undefined), @truncate(0x99990222)); |
| 464 | } |
| 465 | |
| 466 | test "argument to generic function has correct result type" { |
| 467 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 468 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 469 | |
| 470 | const S = struct { |
| 471 | fn foo(_: anytype, e: enum { a, b }) bool { |
| 472 | return e == .b; |
| 473 | } |
| 474 | |
| 475 | fn doTheTest() !void { |
| 476 | var t = true; |
| 477 | _ = &t; |
| 478 | |
| 479 | // Since the enum literal passes through a runtime conditional here, these can only |
| 480 | // compile if RLS provides the correct result type to the argument |
| 481 | try expect(foo({}, if (!t) .a else .b)); |
| 482 | try expect(!foo("dummy", if (t) .a else .b)); |
| 483 | try expect(foo({}, if (t) .b else .a)); |
| 484 | try expect(!foo(123, if (t) .a else .a)); |
| 485 | try expect(foo(123, if (t) .b else .b)); |
| 486 | } |
| 487 | }; |
| 488 | |
| 489 | try S.doTheTest(); |
| 490 | try comptime S.doTheTest(); |
| 491 | } |
| 492 | |
| 493 | test "call inline fn through pointer" { |
| 494 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 495 | |
| 496 | const S = struct { |
| 497 | inline fn foo(x: u8) !void { |
| 498 | try expect(x == 123); |
| 499 | } |
| 500 | }; |
| 501 | const f = &S.foo; |
| 502 | try f(123); |
| 503 | } |
| 504 | |
| 505 | test "call function in comptime field" { |
| 506 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 507 | |
| 508 | const S = struct { |
| 509 | comptime capacity: fn () u64 = capacity_, |
| 510 | fn capacity_() u64 { |
| 511 | return 64; |
| 512 | } |
| 513 | }; |
| 514 | try std.testing.expect((S{}).capacity() == 64); |
| 515 | } |
| 516 | |
| 517 | test "call function pointer in comptime field" { |
| 518 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 519 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 520 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 521 | |
| 522 | const Auto = struct { |
| 523 | auto: [max_len]u8 = undefined, |
| 524 | offset: u64 = 0, |
| 525 | |
| 526 | comptime capacityFn: *const fn () u64 = capacity, |
| 527 | |
| 528 | const max_len: u64 = 32; |
| 529 | |
| 530 | fn capacity() u64 { |
| 531 | return max_len; |
| 532 | } |
| 533 | }; |
| 534 | |
| 535 | const a: Auto = .{ .offset = 16, .capacityFn = Auto.capacity }; |
| 536 | try std.testing.expect(a.capacityFn() == 32); |
| 537 | try std.testing.expect((a.capacityFn)() == 32); |
| 538 | } |
| 539 | |
| 540 | test "generic function pointer can be called" { |
| 541 | const S = struct { |
| 542 | var ok = false; |
| 543 | fn foo(x: anytype) void { |
| 544 | ok = x; |
| 545 | } |
| 546 | }; |
| 547 | const x = &S.foo; |
| 548 | x(true); |
| 549 | try expect(S.ok); |
| 550 | } |
| 551 | |
| 552 | test "value returned from comptime function is comptime known" { |
| 553 | const S = struct { |
| 554 | fn fieldCount(comptime T: type) switch (@typeInfo(T)) { |
| 555 | .@"struct" => comptime_int, |
| 556 | else => unreachable, |
| 557 | } { |
| 558 | return switch (@typeInfo(T)) { |
| 559 | .@"struct" => |info| info.field_names.len, |
| 560 | else => unreachable, |
| 561 | }; |
| 562 | } |
| 563 | }; |
| 564 | const fields_len = S.fieldCount(@TypeOf(.{})); |
| 565 | comptime assert(fields_len == 0); |
| 566 | } |
| 567 | |
| 568 | test "registers get overwritten when ignoring return" { |
| 569 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 570 | if (builtin.cpu.arch != .x86_64 or builtin.os.tag != .linux) return error.SkipZigTest; |
| 571 | |
| 572 | const S = struct { |
| 573 | fn open() usize { |
| 574 | return 42; |
| 575 | } |
| 576 | fn write(fd: usize, a: [*]const u8, len: usize) usize { |
| 577 | return syscall4(.WRITE, fd, @intFromPtr(a), len); |
| 578 | } |
| 579 | fn syscall4(_: enum { WRITE }, _: usize, _: usize, _: usize) usize { |
| 580 | return 23; |
| 581 | } |
| 582 | fn close(fd: usize) usize { |
| 583 | if (fd != 42) |
| 584 | unreachable; |
| 585 | return 0; |
| 586 | } |
| 587 | }; |
| 588 | |
| 589 | const fd = S.open(); |
| 590 | _ = S.write(fd, "a", 1); |
| 591 | _ = S.close(fd); |
| 592 | } |
| 593 | |
| 594 | test "call with union with zero sized field is not memorized incorrectly" { |
| 595 | const U = union(enum) { |
| 596 | T: type, |
| 597 | N: void, |
| 598 | fn S(comptime query: @This()) type { |
| 599 | return struct { |
| 600 | fn tag() type { |
| 601 | return query.T; |
| 602 | } |
| 603 | }; |
| 604 | } |
| 605 | }; |
| 606 | const s1 = U.S(U{ .T = u32 }).tag(); |
| 607 | try std.testing.expectEqual(u32, s1); |
| 608 | |
| 609 | const s2 = U.S(U{ .T = u64 }).tag(); |
| 610 | try std.testing.expectEqual(u64, s2); |
| 611 | } |
| 612 | |
| 613 | test "function call with cast to anyopaque pointer" { |
| 614 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 615 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 616 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 617 | |
| 618 | const Foo = struct { |
| 619 | y: u8, |
| 620 | var foo: @This() = undefined; |
| 621 | const t = &foo; |
| 622 | |
| 623 | fn bar(pointer: ?*anyopaque) void { |
| 624 | _ = pointer; |
| 625 | } |
| 626 | }; |
| 627 | Foo.bar(Foo.t); |
| 628 | } |
| 629 | |
| 630 | test "arguments pointed to on stack into tailcall" { |
| 631 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 632 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 633 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 634 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 635 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls |
| 636 | |
| 637 | switch (builtin.cpu.arch) { |
| 638 | .wasm32, |
| 639 | .wasm64, |
| 640 | .mips, |
| 641 | .mipsel, |
| 642 | .mips64, |
| 643 | .mips64el, |
| 644 | .powerpc, |
| 645 | .powerpcle, |
| 646 | .powerpc64, |
| 647 | .powerpc64le, |
| 648 | => return error.SkipZigTest, |
| 649 | else => {}, |
| 650 | } |
| 651 | |
| 652 | const S = struct { |
| 653 | var base: usize = undefined; |
| 654 | var result_off: [7]usize = undefined; |
| 655 | var result_len: [7]usize = undefined; |
| 656 | var result_index: usize = 0; |
| 657 | |
| 658 | noinline fn insertionSort(data: []u64) void { |
| 659 | result_off[result_index] = @intFromPtr(data.ptr) - base; |
| 660 | result_len[result_index] = data.len; |
| 661 | result_index += 1; |
| 662 | if (data.len > 1) { |
| 663 | var least_i: usize = 0; |
| 664 | var i: usize = 1; |
| 665 | while (i < data.len) : (i += 1) { |
| 666 | if (data[i] < data[least_i]) |
| 667 | least_i = i; |
| 668 | } |
| 669 | std.mem.swap(u64, &data[0], &data[least_i]); |
| 670 | |
| 671 | // there used to be a bug where |
| 672 | // `data[1..]` is created on the stack |
| 673 | // and pointed to by the first argument register |
| 674 | // then stack is invalidated by the tailcall and |
| 675 | // overwritten by callee |
| 676 | // https://github.com/ziglang/zig/issues/9703 |
| 677 | return @call(.always_tail, insertionSort, .{data[1..]}); |
| 678 | } |
| 679 | } |
| 680 | }; |
| 681 | |
| 682 | var data = [_]u64{ 1, 6, 2, 7, 1, 9, 3 }; |
| 683 | S.base = @intFromPtr(&data); |
| 684 | S.insertionSort(data[0..]); |
| 685 | try expect(S.result_len[0] == 7); |
| 686 | try expect(S.result_len[1] == 6); |
| 687 | try expect(S.result_len[2] == 5); |
| 688 | try expect(S.result_len[3] == 4); |
| 689 | try expect(S.result_len[4] == 3); |
| 690 | try expect(S.result_len[5] == 2); |
| 691 | try expect(S.result_len[6] == 1); |
| 692 | |
| 693 | try expect(S.result_off[0] == 0); |
| 694 | try expect(S.result_off[1] == 8); |
| 695 | try expect(S.result_off[2] == 16); |
| 696 | try expect(S.result_off[3] == 24); |
| 697 | try expect(S.result_off[4] == 32); |
| 698 | try expect(S.result_off[5] == 40); |
| 699 | try expect(S.result_off[6] == 48); |
| 700 | } |
| 701 | |
| 702 | test "tail call function pointer" { |
| 703 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 704 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 705 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 706 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 707 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 708 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 709 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 710 | |
| 711 | if (builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_c) { |
| 712 | if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) { |
| 713 | return error.SkipZigTest; |
| 714 | } |
| 715 | } |
| 716 | |
| 717 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls |
| 718 | |
| 719 | const S = struct { |
| 720 | fn foo(n: u8) void { |
| 721 | if (n == 0) return; |
| 722 | const other: *const fn (u8) void = &bar; |
| 723 | return @call(.always_tail, other, .{n - 1}); |
| 724 | } |
| 725 | fn bar(n: u8) void { |
| 726 | var other: *const fn (u8) void = undefined; |
| 727 | other = &foo; // runtime-known pointer |
| 728 | return @call(.always_tail, other, .{n}); |
| 729 | } |
| 730 | }; |
| 731 | |
| 732 | S.foo(100); |
| 733 | } |
| 734 | |
| 735 | test "tail call with potentially extended types" { |
| 736 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 737 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 738 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 739 | |
| 740 | if (builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_c) { |
| 741 | if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) { |
| 742 | return error.SkipZigTest; |
| 743 | } |
| 744 | } |
| 745 | |
| 746 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls |
| 747 | |
| 748 | const S = struct { |
| 749 | fn Test(comptime Return: type) type { |
| 750 | return struct { |
| 751 | fn callee(@"u8": u8, @"i8": i8, @"u16": u16, @"i16": i16) callconv(.c) Return { |
| 752 | return @intCast(@as(i32, @"u8") + @as(i32, @"i8") + @as(i32, @"u16") + @as(i32, @"i16")); |
| 753 | } |
| 754 | fn caller(@"u8": u8, @"i8": i8, @"u16": u16, @"i16": i16) callconv(.c) Return { |
| 755 | return @call(.always_tail, callee, .{ @"u8", @"i8", @"u16", @"i16" }); |
| 756 | } |
| 757 | }; |
| 758 | } |
| 759 | }; |
| 760 | try std.testing.expect(S.Test(u8).caller(1, -2, 3, 4) == 6); |
| 761 | try std.testing.expect(S.Test(i8).caller(5, -6, 7, -8) == -2); |
| 762 | try std.testing.expect(S.Test(u16).caller(9, 10, 11, 12) == 42); |
| 763 | try std.testing.expect(S.Test(i16).caller(13, 14, 15, -16) == 26); |
| 764 | } |