master
  1const builtin = @import("builtin");
  2const std = @import("std");
  3const expect = std.testing.expect;
  4
  5fn add(args: anytype) i32 {
  6    var sum = @as(i32, 0);
  7    {
  8        comptime var i: usize = 0;
  9        inline while (i < args.len) : (i += 1) {
 10            sum += args[i];
 11        }
 12    }
 13    return sum;
 14}
 15
 16test "add arbitrary args" {
 17    try expect(add(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10);
 18    try expect(add(.{@as(i32, 1234)}) == 1234);
 19    try expect(add(.{}) == 0);
 20}
 21
 22fn readFirstVarArg(args: anytype) void {
 23    _ = args[0];
 24}
 25
 26test "send void arg to var args" {
 27    readFirstVarArg(.{{}});
 28}
 29
 30test "pass args directly" {
 31    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 32
 33    try expect(addSomeStuff(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10);
 34    try expect(addSomeStuff(.{@as(i32, 1234)}) == 1234);
 35    try expect(addSomeStuff(.{}) == 0);
 36}
 37
 38fn addSomeStuff(args: anytype) i32 {
 39    return add(args);
 40}
 41
 42test "runtime parameter before var args" {
 43    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 44
 45    try expect((try extraFn(10, .{})) == 0);
 46    try expect((try extraFn(10, .{false})) == 1);
 47    try expect((try extraFn(10, .{ false, true })) == 2);
 48
 49    comptime {
 50        try expect((try extraFn(10, .{})) == 0);
 51        try expect((try extraFn(10, .{false})) == 1);
 52        try expect((try extraFn(10, .{ false, true })) == 2);
 53    }
 54}
 55
 56fn extraFn(extra: u32, args: anytype) !usize {
 57    _ = extra;
 58    if (args.len >= 1) {
 59        try expect(args[0] == false);
 60    }
 61    if (args.len >= 2) {
 62        try expect(args[1] == true);
 63    }
 64    return args.len;
 65}
 66
 67const foos = [_]fn (anytype) bool{
 68    foo1,
 69    foo2,
 70};
 71
 72fn foo1(args: anytype) bool {
 73    _ = args;
 74    return true;
 75}
 76fn foo2(args: anytype) bool {
 77    _ = args;
 78    return false;
 79}
 80
 81test "array of var args functions" {
 82    try expect(foos[0](.{}));
 83    try expect(!foos[1](.{}));
 84}
 85
 86test "pass zero length array to var args param" {
 87    doNothingWithFirstArg(.{""});
 88}
 89
 90fn doNothingWithFirstArg(args: anytype) void {
 91    _ = args[0];
 92}
 93
 94test "simple variadic function" {
 95    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 96    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
 97    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
 98    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
 99    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
100    if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
101        // https://github.com/ziglang/zig/issues/14096
102        return error.SkipZigTest;
103    }
104    if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
105    if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
106    if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
107    if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
108
109    const S = struct {
110        fn simple(...) callconv(.c) c_int {
111            var ap = @cVaStart();
112            defer @cVaEnd(&ap);
113            return @cVaArg(&ap, c_int);
114        }
115
116        fn compatible(_: c_int, ...) callconv(.c) c_int {
117            var ap = @cVaStart();
118            defer @cVaEnd(&ap);
119            return @cVaArg(&ap, c_int);
120        }
121
122        fn add(count: c_int, ...) callconv(.c) c_int {
123            var ap = @cVaStart();
124            defer @cVaEnd(&ap);
125            var i: usize = 0;
126            var sum: c_int = 0;
127            while (i < count) : (i += 1) {
128                sum += @cVaArg(&ap, c_int);
129            }
130            return sum;
131        }
132    };
133
134    if (builtin.zig_backend != .stage2_c) {
135        // pre C23 doesn't support varargs without a preceding runtime arg.
136        try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0)));
137        try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024)));
138    }
139    try std.testing.expectEqual(@as(c_int, 0), S.compatible(undefined, @as(c_int, 0)));
140    try std.testing.expectEqual(@as(c_int, 1024), S.compatible(undefined, @as(c_int, 1024)));
141    try std.testing.expectEqual(@as(c_int, 0), S.add(0));
142    try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1)));
143    try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2)));
144
145    {
146        // Test type coercion of a var args argument.
147        // Originally reported at https://github.com/ziglang/zig/issues/16197
148        var runtime: bool = true;
149        var a: i32 = 1;
150        var b: i32 = 2;
151        _ = .{ &runtime, &a, &b };
152        try expect(1 == S.add(1, if (runtime) a else b));
153    }
154}
155
156test "coerce reference to var arg" {
157    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
158    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
159    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
160    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
161    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
162    if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
163        // https://github.com/ziglang/zig/issues/14096
164        return error.SkipZigTest;
165    }
166    if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
167    if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
168
169    const S = struct {
170        fn addPtr(count: c_int, ...) callconv(.c) c_int {
171            var ap = @cVaStart();
172            defer @cVaEnd(&ap);
173            var i: usize = 0;
174            var sum: c_int = 0;
175            while (i < count) : (i += 1) {
176                sum += @cVaArg(&ap, *c_int).*;
177            }
178            return sum;
179        }
180    };
181
182    // Originally reported at https://github.com/ziglang/zig/issues/17494
183    var a: i32 = 12;
184    var b: i32 = 34;
185    try expect(46 == S.addPtr(2, &a, &b));
186}
187
188test "variadic functions" {
189    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
190    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
191    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
192    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
193    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
194    if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
195        // https://github.com/ziglang/zig/issues/14096
196        return error.SkipZigTest;
197    }
198    if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
199    if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
200    if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
201    if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
202
203    const S = struct {
204        fn printf(buffer: [*]u8, format: [*:0]const u8, ...) callconv(.c) void {
205            var ap = @cVaStart();
206            defer @cVaEnd(&ap);
207            vprintf(buffer, format, &ap);
208        }
209
210        fn vprintf(buffer: [*]u8, format: [*:0]const u8, ap: *std.builtin.VaList) callconv(.c) void {
211            var i: usize = 0;
212            for (format[0..3]) |byte| switch (byte) {
213                's' => {
214                    const arg = @cVaArg(ap, [*:0]const u8);
215                    buffer[i..][0..5].* = arg[0..5].*;
216                    i += 5;
217                },
218                'd' => {
219                    const arg = @cVaArg(ap, c_int);
220                    switch (arg) {
221                        1 => {
222                            buffer[i] = '1';
223                            i += 1;
224                        },
225                        5 => {
226                            buffer[i] = '5';
227                            i += 1;
228                        },
229                        else => unreachable,
230                    }
231                },
232                else => unreachable,
233            };
234        }
235    };
236
237    var buffer: [7]u8 = undefined;
238    S.printf(&buffer, "dsd", @as(c_int, 1), @as([*:0]const u8, "hello"), @as(c_int, 5));
239    try expect(std.mem.eql(u8, &buffer, "1hello5"));
240}
241
242test "copy VaList" {
243    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
244    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
245    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
246    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
247    if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
248        // https://github.com/ziglang/zig/issues/14096
249        return error.SkipZigTest;
250    }
251    if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
252    if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
253    if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
254    if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
255
256    const S = struct {
257        fn add(count: c_int, ...) callconv(.c) c_int {
258            var ap = @cVaStart();
259            defer @cVaEnd(&ap);
260            var copy = @cVaCopy(&ap);
261            defer @cVaEnd(&copy);
262            var i: usize = 0;
263            var sum: c_int = 0;
264            while (i < count) : (i += 1) {
265                sum += @cVaArg(&ap, c_int);
266                sum += @cVaArg(&copy, c_int) * 2;
267            }
268            return sum;
269        }
270    };
271
272    try std.testing.expectEqual(@as(c_int, 0), S.add(0));
273    try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));
274    try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));
275}
276
277test "unused VaList arg" {
278    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
279    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
280    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
281    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
282    if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
283        // https://github.com/ziglang/zig/issues/14096
284        return error.SkipZigTest;
285    }
286    if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) {
287        // https://github.com/ziglang/zig/issues/16961
288        return error.SkipZigTest; // TODO
289    }
290    if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
291    if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
292    if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
293
294    const S = struct {
295        fn thirdArg(dummy: c_int, ...) callconv(.c) c_int {
296            _ = dummy;
297
298            var ap = @cVaStart();
299            defer @cVaEnd(&ap);
300
301            _ = @cVaArg(&ap, c_int);
302            return @cVaArg(&ap, c_int);
303        }
304    };
305    const x = S.thirdArg(0, @as(c_int, 1), @as(c_int, 2));
306    try std.testing.expectEqual(@as(c_int, 2), x);
307}