authorgravatar for r00ster91@proton.meWooster <r00ster91@proton.me> 2023-07-08 18:05:03-04:00
committergravatar for r00ster91@proton.meWooster <r00ster91@proton.me> 2023-07-08 18:05:03-04:00
log026c63d8fe5caa30d003230b5514d8a3e274f82c
tree3d127b588a4a1e92337e75d432d754f8e8ccad1b
parent9be1a3f7ef9177fd1d495ae1f85d53693f6c6dcf

Sema: infrastructure for supporting more than .C callconv for variadic functions

Now you can add new calling conventions that you confirmed to work with variadic functions simply in a single place and the rest will work automatically.

3 files changed, 49 insertions(+), 8 deletions(-)

src/Sema.zig+39-6
...@@ -6628,7 +6628,7 @@ fn checkCallArgumentCount(...@@ -6628,7 +6628,7 @@ fn checkCallArgumentCount(
6628 const fn_params_len = func_ty_info.param_types.len;6628 const fn_params_len = func_ty_info.param_types.len;
6629 const args_len = total_args - @intFromBool(member_fn);6629 const args_len = total_args - @intFromBool(member_fn);
6630 if (func_ty_info.is_var_args) {6630 if (func_ty_info.is_var_args) {
6631 assert(func_ty_info.cc == .C);6631 assert(callConvSupportsVarArgs(func_ty_info.cc));
6632 if (total_args >= fn_params_len) return func_ty;6632 if (total_args >= fn_params_len) return func_ty;
6633 } else if (fn_params_len == total_args) {6633 } else if (fn_params_len == total_args) {
6634 return func_ty;6634 return func_ty;
...@@ -8917,6 +8917,41 @@ fn handleExternLibName(...@@ -8917,6 +8917,41 @@ fn handleExternLibName(
8917 return sema.gpa.dupeZ(u8, lib_name);8917 return sema.gpa.dupeZ(u8, lib_name);
8918}8918}
89198919
8920/// These are calling conventions that are confirmed to work with variadic functions.
8921/// Any calling conventions not included here are either not yet verified to work with variadic
8922/// functions or there are no more other calling conventions that support variadic functions.
8923const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention{
8924 .C,
8925};
8926fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention) bool {
8927 return for (calling_conventions_supporting_var_args) |supported_cc| {
8928 if (cc == supported_cc) return true;
8929 } else false;
8930}
8931fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention) CompileError!void {
8932 const CallingConventionsSupportingVarArgsList = struct {
8933 pub fn format(_: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
8934 _ = fmt;
8935 _ = options;
8936 for (calling_conventions_supporting_var_args, 0..) |cc_inner, i| {
8937 if (i != 0)
8938 try writer.writeAll(", ");
8939 try writer.print("'.{s}'", .{@tagName(cc_inner)});
8940 }
8941 }
8942 };
8943
8944 if (!callConvSupportsVarArgs(cc)) {
8945 const msg = msg: {
8946 const msg = try sema.errMsg(block, src, "variadic function does not support '.{s}' calling convention", .{@tagName(cc)});
8947 errdefer msg.destroy(sema.gpa);
8948 try sema.errNote(block, src, msg, "supported calling conventions: {}", .{CallingConventionsSupportingVarArgsList{}});
8949 break :msg msg;
8950 };
8951 return sema.failWithOwnedErrorMsg(msg);
8952 }
8953}
8954
8920const FuncLinkSection = union(enum) {8955const FuncLinkSection = union(enum) {
8921 generic,8956 generic,
8922 default,8957 default,
...@@ -8963,9 +8998,7 @@ fn funcCommon(...@@ -8963,9 +8998,7 @@ fn funcCommon(
8963 if (is_generic) {8998 if (is_generic) {
8964 return sema.fail(block, func_src, "generic function cannot be variadic", .{});8999 return sema.fail(block, func_src, "generic function cannot be variadic", .{});
8965 }9000 }
8966 if (cc.? != .C) {9001 try sema.checkCallConvSupportsVarArgs(block, cc_src, cc.?);
8967 return sema.fail(block, cc_src, "variadic function must have 'C' calling convention", .{});
8968 }
8969 }9002 }
89709003
8971 var destroy_fn_on_error = false;9004 var destroy_fn_on_error = false;
...@@ -20325,8 +20358,8 @@ fn zirReify(...@@ -20325,8 +20358,8 @@ fn zirReify(
2032520358
20326 const is_var_args = is_var_args_val.toBool();20359 const is_var_args = is_var_args_val.toBool();
20327 const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val);20360 const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val);
20328 if (is_var_args and cc != .C) {20361 if (is_var_args) {
20329 return sema.fail(block, src, "varargs functions must have C calling convention", .{});20362 try sema.checkCallConvSupportsVarArgs(block, src, cc);
20330 }20363 }
2033120364
20332 const alignment = alignment: {20365 const alignment = alignment: {
test/cases/compile_errors/invalid_variadic_function.zig+8-1
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1fn foo(...) void {}1fn foo(...) void {}
2fn bar(a: anytype, ...) callconv(a) void {}2fn bar(a: anytype, ...) callconv(a) void {}
3inline fn foo2(...) void {}
34
4comptime {5comptime {
5 _ = foo;6 _ = foo;
...@@ -7,10 +8,16 @@ comptime {...@@ -7,10 +8,16 @@ comptime {
7comptime {8comptime {
8 _ = bar;9 _ = bar;
9}10}
11comptime {
12 _ = foo2;
13}
1014
11// error15// error
12// backend=stage216// backend=stage2
13// target=native17// target=native
14//18//
15// :1:1: error: variadic function must have 'C' calling convention19// :1:1: error: variadic function does not support '.Unspecified' calling convention
20// :1:1: note: supported calling conventions: '.C'
16// :2:1: error: generic function cannot be variadic21// :2:1: error: generic function cannot be variadic
22// :1:1: error: variadic function does not support '.Inline' calling convention
23// :1:1: note: supported calling conventions: '.C'
test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig+2-1
...@@ -16,4 +16,5 @@ comptime {...@@ -16,4 +16,5 @@ comptime {
16// backend=stage216// backend=stage2
17// target=native17// target=native
18//18//
19// :1:13: error: varargs functions must have C calling convention19// :1:13: error: variadic function does not support '.Unspecified' calling convention
20// :1:13: note: supported calling conventions: '.C'