master
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_wasm) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
28
29 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support tail call modifiers
30
31 const foo = struct {
32 fn foo(_: i32) i32 {
33 return 1234;
34 }
35 }.foo;
36 try expect(@call(.auto, foo, .{1}) == 1234);
37 comptime {
38 // comptime calls with supported modifiers
39 try expect(@call(.auto, foo, .{2}) == 1234);
40 try expect(@call(.no_suspend, foo, .{3}) == 1234);
41 try expect(@call(.always_tail, foo, .{4}) == 1234);
42 try expect(@call(.always_inline, foo, .{5}) == 1234);
43 }
44 // comptime call without comptime keyword
45 const result = @call(.compile_time, foo, .{6}) == 1234;
46 comptime assert(result);
47 // runtime calls of comptime-known function
48 try expect(@call(.no_suspend, foo, .{7}) == 1234);
49 try expect(@call(.never_tail, foo, .{8}) == 1234);
50 try expect(@call(.never_inline, foo, .{9}) == 1234);
51 // CBE does not support attributes on runtime functions
52 if (builtin.zig_backend != .stage2_c) {
53 // runtime calls of non comptime-known function
54 var alias_foo = &foo;
55 _ = &alias_foo;
56 try expect(@call(.no_suspend, alias_foo, .{10}) == 1234);
57 try expect(@call(.never_tail, alias_foo, .{11}) == 1234);
58 try expect(@call(.never_inline, alias_foo, .{12}) == 1234);
59 }
60}
61
62test "tuple parameters" {
63 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
64 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
65 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
66
67 const add = struct {
68 fn add(a: i32, b: i32) i32 {
69 return a + b;
70 }
71 }.add;
72 var a: i32 = 12;
73 var b: i32 = 34;
74 _ = .{ &a, &b };
75 try expect(@call(.auto, add, .{ a, 34 }) == 46);
76 try expect(@call(.auto, add, .{ 12, b }) == 46);
77 try expect(@call(.auto, add, .{ a, b }) == 46);
78 try expect(@call(.auto, add, .{ 12, 34 }) == 46);
79 if (false) {
80 comptime assert(@call(.auto, add, .{ 12, 34 }) == 46); // TODO
81 }
82 try expect(comptime @call(.auto, add, .{ 12, 34 }) == 46);
83 {
84 const separate_args0 = .{ a, b };
85 const separate_args1 = .{ a, 34 };
86 const separate_args2 = .{ 12, 34 };
87 const separate_args3 = .{ 12, b };
88 try expect(@call(.always_inline, add, separate_args0) == 46);
89 try expect(@call(.always_inline, add, separate_args1) == 46);
90 try expect(@call(.always_inline, add, separate_args2) == 46);
91 try expect(@call(.always_inline, add, separate_args3) == 46);
92 }
93}
94
95test "result location of function call argument through runtime condition and struct init" {
96 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
97 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
98
99 const E = enum { a, b };
100 const S = struct {
101 e: E,
102 };
103 const namespace = struct {
104 fn foo(s: S) !void {
105 try expect(s.e == .b);
106 }
107 };
108 var runtime = true;
109 _ = &runtime;
110 try namespace.foo(.{
111 .e = if (!runtime) .a else .b,
112 });
113}
114
115test "function call with 40 arguments" {
116 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
117 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
118 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
119
120 const S = struct {
121 fn doTheTest(thirty_nine: i32) !void {
122 const result = add(
123 0,
124 1,
125 2,
126 3,
127 4,
128 5,
129 6,
130 7,
131 8,
132 9,
133 10,
134 11,
135 12,
136 13,
137 14,
138 15,
139 16,
140 17,
141 18,
142 19,
143 20,
144 21,
145 22,
146 23,
147 24,
148 25,
149 26,
150 27,
151 28,
152 29,
153 30,
154 31,
155 32,
156 33,
157 34,
158 35,
159 36,
160 37,
161 38,
162 thirty_nine,
163 40,
164 );
165 try expect(result == 820);
166 try expect(thirty_nine == 39);
167 }
168
169 fn add(
170 a0: i32,
171 a1: i32,
172 a2: i32,
173 a3: i32,
174 a4: i32,
175 a5: i32,
176 a6: i32,
177 a7: i32,
178 a8: i32,
179 a9: i32,
180 a10: i32,
181 a11: i32,
182 a12: i32,
183 a13: i32,
184 a14: i32,
185 a15: i32,
186 a16: i32,
187 a17: i32,
188 a18: i32,
189 a19: i32,
190 a20: i32,
191 a21: i32,
192 a22: i32,
193 a23: i32,
194 a24: i32,
195 a25: i32,
196 a26: i32,
197 a27: i32,
198 a28: i32,
199 a29: i32,
200 a30: i32,
201 a31: i32,
202 a32: i32,
203 a33: i32,
204 a34: i32,
205 a35: i32,
206 a36: i32,
207 a37: i32,
208 a38: i32,
209 a39: i32,
210 a40: i32,
211 ) i32 {
212 return a0 +
213 a1 +
214 a2 +
215 a3 +
216 a4 +
217 a5 +
218 a6 +
219 a7 +
220 a8 +
221 a9 +
222 a10 +
223 a11 +
224 a12 +
225 a13 +
226 a14 +
227 a15 +
228 a16 +
229 a17 +
230 a18 +
231 a19 +
232 a20 +
233 a21 +
234 a22 +
235 a23 +
236 a24 +
237 a25 +
238 a26 +
239 a27 +
240 a28 +
241 a29 +
242 a30 +
243 a31 +
244 a32 +
245 a33 +
246 a34 +
247 a35 +
248 a36 +
249 a37 +
250 a38 +
251 a39 +
252 a40;
253 }
254 };
255 try S.doTheTest(39);
256}
257
258test "arguments to comptime parameters generated in comptime blocks" {
259 const S = struct {
260 fn fortyTwo() i32 {
261 return 42;
262 }
263
264 fn foo(comptime x: i32) void {
265 if (x != 42) @compileError("bad");
266 }
267 };
268 S.foo(S.fortyTwo());
269}
270
271test "forced tail call" {
272 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
273 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
274 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
275 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
276 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
277 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
278 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
279
280 if (builtin.zig_backend == .stage2_llvm) {
281 if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) {
282 return error.SkipZigTest;
283 }
284 }
285
286 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls
287
288 const S = struct {
289 fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 {
290 if (n == 0) return a;
291 if (n == 1) return b;
292 return @call(
293 .always_tail,
294 fibonacciTailInternal,
295 .{ n - 1, b, a + b },
296 );
297 }
298
299 fn fibonacciTail(n: u16) u16 {
300 return fibonacciTailInternal(n, 0, 1);
301 }
302 };
303 try expect(S.fibonacciTail(10) == 55);
304}
305
306test "inline call preserves tail call" {
307 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
308 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
309 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
310 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
311 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
312 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
313 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
314
315 if (builtin.zig_backend == .stage2_llvm) {
316 if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) {
317 return error.SkipZigTest;
318 }
319 }
320
321 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls
322
323 const max_depth = 1000;
324 const S = struct {
325 var a: u16 = 0;
326 fn foo() void {
327 return bar();
328 }
329
330 inline fn bar() void {
331 if (a == max_depth) return;
332 // Stack overflow if not tail called
333 var buf: [100_000]u16 = undefined;
334 buf[a] = a;
335 a += 1;
336 return @call(.always_tail, foo, .{});
337 }
338 };
339 S.foo();
340 try expect(S.a == max_depth);
341}
342
343test "inline call doesn't re-evaluate non generic struct" {
344 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
345 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
346
347 const S = struct {
348 fn foo(f: struct { a: u8, b: u8 }) !void {
349 try expect(f.a == 123);
350 try expect(f.b == 45);
351 }
352 };
353 const ArgTuple = std.meta.ArgsTuple(@TypeOf(S.foo));
354 try @call(.always_inline, S.foo, ArgTuple{.{ .a = 123, .b = 45 }});
355 try comptime @call(.always_inline, S.foo, ArgTuple{.{ .a = 123, .b = 45 }});
356}
357
358test "Enum constructed by @Enum passed as generic argument" {
359 const S = struct {
360 const E = std.meta.FieldEnum(struct {
361 prev_pos: bool,
362 pos: bool,
363 vel: bool,
364 damp_vel: bool,
365 acc: bool,
366 rgba: bool,
367 prev_scale: bool,
368 scale: bool,
369 prev_rotation: bool,
370 rotation: bool,
371 angular_vel: bool,
372 alive: bool,
373 });
374 fn foo(comptime a: E, b: u32) !void {
375 try expect(@intFromEnum(a) == b);
376 }
377 };
378 inline for (@typeInfo(S.E).@"enum".fields, 0..) |_, i| {
379 try S.foo(@as(S.E, @enumFromInt(i)), i);
380 }
381}
382
383test "generic function with generic function parameter" {
384 const S = struct {
385 fn f(comptime a: fn (anytype) anyerror!void, b: anytype) anyerror!void {
386 try a(b);
387 }
388 fn g(a: anytype) anyerror!void {
389 try expect(a == 123);
390 }
391 };
392 try S.f(S.g, 123);
393}
394
395test "recursive inline call with comptime known argument" {
396 const S = struct {
397 inline fn foo(x: i32) i32 {
398 if (x <= 0) {
399 return 0;
400 } else {
401 return x * 2 + foo(x - 1);
402 }
403 }
404 };
405
406 try expect(S.foo(4) == 20);
407}
408
409test "inline while with @call" {
410 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
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 fields(comptime T: type) switch (@typeInfo(T)) {
555 .@"struct" => []const std.builtin.Type.StructField,
556 else => unreachable,
557 } {
558 return switch (@typeInfo(T)) {
559 .@"struct" => |info| info.fields,
560 else => unreachable,
561 };
562 }
563 };
564 const fields_list = S.fields(@TypeOf(.{}));
565 if (fields_list.len != 0)
566 @compileError("Argument count mismatch");
567}
568
569test "registers get overwritten when ignoring return" {
570 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
571 if (builtin.cpu.arch != .x86_64 or builtin.os.tag != .linux) return error.SkipZigTest;
572
573 const S = struct {
574 fn open() usize {
575 return 42;
576 }
577 fn write(fd: usize, a: [*]const u8, len: usize) usize {
578 return syscall4(.WRITE, fd, @intFromPtr(a), len);
579 }
580 fn syscall4(_: enum { WRITE }, _: usize, _: usize, _: usize) usize {
581 return 23;
582 }
583 fn close(fd: usize) usize {
584 if (fd != 42)
585 unreachable;
586 return 0;
587 }
588 };
589
590 const fd = S.open();
591 _ = S.write(fd, "a", 1);
592 _ = S.close(fd);
593}
594
595test "call with union with zero sized field is not memorized incorrectly" {
596 const U = union(enum) {
597 T: type,
598 N: void,
599 fn S(comptime query: @This()) type {
600 return struct {
601 fn tag() type {
602 return query.T;
603 }
604 };
605 }
606 };
607 const s1 = U.S(U{ .T = u32 }).tag();
608 try std.testing.expectEqual(u32, s1);
609
610 const s2 = U.S(U{ .T = u64 }).tag();
611 try std.testing.expectEqual(u64, s2);
612}
613
614test "function call with cast to anyopaque pointer" {
615 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
616 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
617 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
618
619 const Foo = struct {
620 y: u8,
621 var foo: @This() = undefined;
622 const t = &foo;
623
624 fn bar(pointer: ?*anyopaque) void {
625 _ = pointer;
626 }
627 };
628 Foo.bar(Foo.t);
629}
630
631test "arguments pointed to on stack into tailcall" {
632 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
633 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
634 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
635 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
636 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls
637
638 switch (builtin.cpu.arch) {
639 .wasm32,
640 .wasm64,
641 .mips,
642 .mipsel,
643 .mips64,
644 .mips64el,
645 .powerpc,
646 .powerpcle,
647 .powerpc64,
648 .powerpc64le,
649 => return error.SkipZigTest,
650 else => {},
651 }
652
653 const S = struct {
654 var base: usize = undefined;
655 var result_off: [7]usize = undefined;
656 var result_len: [7]usize = undefined;
657 var result_index: usize = 0;
658
659 noinline fn insertionSort(data: []u64) void {
660 result_off[result_index] = @intFromPtr(data.ptr) - base;
661 result_len[result_index] = data.len;
662 result_index += 1;
663 if (data.len > 1) {
664 var least_i: usize = 0;
665 var i: usize = 1;
666 while (i < data.len) : (i += 1) {
667 if (data[i] < data[least_i])
668 least_i = i;
669 }
670 std.mem.swap(u64, &data[0], &data[least_i]);
671
672 // there used to be a bug where
673 // `data[1..]` is created on the stack
674 // and pointed to by the first argument register
675 // then stack is invalidated by the tailcall and
676 // overwritten by callee
677 // https://github.com/ziglang/zig/issues/9703
678 return @call(.always_tail, insertionSort, .{data[1..]});
679 }
680 }
681 };
682
683 var data = [_]u64{ 1, 6, 2, 7, 1, 9, 3 };
684 S.base = @intFromPtr(&data);
685 S.insertionSort(data[0..]);
686 try expect(S.result_len[0] == 7);
687 try expect(S.result_len[1] == 6);
688 try expect(S.result_len[2] == 5);
689 try expect(S.result_len[3] == 4);
690 try expect(S.result_len[4] == 3);
691 try expect(S.result_len[5] == 2);
692 try expect(S.result_len[6] == 1);
693
694 try expect(S.result_off[0] == 0);
695 try expect(S.result_off[1] == 8);
696 try expect(S.result_off[2] == 16);
697 try expect(S.result_off[3] == 24);
698 try expect(S.result_off[4] == 32);
699 try expect(S.result_off[5] == 40);
700 try expect(S.result_off[6] == 48);
701}
702
703test "tail call function pointer" {
704 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
705 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
706 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
707 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
708 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
709 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
710 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
711
712 if (builtin.zig_backend == .stage2_llvm) {
713 if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) {
714 return error.SkipZigTest;
715 }
716 }
717
718 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls
719
720 const S = struct {
721 fn foo(n: u8) void {
722 if (n == 0) return;
723 const other: *const fn (u8) void = &bar;
724 return @call(.always_tail, other, .{n - 1});
725 }
726 fn bar(n: u8) void {
727 var other: *const fn (u8) void = undefined;
728 other = &foo; // runtime-known pointer
729 return @call(.always_tail, other, .{n});
730 }
731 };
732
733 S.foo(100);
734}