Skip to content

Add @Tuple #4607

Closed
Closed
@alexnask

Description

@alexnask

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;

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions