<% needs_make? = Enum.any?(@type.params, &Type.needs_make?/1) %>

fn <%= @type.name %>(env: beam.env, argc: c_int, args: [*c] const e.ErlNifTerm) callconv(.C) e.ErlNifTerm {
    beam.context = .<%= context(@concurrency) %>;
    <%= if needs_make? do %>
    var error_info = beam.make_empty_list(env); 
    <% end %>
    <%# TODO: set up a proper allocator variable. %>
    <%= if @leak_check do %>
    var gpa = beam.make_general_purpose_allocator_instance();
    // set the threadlocal beam.allocator
    beam.allocator = gpa.allocator();
    <% else %>
    beam.allocator = beam.raw_allocator;
    <% end %>

    const outer_result = get_result: {
        <%= for {param_type, index} <- Nif.indexed_parameters(@type.params), Type.missing_size?(param_type) do %>
        var size<%= index %>:usize = undefined;
        <% end %>

        const payload_opts = .{
            <%= for {param_type, index} <- Nif.indexed_parameters(@type.params) do %>
            .{
                <%= if needs_make? do %>.error_info = &error_info,<% end %>
                <%= if Type.missing_size?(param_type) do%>.size = &size<%= index %>,<% end %>
                <%= if match?(%Zig.Type.Resource{}, param_type) do%>.root = @This() <% end %>
            },
            <% end %>
        };

        var error_index: u8 = undefined;

        const payload = beam.payload.build(nif.<%= @type.name %>, env, argc, args, &error_index, payload_opts) catch |err| {
            <%= if needs_make? do %>
            return e.enif_raise_exception(env, beam.make(env, .{err, error_index, error_info}, .{}).v);
            <% else %>
            return e.enif_raise_exception(env, beam.make(env, .{err, error_index, beam.make_empty_list(env)}, .{}).v);
            <% end %>
        };

        const cleanup_opts = .{
            <%= for {param_type, index} <- Nif.indexed_parameters(@type.params), do: cleanup_for(@args, param_type, index) %>
        };

        defer beam.payload.cleanup(payload, cleanup_opts);

        const result = @call(.{}, nif.<%= @type.name %>, payload) <%= Nif.maybe_catch(@type.return) %>;

        <%= Type.get_result(@type.return, @return) %>
    };

    <%= if @leak_check do %>
    // NB: confusing!  True if leaks, False if doesn't leak.
    return if (gpa.deinit()) beam.raise_elixir_exception(env, "RuntimeError", .{.message = "memory leak detected in function `<%= @type.name %>/<%= @type.arity %>`"}).v else outer_result;
    
    // NB: this changes to the following code in 0.11: 
    //switch (gpa.deinit()) {
    //    .ok => 
    //        return outer_result,
    //    .leak =>
    //        return beam.raise_elixir_exception(env, "RuntimeError", .{.message = "memory leak detected in function `<%= @type.name %>`"}).v
    //}
    <% else %>
    return outer_result;
    <% end %>
}