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.
This commit is contained in:
r00ster91 2023-07-08 18:05:03 -04:00
parent 9be1a3f7ef
commit 026c63d8fe
3 changed files with 49 additions and 8 deletions

View File

@ -6628,7 +6628,7 @@ fn checkCallArgumentCount(
const fn_params_len = func_ty_info.param_types.len; const fn_params_len = func_ty_info.param_types.len;
const args_len = total_args - @intFromBool(member_fn); const args_len = total_args - @intFromBool(member_fn);
if (func_ty_info.is_var_args) { if (func_ty_info.is_var_args) {
assert(func_ty_info.cc == .C); assert(callConvSupportsVarArgs(func_ty_info.cc));
if (total_args >= fn_params_len) return func_ty; if (total_args >= fn_params_len) return func_ty;
} else if (fn_params_len == total_args) { } else if (fn_params_len == total_args) {
return func_ty; return func_ty;
@ -8917,6 +8917,41 @@ fn handleExternLibName(
return sema.gpa.dupeZ(u8, lib_name); return sema.gpa.dupeZ(u8, lib_name);
} }
/// These are calling conventions that are confirmed to work with variadic functions.
/// Any calling conventions not included here are either not yet verified to work with variadic
/// functions or there are no more other calling conventions that support variadic functions.
const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention{
.C,
};
fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention) bool {
return for (calling_conventions_supporting_var_args) |supported_cc| {
if (cc == supported_cc) return true;
} else false;
}
fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention) CompileError!void {
const CallingConventionsSupportingVarArgsList = struct {
pub fn format(_: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
_ = fmt;
_ = options;
for (calling_conventions_supporting_var_args, 0..) |cc_inner, i| {
if (i != 0)
try writer.writeAll(", ");
try writer.print("'.{s}'", .{@tagName(cc_inner)});
}
}
};
if (!callConvSupportsVarArgs(cc)) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "variadic function does not support '.{s}' calling convention", .{@tagName(cc)});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, src, msg, "supported calling conventions: {}", .{CallingConventionsSupportingVarArgsList{}});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
}
}
const FuncLinkSection = union(enum) { const FuncLinkSection = union(enum) {
generic, generic,
default, default,
@ -8963,9 +8998,7 @@ fn funcCommon(
if (is_generic) { if (is_generic) {
return sema.fail(block, func_src, "generic function cannot be variadic", .{}); return sema.fail(block, func_src, "generic function cannot be variadic", .{});
} }
if (cc.? != .C) { try sema.checkCallConvSupportsVarArgs(block, cc_src, cc.?);
return sema.fail(block, cc_src, "variadic function must have 'C' calling convention", .{});
}
} }
var destroy_fn_on_error = false; var destroy_fn_on_error = false;
@ -20325,8 +20358,8 @@ fn zirReify(
const is_var_args = is_var_args_val.toBool(); const is_var_args = is_var_args_val.toBool();
const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val); const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val);
if (is_var_args and cc != .C) { if (is_var_args) {
return sema.fail(block, src, "varargs functions must have C calling convention", .{}); try sema.checkCallConvSupportsVarArgs(block, src, cc);
} }
const alignment = alignment: { const alignment = alignment: {

View File

@ -1,5 +1,6 @@
fn foo(...) void {} fn foo(...) void {}
fn bar(a: anytype, ...) callconv(a) void {} fn bar(a: anytype, ...) callconv(a) void {}
inline fn foo2(...) void {}
comptime { comptime {
_ = foo; _ = foo;
@ -7,10 +8,16 @@ comptime {
comptime { comptime {
_ = bar; _ = bar;
} }
comptime {
_ = foo2;
}
// error // error
// backend=stage2 // backend=stage2
// target=native // target=native
// //
// :1:1: error: variadic function must have 'C' calling convention // :1:1: error: variadic function does not support '.Unspecified' calling convention
// :1:1: note: supported calling conventions: '.C'
// :2:1: error: generic function cannot be variadic // :2:1: error: generic function cannot be variadic
// :1:1: error: variadic function does not support '.Inline' calling convention
// :1:1: note: supported calling conventions: '.C'

View File

@ -16,4 +16,5 @@ comptime {
// backend=stage2 // backend=stage2
// target=native // target=native
// //
// :1:13: error: varargs functions must have C calling convention // :1:13: error: variadic function does not support '.Unspecified' calling convention
// :1:13: note: supported calling conventions: '.C'