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_spirv) return error.SkipZigTest;
98 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
100 // https://github.com/ziglang/zig/issues/14096
101 return error.SkipZigTest;
102 }
103 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
104 if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
105 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
106 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
107
108 const S = struct {
109 fn simple(...) callconv(.c) c_int {
110 var ap = @cVaStart();
111 defer @cVaEnd(&ap);
112 return @cVaArg(&ap, c_int);
113 }
114
115 fn compatible(_: c_int, ...) callconv(.c) c_int {
116 var ap = @cVaStart();
117 defer @cVaEnd(&ap);
118 return @cVaArg(&ap, c_int);
119 }
120
121 fn add(count: c_int, ...) callconv(.c) c_int {
122 var ap = @cVaStart();
123 defer @cVaEnd(&ap);
124 var i: usize = 0;
125 var sum: c_int = 0;
126 while (i < count) : (i += 1) {
127 sum += @cVaArg(&ap, c_int);
128 }
129 return sum;
130 }
131 };
132
133 if (builtin.zig_backend != .stage2_c) {
134 // pre C23 doesn't support varargs without a preceding runtime arg.
135 try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0)));
136 try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024)));
137 }
138 try std.testing.expectEqual(@as(c_int, 0), S.compatible(undefined, @as(c_int, 0)));
139 try std.testing.expectEqual(@as(c_int, 1024), S.compatible(undefined, @as(c_int, 1024)));
140 try std.testing.expectEqual(@as(c_int, 0), S.add(0));
141 try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1)));
142 try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2)));
143
144 {
145 // Test type coercion of a var args argument.
146 // Originally reported at https://github.com/ziglang/zig/issues/16197
147 var runtime: bool = true;
148 var a: i32 = 1;
149 var b: i32 = 2;
150 _ = .{ &runtime, &a, &b };
151 try expect(1 == S.add(1, if (runtime) a else b));
152 }
153}
154
155test "coerce reference to var arg" {
156 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
157 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
158 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
159 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
160 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
161 // https://github.com/ziglang/zig/issues/14096
162 return error.SkipZigTest;
163 }
164 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
165 if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
166
167 const S = struct {
168 fn addPtr(count: c_int, ...) callconv(.c) c_int {
169 var ap = @cVaStart();
170 defer @cVaEnd(&ap);
171 var i: usize = 0;
172 var sum: c_int = 0;
173 while (i < count) : (i += 1) {
174 sum += @cVaArg(&ap, *c_int).*;
175 }
176 return sum;
177 }
178 };
179
180 // Originally reported at https://github.com/ziglang/zig/issues/17494
181 var a: i32 = 12;
182 var b: i32 = 34;
183 try expect(46 == S.addPtr(2, &a, &b));
184}
185
186test "variadic functions" {
187 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
188 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
189 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
190 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
191 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
192 // https://github.com/ziglang/zig/issues/14096
193 return error.SkipZigTest;
194 }
195 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
196 if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
197 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
198 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
199
200 const S = struct {
201 fn printf(buffer: [*]u8, format: [*:0]const u8, ...) callconv(.c) void {
202 var ap = @cVaStart();
203 defer @cVaEnd(&ap);
204 vprintf(buffer, format, &ap);
205 }
206
207 fn vprintf(buffer: [*]u8, format: [*:0]const u8, ap: *std.builtin.VaList) callconv(.c) void {
208 var i: usize = 0;
209 for (format[0..3]) |byte| switch (byte) {
210 's' => {
211 const arg = @cVaArg(ap, [*:0]const u8);
212 buffer[i..][0..5].* = arg[0..5].*;
213 i += 5;
214 },
215 'd' => {
216 const arg = @cVaArg(ap, c_int);
217 switch (arg) {
218 1 => {
219 buffer[i] = '1';
220 i += 1;
221 },
222 5 => {
223 buffer[i] = '5';
224 i += 1;
225 },
226 else => unreachable,
227 }
228 },
229 else => unreachable,
230 };
231 }
232 };
233
234 var buffer: [7]u8 = undefined;
235 S.printf(&buffer, "dsd", @as(c_int, 1), @as([*:0]const u8, "hello"), @as(c_int, 5));
236 try expect(std.mem.eql(u8, &buffer, "1hello5"));
237}
238
239test "copy VaList" {
240 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
241 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
242 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
243 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
244 // https://github.com/ziglang/zig/issues/14096
245 return error.SkipZigTest;
246 }
247 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
248 if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
249 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
250 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
251
252 const S = struct {
253 fn add(count: c_int, ...) callconv(.c) c_int {
254 var ap = @cVaStart();
255 defer @cVaEnd(&ap);
256 var copy = @cVaCopy(&ap);
257 defer @cVaEnd(&copy);
258 var i: usize = 0;
259 var sum: c_int = 0;
260 while (i < count) : (i += 1) {
261 sum += @cVaArg(&ap, c_int);
262 sum += @cVaArg(&copy, c_int) * 2;
263 }
264 return sum;
265 }
266 };
267
268 try std.testing.expectEqual(@as(c_int, 0), S.add(0));
269 try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));
270 try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));
271}
272
273test "unused VaList arg" {
274 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
275 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
276 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
277 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
278 // https://github.com/ziglang/zig/issues/14096
279 return error.SkipZigTest;
280 }
281 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) {
282 // https://github.com/ziglang/zig/issues/16961
283 return error.SkipZigTest; // TODO
284 }
285 if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350
286 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718
287 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064
288
289 const S = struct {
290 fn thirdArg(dummy: c_int, ...) callconv(.c) c_int {
291 _ = dummy;
292
293 var ap = @cVaStart();
294 defer @cVaEnd(&ap);
295
296 _ = @cVaArg(&ap, c_int);
297 return @cVaArg(&ap, c_int);
298 }
299 };
300 const x = S.thirdArg(0, @as(c_int, 1), @as(c_int, 2));
301 try std.testing.expectEqual(@as(c_int, 2), x);
302}
303
304test "floating point VaList args" {
305 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
306 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
307 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
308 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/16961
309
310 // Float register arguments are handled specially on cc == .x86_64_win, so it's important that we test all 4 slots,
311 // and pre-C23 doesn't allow a variadic function without at least one non-variadic argument.
312 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
313
314 const S = struct {
315 fn proxy(...) callconv(.c) void {
316 var ap = @cVaStart();
317 defer @cVaEnd(&ap);
318
319 var out_f32: [3]f32 = undefined;
320 var out_f64: [3]f64 = undefined;
321 out_f32[0] = @cVaArg(&ap, f32);
322 out_f64[0] = @cVaArg(&ap, f64);
323 out_f32[1] = @cVaArg(&ap, f32);
324 out_f64[1] = @cVaArg(&ap, f64);
325 out_f32[2] = @cVaArg(&ap, f32);
326 out_f64[2] = @cVaArg(&ap, f64);
327 @cVaArg(&ap, *[3]f32).* = out_f32;
328 @cVaArg(&ap, *[3]f64).* = out_f64;
329 }
330 };
331
332 const expected_f32: []const f32 = &.{ 1000, std.math.floatMax(f32), std.math.floatMin(f32) };
333 const expected_f64: []const f64 = &.{ 2000, std.math.floatMax(f64), std.math.floatMin(f64) };
334 var actual_f32: [3]f32 = undefined;
335 var actual_f64: [3]f64 = undefined;
336 S.proxy(
337 expected_f32[0],
338 expected_f64[0],
339 expected_f32[1],
340 expected_f64[1],
341 expected_f32[2],
342 expected_f64[2],
343 &actual_f32,
344 &actual_f64,
345 );
346
347 try std.testing.expectEqualSlices(f32, expected_f32, &actual_f32);
348 try std.testing.expectEqualSlices(f64, expected_f64, &actual_f64);
349}