1const builtin = @import("builtin");
2const std = @import("std");
3const assert = std.debug.assert;
4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
6
7test "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
22test "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
61test "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
94test "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
114test "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
257test "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
270test "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
305test "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
342test "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
357test "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
382test "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
394test "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
408test "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
425test "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
438test "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
466test "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
493test "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
505test "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
517test "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
540test "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
552test "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
568test "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
594test "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
613test "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
630test "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
702test "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
735test "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}