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(©);
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(©, 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}