authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-18 16:24:13-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-18 16:24:13-05:00
logaca9c74e80e106309b9783ff251ab0cdd3fb9626
tree16c65995ac6d3b434af61c4cd61a191b81dd93a0
parentd93edadead45e447e6bc16c0934a3031a06d0fd8
parent9bb1104e373dec192fb2a22d48b023330ddbaeae
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13914 from Vexu/variadic

implement defining C variadic functions

26 files changed, 661 insertions(+), 26 deletions(-)

doc/langref.html.in+53-6
......@@ -8088,6 +8088,35 @@ test "main" {
80888088 {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#}
80898089 {#header_close#}
80908090
8091 {#header_open|@cVaArg#}
8092 <pre>{#syntax#}@cVaArg(operand: *std.builtin.VaList, comptime T: type) T{#endsyntax#}</pre>
8093 <p>
8094 Implements the C macro {#syntax#}va_arg{#endsyntax#}.
8095 </p>
8096 {#see_also|@cVaCopy|@cVaEnd|@cVaStart#}
8097 {#header_close#}
8098 {#header_open|@cVaCopy#}
8099 <pre>{#syntax#}@cVaCopy(src: *std.builtin.VaList) std.builtin.VaList{#endsyntax#}</pre>
8100 <p>
8101 Implements the C macro {#syntax#}va_copy{#endsyntax#}.
8102 </p>
8103 {#see_also|@cVaArg|@cVaEnd|@cVaStart#}
8104 {#header_close#}
8105 {#header_open|@cVaEnd#}
8106 <pre>{#syntax#}@cVaEnd(src: *std.builtin.VaList) void{#endsyntax#}</pre>
8107 <p>
8108 Implements the C macro {#syntax#}va_end{#endsyntax#}.
8109 </p>
8110 {#see_also|@cVaArg|@cVaCopy|@cVaStart#}
8111 {#header_close#}
8112 {#header_open|@cVaStart#}
8113 <pre>{#syntax#}@cVaStart() std.builtin.VaList{#endsyntax#}</pre>
8114 <p>
8115 Implements the C macro {#syntax#}va_start{#endsyntax#}. Only valid inside a variadic function.
8116 </p>
8117 {#see_also|@cVaArg|@cVaCopy|@cVaEnd#}
8118 {#header_close#}
8119
80918120 {#header_open|@divExact#}
80928121 <pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre>
80938122 <p>
......@@ -10802,14 +10831,32 @@ test "variadic function" {
1080210831}
1080310832 {#code_end#}
1080410833 <p>
10805 Non extern variadic functions are currently not implemented, but there
10806 is an accepted proposal. See <a href="https://github.com/ziglang/zig/issues/515">#515</a>.
10834 Variadic functions can be implemented using {#link|@cVaStart#}, {#link|@cVaEnd#}, {#link|@cVaArg#} and {#link|@cVaCopy#}
1080710835 </p>
10808 {#code_begin|obj_err|non-extern function is variadic#}
10809export fn printf(format: [*:0]const u8, ...) c_int {
10810 _ = format;
10836 {#code_begin|test|defining_variadic_function#}
10837const std = @import("std");
10838const testing = std.testing;
10839const builtin = @import("builtin");
1081110840
10812 return 0;
10841fn add(count: c_int, ...) callconv(.C) c_int {
10842 var ap = @cVaStart();
10843 defer @cVaEnd(&ap);
10844 var i: usize = 0;
10845 var sum: c_int = 0;
10846 while (i < count) : (i += 1) {
10847 sum += @cVaArg(&ap, c_int);
10848 }
10849 return sum;
10850}
10851
10852test "defining a variadic function" {
10853 // Variadic functions are currently disabled on some targets due to miscompilations.
10854 if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest;
10855 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest;
10856
10857 try std.testing.expectEqual(@as(c_int, 0), add(0));
10858 try std.testing.expectEqual(@as(c_int, 1), add(1, @as(c_int, 1)));
10859 try std.testing.expectEqual(@as(c_int, 3), add(2, @as(c_int, 1), @as(c_int, 2)));
1081310860}
1081410861 {#code_end#}
1081510862 {#header_close#}
lib/std/builtin.zig+81
......@@ -620,6 +620,87 @@ pub const CallModifier = enum {
620620 compile_time,
621621};
622622
623/// This data structure is used by the Zig language code generation and
624/// therefore must be kept in sync with the compiler implementation.
625pub const VaListAarch64 = extern struct {
626 __stack: *anyopaque,
627 __gr_top: *anyopaque,
628 __vr_top: *anyopaque,
629 __gr_offs: c_int,
630 __vr_offs: c_int,
631};
632
633/// This data structure is used by the Zig language code generation and
634/// therefore must be kept in sync with the compiler implementation.
635pub const VaListHexagon = extern struct {
636 __gpr: c_long,
637 __fpr: c_long,
638 __overflow_arg_area: *anyopaque,
639 __reg_save_area: *anyopaque,
640};
641
642/// This data structure is used by the Zig language code generation and
643/// therefore must be kept in sync with the compiler implementation.
644pub const VaListPowerPc = extern struct {
645 gpr: u8,
646 fpr: u8,
647 reserved: c_ushort,
648 overflow_arg_area: *anyopaque,
649 reg_save_area: *anyopaque,
650};
651
652/// This data structure is used by the Zig language code generation and
653/// therefore must be kept in sync with the compiler implementation.
654pub const VaListS390x = extern struct {
655 __current_saved_reg_area_pointer: *anyopaque,
656 __saved_reg_area_end_pointer: *anyopaque,
657 __overflow_area_pointer: *anyopaque,
658};
659
660/// This data structure is used by the Zig language code generation and
661/// therefore must be kept in sync with the compiler implementation.
662pub const VaListX86_64 = extern struct {
663 gp_offset: c_uint,
664 fp_offset: c_uint,
665 overflow_arg_area: *anyopaque,
666 reg_save_area: *anyopaque,
667};
668
669/// This data structure is used by the Zig language code generation and
670/// therefore must be kept in sync with the compiler implementation.
671pub const VaList = switch (builtin.cpu.arch) {
672 .aarch64 => switch (builtin.os.tag) {
673 .windows => *u8,
674 .ios, .macos, .tvos, .watchos => *u8,
675 else => @compileError("disabled due to miscompilations"), // VaListAarch64,
676 },
677 .arm => switch (builtin.os.tag) {
678 .ios, .macos, .tvos, .watchos => *u8,
679 else => *anyopaque,
680 },
681 .amdgcn => *u8,
682 .avr => *anyopaque,
683 .bpfel, .bpfeb => *anyopaque,
684 .hexagon => if (builtin.target.isMusl()) VaListHexagon else *u8,
685 .mips, .mipsel, .mips64, .mips64el => *anyopaque,
686 .riscv32, .riscv64 => *anyopaque,
687 .powerpc, .powerpcle => switch (builtin.os.tag) {
688 .ios, .macos, .tvos, .watchos, .aix => *u8,
689 else => VaListPowerPc,
690 },
691 .powerpc64, .powerpc64le => *u8,
692 .sparc, .sparcel, .sparc64 => *anyopaque,
693 .spirv32, .spirv64 => *anyopaque,
694 .s390x => VaListS390x,
695 .wasm32, .wasm64 => *anyopaque,
696 .x86 => *u8,
697 .x86_64 => switch (builtin.os.tag) {
698 .windows => @compileError("disabled due to miscompilations"), // *u8,
699 else => VaListX86_64,
700 },
701 else => @compileError("VaList not supported for this target yet"),
702};
703
623704/// This data structure is used by the Zig language code generation and
624705/// therefore must be kept in sync with the compiler implementation.
625706pub const PrefetchOptions = struct {
src/Air.zig+17
......@@ -741,6 +741,19 @@ pub const Inst = struct {
741741 /// Uses the `vector_store_elem` field.
742742 vector_store_elem,
743743
744 /// Implements @cVaArg builtin.
745 /// Uses the `ty_op` field.
746 c_va_arg,
747 /// Implements @cVaCopy builtin.
748 /// Uses the `ty_op` field.
749 c_va_copy,
750 /// Implements @cVaEnd builtin.
751 /// Uses the `un_op` field.
752 c_va_end,
753 /// Implements @cVaStart builtin.
754 /// Uses the `ty` field.
755 c_va_start,
756
744757 pub fn fromCmpOp(op: std.math.CompareOperator, optimized: bool) Tag {
745758 switch (op) {
746759 .lt => return if (optimized) .cmp_lt_optimized else .cmp_lt,
......@@ -1092,6 +1105,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
10921105 .ret_ptr,
10931106 .arg,
10941107 .err_return_trace,
1108 .c_va_start,
10951109 => return datas[inst].ty,
10961110
10971111 .assembly,
......@@ -1156,6 +1170,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
11561170 .byte_swap,
11571171 .bit_reverse,
11581172 .addrspace_cast,
1173 .c_va_arg,
1174 .c_va_copy,
11591175 => return air.getRefType(datas[inst].ty_op.ty),
11601176
11611177 .loop,
......@@ -1187,6 +1203,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
11871203 .prefetch,
11881204 .set_err_return_trace,
11891205 .vector_store_elem,
1206 .c_va_end,
11901207 => return Type.void,
11911208
11921209 .ptrtoint,
src/AstGen.zig+47-6
......@@ -42,6 +42,7 @@ string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.d
4242compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
4343/// The topmost block of the current function.
4444fn_block: ?*GenZir = null,
45fn_var_args: bool = false,
4546/// Maps string table indexes to the first `@import` ZIR instruction
4647/// that uses this string as the operand.
4748imports: std.AutoArrayHashMapUnmanaged(u32, Ast.TokenIndex) = .{},
......@@ -3892,10 +3893,6 @@ fn fnDecl(
38923893 .noalias_bits = noalias_bits,
38933894 });
38943895 } else func: {
3895 if (is_var_args) {
3896 return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{});
3897 }
3898
38993896 // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
39003897 fn_gz.instructions_top = ret_gz.instructions.items.len;
39013898
......@@ -3903,6 +3900,10 @@ fn fnDecl(
39033900 astgen.fn_block = &fn_gz;
39043901 defer astgen.fn_block = prev_fn_block;
39053902
3903 const prev_var_args = astgen.fn_var_args;
3904 astgen.fn_var_args = is_var_args;
3905 defer astgen.fn_var_args = prev_var_args;
3906
39063907 astgen.advanceSourceCursorToNode(body_node);
39073908 const lbrace_line = astgen.source_line - decl_gz.decl_line;
39083909 const lbrace_column = astgen.source_column;
......@@ -6071,7 +6072,7 @@ fn whileExpr(
60716072 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
60726073 break :c .{
60736074 .inst = err_union,
6074 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.then_expr),
6075 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr),
60756076 };
60766077 } else if (while_full.payload_token) |_| {
60776078 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
......@@ -6079,7 +6080,7 @@ fn whileExpr(
60796080 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
60806081 break :c .{
60816082 .inst = optional,
6082 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.then_expr),
6083 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
60836084 };
60846085 } else {
60856086 const cond = try expr(&cond_scope, &cond_scope.base, bool_ri, while_full.ast.cond_expr);
......@@ -8384,6 +8385,46 @@ fn builtinCall(
83848385 });
83858386 return rvalue(gz, ri, result, node);
83868387 },
8388 .c_va_arg => {
8389 if (astgen.fn_block == null) {
8390 return astgen.failNode(node, "'@cVaArg' outside function scope", .{});
8391 }
8392 const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{
8393 .node = gz.nodeIndexToRelative(node),
8394 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8395 .rhs = try typeExpr(gz, scope, params[1]),
8396 });
8397 return rvalue(gz, ri, result, node);
8398 },
8399 .c_va_copy => {
8400 if (astgen.fn_block == null) {
8401 return astgen.failNode(node, "'@cVaCopy' outside function scope", .{});
8402 }
8403 const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{
8404 .node = gz.nodeIndexToRelative(node),
8405 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
8406 });
8407 return rvalue(gz, ri, result, node);
8408 },
8409 .c_va_end => {
8410 if (astgen.fn_block == null) {
8411 return astgen.failNode(node, "'@cVaEnd' outside function scope", .{});
8412 }
8413 const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{
8414 .node = gz.nodeIndexToRelative(node),
8415 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
8416 });
8417 return rvalue(gz, ri, result, node);
8418 },
8419 .c_va_start => {
8420 if (astgen.fn_block == null) {
8421 return astgen.failNode(node, "'@cVaStart' outside function scope", .{});
8422 }
8423 if (!astgen.fn_var_args) {
8424 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{});
8425 }
8426 return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node);
8427 },
83878428 }
83888429}
83898430
src/BuiltinFn.zig+28
......@@ -30,6 +30,10 @@ pub const Tag = enum {
3030 compile_log,
3131 ctz,
3232 c_undef,
33 c_va_arg,
34 c_va_copy,
35 c_va_end,
36 c_va_start,
3337 div_exact,
3438 div_floor,
3539 div_trunc,
......@@ -354,6 +358,30 @@ pub const list = list: {
354358 .param_count = 1,
355359 },
356360 },
361 .{
362 "@cVaArg", .{
363 .tag = .c_va_arg,
364 .param_count = 2,
365 },
366 },
367 .{
368 "@cVaCopy", .{
369 .tag = .c_va_copy,
370 .param_count = 1,
371 },
372 },
373 .{
374 "@cVaEnd", .{
375 .tag = .c_va_end,
376 .param_count = 1,
377 },
378 },
379 .{
380 "@cVaStart", .{
381 .tag = .c_va_start,
382 .param_count = 0,
383 },
384 },
357385 .{
358386 "@divExact",
359387 .{
src/Liveness.zig+8
......@@ -238,6 +238,7 @@ pub fn categorizeOperand(
238238 .wasm_memory_size,
239239 .err_return_trace,
240240 .save_err_return_trace_index,
241 .c_va_start,
241242 => return .none,
242243
243244 .fence => return .write,
......@@ -279,6 +280,8 @@ pub fn categorizeOperand(
279280 .splat,
280281 .error_set_has_value,
281282 .addrspace_cast,
283 .c_va_arg,
284 .c_va_copy,
282285 => {
283286 const o = air_datas[inst].ty_op;
284287 if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none);
......@@ -322,6 +325,7 @@ pub fn categorizeOperand(
322325 .trunc_float,
323326 .neg,
324327 .cmp_lt_errors_len,
328 .c_va_end,
325329 => {
326330 const o = air_datas[inst].un_op;
327331 if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none);
......@@ -857,6 +861,7 @@ fn analyzeInst(
857861 .wasm_memory_size,
858862 .err_return_trace,
859863 .save_err_return_trace_index,
864 .c_va_start,
860865 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
861866
862867 .not,
......@@ -898,6 +903,8 @@ fn analyzeInst(
898903 .splat,
899904 .error_set_has_value,
900905 .addrspace_cast,
906 .c_va_arg,
907 .c_va_copy,
901908 => {
902909 const o = inst_datas[inst].ty_op;
903910 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
......@@ -936,6 +943,7 @@ fn analyzeInst(
936943 .neg_optimized,
937944 .cmp_lt_errors_len,
938945 .set_err_return_trace,
946 .c_va_end,
939947 => {
940948 const operand = inst_datas[inst].un_op;
941949 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/Sema.zig+119-1
......@@ -1148,6 +1148,10 @@ fn analyzeBodyInner(
11481148 .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended),
11491149 .cmpxchg => try sema.zirCmpxchg( block, extended),
11501150 .addrspace_cast => try sema.zirAddrSpaceCast( block, extended),
1151 .c_va_arg => try sema.zirCVaArg( block, extended),
1152 .c_va_copy => try sema.zirCVaCopy( block, extended),
1153 .c_va_end => try sema.zirCVaEnd( block, extended),
1154 .c_va_start => try sema.zirCVaStart( block, extended),
11511155 // zig fmt: on
11521156
11531157 .fence => {
......@@ -6426,6 +6430,11 @@ fn analyzeCall(
64266430 else => unreachable,
64276431 };
64286432 if (!is_comptime_call and module_fn.state == .sema_failure) return error.AnalysisFail;
6433 if (func_ty_info.is_var_args) {
6434 return sema.fail(block, call_src, "{s} call of variadic function", .{
6435 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
6436 });
6437 }
64296438
64306439 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
64316440 // or an inlined call depending on what union tag the `label` field is
......@@ -8407,6 +8416,7 @@ fn funcCommon(
84078416) CompileError!Air.Inst.Ref {
84088417 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
84098418 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset };
8419 const func_src = LazySrcLoc.nodeOffset(src_node_offset);
84108420
84118421 var is_generic = bare_return_type.tag() == .generic_poison or
84128422 alignment == null or
......@@ -8414,6 +8424,15 @@ fn funcCommon(
84148424 section == .generic or
84158425 cc == null;
84168426
8427 if (var_args) {
8428 if (is_generic) {
8429 return sema.fail(block, func_src, "generic function cannot be variadic", .{});
8430 }
8431 if (cc.? != .C) {
8432 return sema.fail(block, cc_src, "variadic function must have 'C' calling convention", .{});
8433 }
8434 }
8435
84178436 var destroy_fn_on_error = false;
84188437 const new_func: *Module.Fn = new_func: {
84198438 if (!has_body) break :new_func undefined;
......@@ -16353,6 +16372,15 @@ fn finishCondBr(
1635316372 return Air.indexToRef(block_inst);
1635416373}
1635516374
16375fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
16376 switch (ty.zigTypeTag()) {
16377 .Optional, .Null, .Undefined => return,
16378 .Pointer => if (ty.isPtrLikeOptional()) return,
16379 else => {},
16380 }
16381 return sema.failWithExpectedOptionalType(block, src, ty);
16382}
16383
1635616384fn zirIsNonNull(
1635716385 sema: *Sema,
1635816386 block: *Block,
......@@ -16364,6 +16392,7 @@ fn zirIsNonNull(
1636416392 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1636516393 const src = inst_data.src();
1636616394 const operand = try sema.resolveInst(inst_data.operand);
16395 try sema.checkNullableType(block, src, sema.typeOf(operand));
1636716396 return sema.analyzeIsNull(block, src, operand, true);
1636816397}
1636916398
......@@ -16378,6 +16407,7 @@ fn zirIsNonNullPtr(
1637816407 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1637916408 const src = inst_data.src();
1638016409 const ptr = try sema.resolveInst(inst_data.operand);
16410 try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2());
1638116411 if ((try sema.resolveMaybeUndefVal(ptr)) == null) {
1638216412 return block.addUnOp(.is_non_null_ptr, ptr);
1638316413 }
......@@ -16385,12 +16415,23 @@ fn zirIsNonNullPtr(
1638516415 return sema.analyzeIsNull(block, src, loaded, true);
1638616416}
1638716417
16418fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
16419 switch (ty.zigTypeTag()) {
16420 .ErrorSet, .ErrorUnion, .Undefined => return,
16421 else => return sema.fail(block, src, "expected error union type, found '{}'", .{
16422 ty.fmt(sema.mod),
16423 }),
16424 }
16425}
16426
1638816427fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1638916428 const tracy = trace(@src());
1639016429 defer tracy.end();
1639116430
1639216431 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
16432 const src = inst_data.src();
1639316433 const operand = try sema.resolveInst(inst_data.operand);
16434 try sema.checkErrorType(block, src, sema.typeOf(operand));
1639416435 return sema.analyzeIsNonErr(block, inst_data.src(), operand);
1639516436}
1639616437
......@@ -16401,6 +16442,7 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1640116442 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1640216443 const src = inst_data.src();
1640316444 const ptr = try sema.resolveInst(inst_data.operand);
16445 try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2());
1640416446 const loaded = try sema.analyzeLoad(block, src, ptr, src);
1640516447 return sema.analyzeIsNonErr(block, src, loaded);
1640616448}
......@@ -19020,6 +19062,79 @@ fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
1902019062 });
1902119063}
1902219064
19065fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
19066 const va_list_ty = try sema.getBuiltinType("VaList");
19067 const va_list_ptr = try Type.ptr(sema.arena, sema.mod, .{
19068 .pointee_type = va_list_ty,
19069 .mutable = true,
19070 .@"addrspace" = .generic,
19071 });
19072
19073 const inst = try sema.resolveInst(zir_ref);
19074 return sema.coerce(block, va_list_ptr, inst, src);
19075}
19076
19077fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
19078 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
19079 const src = LazySrcLoc.nodeOffset(extra.node);
19080 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
19081 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
19082
19083 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
19084 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
19085
19086 if (!try sema.validateExternType(arg_ty, .param_ty)) {
19087 const msg = msg: {
19088 const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)});
19089 errdefer msg.destroy(sema.gpa);
19090
19091 const src_decl = sema.mod.declPtr(block.src_decl);
19092 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl), arg_ty, .param_ty);
19093
19094 try sema.addDeclaredHereNote(msg, arg_ty);
19095 break :msg msg;
19096 };
19097 return sema.failWithOwnedErrorMsg(msg);
19098 }
19099
19100 try sema.requireRuntimeBlock(block, src, null);
19101 return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
19102}
19103
19104fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
19105 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19106 const src = LazySrcLoc.nodeOffset(extra.node);
19107 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
19108
19109 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
19110 const va_list_ty = try sema.getBuiltinType("VaList");
19111
19112 try sema.requireRuntimeBlock(block, src, null);
19113 return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
19114}
19115
19116fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
19117 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19118 const src = LazySrcLoc.nodeOffset(extra.node);
19119 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
19120
19121 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
19122
19123 try sema.requireRuntimeBlock(block, src, null);
19124 return block.addUnOp(.c_va_end, va_list_ref);
19125}
19126
19127fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
19128 const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
19129
19130 const va_list_ty = try sema.getBuiltinType("VaList");
19131 try sema.requireRuntimeBlock(block, src, null);
19132 return block.addInst(.{
19133 .tag = .c_va_start,
19134 .data = .{ .ty = va_list_ty },
19135 });
19136}
19137
1902319138fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1902419139 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1902519140 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
......@@ -21524,7 +21639,10 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
2152421639 else => |e| return e,
2152521640 };
2152621641 break :blk cc_tv.val.toEnum(std.builtin.CallingConvention);
21527 } else std.builtin.CallingConvention.Unspecified;
21642 } else if (sema.owner_decl.is_exported and has_body)
21643 .C
21644 else
21645 .Unspecified;
2152821646
2152921647 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
2153021648 const body_len = sema.code.extra[extra_index];
src/Zir.zig+12
......@@ -1993,6 +1993,18 @@ pub const Inst = struct {
19931993 /// Implement the builtin `@addrSpaceCast`
19941994 /// `Operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
19951995 addrspace_cast,
1996 /// Implement builtin `@cVaArg`.
1997 /// `operand` is payload index to `BinNode`.
1998 c_va_arg,
1999 /// Implement builtin `@cVaStart`.
2000 /// `operand` is payload index to `UnNode`.
2001 c_va_copy,
2002 /// Implement builtin `@cVaStart`.
2003 /// `operand` is payload index to `UnNode`.
2004 c_va_end,
2005 /// Implement builtin `@cVaStart`.
2006 /// `operand` is `src_node: i32`.
2007 c_va_start,
19962008
19972009 pub const InstData = struct {
19982010 opcode: Extended,
src/arch/aarch64/CodeGen.zig+5
......@@ -875,6 +875,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
875875 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
876876 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
877877
878 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
879 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
880 .c_va_end => return self.fail("TODO implement c_va_end", .{}),
881 .c_va_start => return self.fail("TODO implement c_va_start", .{}),
882
878883 .wasm_memory_size => unreachable,
879884 .wasm_memory_grow => unreachable,
880885 // zig fmt: on
src/arch/arm/CodeGen.zig+5
......@@ -785,6 +785,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
785785 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
786786 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
787787
788 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
789 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
790 .c_va_end => return self.fail("TODO implement c_va_end", .{}),
791 .c_va_start => return self.fail("TODO implement c_va_start", .{}),
792
788793 .wasm_memory_size => unreachable,
789794 .wasm_memory_grow => unreachable,
790795 // zig fmt: on
src/arch/riscv64/CodeGen.zig+5
......@@ -699,6 +699,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
699699 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
700700 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
701701
702 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
703 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
704 .c_va_end => return self.fail("TODO implement c_va_end", .{}),
705 .c_va_start => return self.fail("TODO implement c_va_start", .{}),
706
702707 .wasm_memory_size => unreachable,
703708 .wasm_memory_grow => unreachable,
704709 // zig fmt: on
src/arch/sparc64/CodeGen.zig+5
......@@ -716,6 +716,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
716716 .error_set_has_value => @panic("TODO implement error_set_has_value"),
717717 .vector_store_elem => @panic("TODO implement vector_store_elem"),
718718
719 .c_va_arg => @panic("TODO implement c_va_arg"),
720 .c_va_copy => @panic("TODO implement c_va_copy"),
721 .c_va_end => @panic("TODO implement c_va_end"),
722 .c_va_start => @panic("TODO implement c_va_start"),
723
719724 .wasm_memory_size => unreachable,
720725 .wasm_memory_grow => unreachable,
721726 // zig fmt: on
src/arch/wasm/CodeGen.zig+4
......@@ -1972,6 +1972,10 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19721972 .error_set_has_value,
19731973 .addrspace_cast,
19741974 .vector_store_elem,
1975 .c_va_arg,
1976 .c_va_copy,
1977 .c_va_end,
1978 .c_va_start,
19751979 => |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
19761980
19771981 .add_optimized,
src/arch/x86_64/CodeGen.zig+5
......@@ -787,6 +787,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
787787 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
788788 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
789789
790 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
791 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
792 .c_va_end => return self.fail("TODO implement c_va_end", .{}),
793 .c_va_start => return self.fail("TODO implement c_va_start", .{}),
794
790795 .wasm_memory_size => unreachable,
791796 .wasm_memory_grow => unreachable,
792797 // zig fmt: on
src/codegen/c.zig+5
......@@ -2909,6 +2909,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
29092909 .is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}),
29102910 .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}),
29112911 .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}),
2912
2913 .c_va_arg => return f.fail("TODO implement c_va_arg", .{}),
2914 .c_va_copy => return f.fail("TODO implement c_va_copy", .{}),
2915 .c_va_end => return f.fail("TODO implement c_va_end", .{}),
2916 .c_va_start => return f.fail("TODO implement c_va_start", .{}),
29122917 // zig fmt: on
29132918 };
29142919 if (result_value == .local) {
src/codegen/llvm.zig+93
......@@ -4699,6 +4699,11 @@ pub const FuncGen = struct {
46994699 .dbg_block_end => try self.airDbgBlockEnd(),
47004700 .dbg_var_ptr => try self.airDbgVarPtr(inst),
47014701 .dbg_var_val => try self.airDbgVarVal(inst),
4702
4703 .c_va_arg => try self.airCVaArg(inst),
4704 .c_va_copy => try self.airCVaCopy(inst),
4705 .c_va_end => try self.airCVaEnd(inst),
4706 .c_va_start => try self.airCVaStart(inst),
47024707 // zig fmt: on
47034708 };
47044709 if (opt_value) |val| {
......@@ -5136,6 +5141,94 @@ pub const FuncGen = struct {
51365141 return null;
51375142 }
51385143
5144 fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5145 if (self.liveness.isUnused(inst)) return null;
5146
5147 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5148 const list = try self.resolveInst(ty_op.operand);
5149 const arg_ty = self.air.getRefType(ty_op.ty);
5150 const llvm_arg_ty = try self.dg.lowerType(arg_ty);
5151
5152 return self.builder.buildVAArg(list, llvm_arg_ty, "");
5153 }
5154
5155 fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5156 if (self.liveness.isUnused(inst)) return null;
5157
5158 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5159 const src_list = try self.resolveInst(ty_op.operand);
5160 const va_list_ty = self.air.getRefType(ty_op.ty);
5161 const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);
5162
5163 const target = self.dg.module.getTarget();
5164 const result_alignment = va_list_ty.abiAlignment(target);
5165 const dest_list = self.buildAlloca(llvm_va_list_ty, result_alignment);
5166
5167 const llvm_fn_name = "llvm.va_copy";
5168 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
5169 const param_types = [_]*llvm.Type{
5170 self.dg.context.intType(8).pointerType(0),
5171 self.dg.context.intType(8).pointerType(0),
5172 };
5173 const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False);
5174 break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
5175 };
5176
5177 const args: [2]*llvm.Value = .{ dest_list, src_list };
5178 _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, "");
5179
5180 if (isByRef(va_list_ty)) {
5181 return dest_list;
5182 } else {
5183 const loaded = self.builder.buildLoad(llvm_va_list_ty, dest_list, "");
5184 loaded.setAlignment(result_alignment);
5185 return loaded;
5186 }
5187 }
5188
5189 fn airCVaEnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5190 const un_op = self.air.instructions.items(.data)[inst].un_op;
5191 const list = try self.resolveInst(un_op);
5192
5193 const llvm_fn_name = "llvm.va_end";
5194 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
5195 const param_types = [_]*llvm.Type{self.dg.context.intType(8).pointerType(0)};
5196 const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False);
5197 break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
5198 };
5199 const args: [1]*llvm.Value = .{list};
5200 _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, "");
5201 return null;
5202 }
5203
5204 fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5205 if (self.liveness.isUnused(inst)) return null;
5206
5207 const va_list_ty = self.air.typeOfIndex(inst);
5208 const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);
5209
5210 const target = self.dg.module.getTarget();
5211 const result_alignment = va_list_ty.abiAlignment(target);
5212 const list = self.buildAlloca(llvm_va_list_ty, result_alignment);
5213
5214 const llvm_fn_name = "llvm.va_start";
5215 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
5216 const param_types = [_]*llvm.Type{self.dg.context.intType(8).pointerType(0)};
5217 const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False);
5218 break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
5219 };
5220 const args: [1]*llvm.Value = .{list};
5221 _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, "");
5222
5223 if (isByRef(va_list_ty)) {
5224 return list;
5225 } else {
5226 const loaded = self.builder.buildLoad(llvm_va_list_ty, list, "");
5227 loaded.setAlignment(result_alignment);
5228 return loaded;
5229 }
5230 }
5231
51395232 fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !?*llvm.Value {
51405233 if (self.liveness.isUnused(inst)) return null;
51415234 self.builder.setFastMath(want_fast_math);
src/codegen/llvm/bindings.zig+3
......@@ -965,6 +965,9 @@ pub const Builder = opaque {
965965
966966 pub const buildAllocaInAddressSpace = ZigLLVMBuildAllocaInAddressSpace;
967967 extern fn ZigLLVMBuildAllocaInAddressSpace(B: *Builder, Ty: *Type, AddressSpace: c_uint, Name: [*:0]const u8) *Value;
968
969 pub const buildVAArg = LLVMBuildVAArg;
970 extern fn LLVMBuildVAArg(*Builder, List: *Value, Ty: *Type, Name: [*:0]const u8) *Value;
968971};
969972
970973pub const MDString = opaque {
src/link/MachO/load_commands.zig+1-1
......@@ -36,7 +36,7 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
3636 // LC_DYLD_INFO_ONLY
3737 sizeofcmds += @sizeOf(macho.dyld_info_command);
3838 // LC_FUNCTION_STARTS
39 if (has_text_segment and ctx.wants_function_starts) |_| {
39 if (has_text_segment and ctx.wants_function_starts) {
4040 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4141 }
4242 // LC_DATA_IN_CODE
src/print_air.zig+4
......@@ -191,6 +191,7 @@ const Writer = struct {
191191 .neg_optimized,
192192 .cmp_lt_errors_len,
193193 .set_err_return_trace,
194 .c_va_end,
194195 => try w.writeUnOp(s, inst),
195196
196197 .breakpoint,
......@@ -205,6 +206,7 @@ const Writer = struct {
205206 .ret_ptr,
206207 .arg,
207208 .err_return_trace,
209 .c_va_start,
208210 => try w.writeTy(s, inst),
209211
210212 .not,
......@@ -246,6 +248,8 @@ const Writer = struct {
246248 .bit_reverse,
247249 .error_set_has_value,
248250 .addrspace_cast,
251 .c_va_arg,
252 .c_va_copy,
249253 => try w.writeTyOp(s, inst),
250254
251255 .block,
src/print_zir.zig+4
......@@ -465,6 +465,7 @@ const Writer = struct {
465465 .frame,
466466 .frame_address,
467467 .breakpoint,
468 .c_va_start,
468469 => try self.writeExtNode(stream, extended),
469470
470471 .builtin_src => {
......@@ -504,6 +505,8 @@ const Writer = struct {
504505 .error_to_int,
505506 .int_to_error,
506507 .reify,
508 .c_va_copy,
509 .c_va_end,
507510 => {
508511 const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
509512 const src = LazySrcLoc.nodeOffset(inst_data.node);
......@@ -518,6 +521,7 @@ const Writer = struct {
518521 .wasm_memory_grow,
519522 .prefetch,
520523 .addrspace_cast,
524 .c_va_arg,
521525 => {
522526 const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data;
523527 const src = LazySrcLoc.nodeOffset(inst_data.node);
src/type.zig+8-2
......@@ -3489,7 +3489,10 @@ pub const Type = extern union {
34893489 return AbiSizeAdvanced{ .scalar = 0 };
34903490 }
34913491
3492 if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };
3492 if (!(child_type.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
3493 error.NeedLazy => return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(strat.lazy, ty) },
3494 else => |e| return e,
3495 })) return AbiSizeAdvanced{ .scalar = 1 };
34933496
34943497 if (ty.optionalReprIsPayload()) {
34953498 return abiSizeAdvanced(child_type, target, strat);
......@@ -3518,7 +3521,10 @@ pub const Type = extern union {
35183521 // in abiAlignmentAdvanced.
35193522 const data = ty.castTag(.error_union).?.data;
35203523 const code_size = abiSize(Type.anyerror, target);
3521 if (!data.payload.hasRuntimeBits()) {
3524 if (!(data.payload.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
3525 error.NeedLazy => return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(strat.lazy, ty) },
3526 else => |e| return e,
3527 })) {
35223528 // Same as anyerror.
35233529 return AbiSizeAdvanced{ .scalar = code_size };
35243530 }
test/behavior/sizeof_and_typeof.zig+5
......@@ -288,3 +288,8 @@ test "runtime instructions inside typeof in comptime only scope" {
288288 try expect(@TypeOf((T{}).b) == i8);
289289 }
290290}
291
292test "@sizeOf optional of previously unresolved union" {
293 const Node = union { a: usize };
294 try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node));
295}
test/behavior/var_args.zig+108-1
......@@ -1,5 +1,6 @@
11const builtin = @import("builtin");
2const expect = @import("std").testing.expect;
2const std = @import("std");
3const expect = std.testing.expect;
34
45fn add(args: anytype) i32 {
56 var sum = @as(i32, 0);
......@@ -91,3 +92,109 @@ test "pass zero length array to var args param" {
9192fn doNothingWithFirstArg(args: anytype) void {
9293 _ = args[0];
9394}
95
96test "simple variadic function" {
97 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
100 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
101 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
102 if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO
103 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
104
105 const S = struct {
106 fn simple(...) callconv(.C) c_int {
107 var ap = @cVaStart();
108 defer @cVaEnd(&ap);
109 return @cVaArg(&ap, c_int);
110 }
111
112 fn add(count: c_int, ...) callconv(.C) c_int {
113 var ap = @cVaStart();
114 defer @cVaEnd(&ap);
115 var i: usize = 0;
116 var sum: c_int = 0;
117 while (i < count) : (i += 1) {
118 sum += @cVaArg(&ap, c_int);
119 }
120 return sum;
121 }
122 };
123
124 try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0)));
125 try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024)));
126 try std.testing.expectEqual(@as(c_int, 0), S.add(0));
127 try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1)));
128 try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2)));
129}
130
131test "variadic functions" {
132 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
134 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
135 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
136 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
137 if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO
138 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
139
140 const S = struct {
141 fn printf(list_ptr: *std.ArrayList(u8), format: [*:0]const u8, ...) callconv(.C) void {
142 var ap = @cVaStart();
143 defer @cVaEnd(&ap);
144 vprintf(list_ptr, format, &ap);
145 }
146
147 fn vprintf(
148 list: *std.ArrayList(u8),
149 format: [*:0]const u8,
150 ap: *std.builtin.VaList,
151 ) callconv(.C) void {
152 for (std.mem.span(format)) |c| switch (c) {
153 's' => {
154 const arg = @cVaArg(ap, [*:0]const u8);
155 list.writer().print("{s}", .{arg}) catch return;
156 },
157 'd' => {
158 const arg = @cVaArg(ap, c_int);
159 list.writer().print("{d}", .{arg}) catch return;
160 },
161 else => unreachable,
162 };
163 }
164 };
165
166 var list = std.ArrayList(u8).init(std.testing.allocator);
167 defer list.deinit();
168 S.printf(&list, "dsd", @as(c_int, 1), @as([*:0]const u8, "hello"), @as(c_int, 5));
169 try std.testing.expectEqualStrings("1hello5", list.items);
170}
171
172test "copy VaList" {
173 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
174 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
175 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
176 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
177 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
178 if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO
179 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
180
181 const S = struct {
182 fn add(count: c_int, ...) callconv(.C) c_int {
183 var ap = @cVaStart();
184 defer @cVaEnd(&ap);
185 var copy = @cVaCopy(&ap);
186 defer @cVaEnd(&copy);
187 var i: usize = 0;
188 var sum: c_int = 0;
189 while (i < count) : (i += 1) {
190 sum += @cVaArg(&ap, c_int);
191 sum += @cVaArg(&copy, c_int) * 2;
192 }
193 return sum;
194 }
195 };
196
197 try std.testing.expectEqual(@as(c_int, 0), S.add(0));
198 try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));
199 try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));
200}
test/cases/compile_errors/invalid_capture_type.zig created+24
......@@ -0,0 +1,24 @@
1export fn f1() void {
2 if (true) |x| { _ = x; }
3}
4export fn f2() void {
5 if (@as(usize, 5)) |_| {}
6}
7export fn f3() void {
8 if (@as(usize, 5)) |_| {} else |_| {}
9}
10export fn f4() void {
11 if (null) |_| {}
12}
13export fn f5() void {
14 if (error.Foo) |_| {} else |_| {}
15}
16
17// error
18// backend=stage2
19// target=native
20//
21// :2:9: error: expected optional type, found 'bool'
22// :5:9: error: expected optional type, found 'usize'
23// :8:9: error: expected error union type, found 'usize'
24// :14:9: error: expected error union type, found 'error{Foo}'
test/cases/compile_errors/invalid_variadic_function.zig created+12
......@@ -0,0 +1,12 @@
1fn foo(...) void {}
2fn bar(a: anytype, ...) callconv(a) void {}
3
4comptime { _ = foo; }
5comptime { _ = bar; }
6
7// error
8// backend=stage2
9// target=native
10//
11// :1:1: error: variadic function must have 'C' calling convention
12// :2:1: error: generic function cannot be variadic
test/cases/compile_errors/stage1/obj/invalid_maybe_type.zig deleted-9
......@@ -1,9 +0,0 @@
1export fn f() void {
2 if (true) |x| { _ = x; }
3}
4
5// error
6// backend=stage1
7// target=native
8//
9// tmp.zig:2:9: error: expected optional type, found 'bool'