Closed
Description
This is a proposal to add a builtin function that returns a tuple type.
Proposed signature:
fn @Tuple([]type) type
This would be a useful feature for metaprogramming purposes.
More specifically, it would be helpful in metaprograms that generate functions that forward arguments using @call
, as it could be used to generate non-generic function pointers that accept tuple types calculated at comptime.
Currently, passing a tuple requires the argument to be 'var' and forces the function to be generic.
Here is an example usecase from the std.interface PR:
// Here, we are generating a type erased version of a method
const args = @typeInfo(fn_decl.fn_type).Fn.args;
// [...] Omitted code, compute our_cc, Return, CurrSelfType, is_const
// Actual function generation
// Currently, there is no way to avoid the following switch statement.
return switch (args.len) {
1 => struct {
fn impl(self_ptr: CurrSelfType) Return {
const self = if (is_const) constSelfPtrAs(self_ptr, ImplT) else selfPtrAs(self_ptr, ImplT);
const f = @field(self, name);
return @call(if (our_cc == .Async) .{ .modifier = .async_kw } else .{ .modifier = .always_inline }, f, .{});
}
}.impl,
2 => struct {
fn impl(self_ptr: CurrSelfType, arg: args[1].arg_type.?) Return {
const self = if (is_const) constSelfPtrAs(self_ptr, ImplT) else selfPtrAs(self_ptr, ImplT);
const f = @field(self, name);
return @call(if (our_cc == .Async) .{ .modifier = .async_kw } else .{ .modifier = .always_inline }, f, .{arg});
}
}.impl,
// [...] Omitted 3 to 6
else => @compileError("Unsupported number of arguments, please provide a manually written vtable."),
};
// With this proposal
var arg_type_arr: [args.len - 1]type = undefined;
for (args[1..]) |arg, i| {
arg_type_arr[i] = arg.arg_type.?;
}
const ArgPackType = @Tuple(arg_type_arr[0..]);
// Call site of generated function also changes from @call(..., .{self, a1,..., aN}) to @call(..., .{self, .{a1, ..., aN} });
return struct {
fn impl(self_ptr: CurrSelfType, args: ArgPackType) Return {
const self = if (is_const) constSelfPtrAs(self_ptr, ImplT) else selfPtrAs(self_ptr, ImplT);
const f = @field(self, name);
return @call(if (our_cc == .Async) .{ .modifier = .async_kw } else .{ .modifier = .always_inline }, f, args);
}
}.impl;