mirror of
https://github.com/NixOS/nix
synced 2025-07-07 06:01:48 +02:00
Merge pull request #10767 from hercules-ci/fix-c-api-primop-for-strict-initializers
C API: Fix custom primops
This commit is contained in:
commit
c90a763273
4 changed files with 297 additions and 4 deletions
|
@ -65,6 +65,17 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, V
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
state->state.callFunction(*(nix::Value *) fn, nargs, (nix::Value * *)args, *(nix::Value *) value, nix::noPos);
|
||||
state->state.forceValue(*(nix::Value *) value, nix::noPos);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value)
|
||||
{
|
||||
if (context)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "nix_api_store.h"
|
||||
#include "nix_api_util.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -80,6 +81,46 @@ nix_err nix_expr_eval_from_string(
|
|||
*/
|
||||
nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value);
|
||||
|
||||
/**
|
||||
* @brief Calls a Nix function with multiple arguments.
|
||||
*
|
||||
* Technically these are functions that return functions. It is common for Nix
|
||||
* functions to be curried, so this function is useful for calling them.
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] state The state of the evaluation.
|
||||
* @param[in] fn The Nix function to call.
|
||||
* @param[in] nargs The number of arguments.
|
||||
* @param[in] args The arguments to pass to the function.
|
||||
* @param[out] value The result of the function call.
|
||||
*
|
||||
* @see nix_value_call For the single argument primitive.
|
||||
* @see NIX_VALUE_CALL For a macro that wraps this function for convenience.
|
||||
*/
|
||||
nix_err nix_value_call_multi(
|
||||
nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value);
|
||||
|
||||
/**
|
||||
* @brief Calls a Nix function with multiple arguments.
|
||||
*
|
||||
* Technically these are functions that return functions. It is common for Nix
|
||||
* functions to be curried, so this function is useful for calling them.
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] state The state of the evaluation.
|
||||
* @param[out] value The result of the function call.
|
||||
* @param[in] fn The Nix function to call.
|
||||
* @param[in] args The arguments to pass to the function.
|
||||
*
|
||||
* @see nix_value_call_multi
|
||||
*/
|
||||
#define NIX_VALUE_CALL(context, state, value, fn, ...) \
|
||||
do { \
|
||||
Value * args_array[] = {__VA_ARGS__}; \
|
||||
size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \
|
||||
nix_value_call_multi(context, state, fn, nargs, args_array, value); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief Forces the evaluation of a Nix value.
|
||||
*
|
||||
|
|
|
@ -73,10 +73,43 @@ static void nix_c_primop_wrapper(
|
|||
PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v)
|
||||
{
|
||||
nix_c_context ctx;
|
||||
f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &v);
|
||||
/* TODO: In the future, this should throw different errors depending on the error code */
|
||||
if (ctx.last_err_code != NIX_OK)
|
||||
state.error<nix::EvalError>("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow();
|
||||
|
||||
// v currently has a thunk, but the C API initializers require an uninitialized value.
|
||||
//
|
||||
// We can't destroy the thunk, because that makes it impossible to retry,
|
||||
// which is needed for tryEval and for evaluation drivers that evaluate more
|
||||
// than one value (e.g. an attrset with two derivations, both of which
|
||||
// reference v).
|
||||
//
|
||||
// Instead we create a temporary value, and then assign the result to v.
|
||||
// This does not give the primop definition access to the thunk, but that's
|
||||
// ok because we don't see a need for this yet (e.g. inspecting thunks,
|
||||
// or maybe something to make blackholes work better; we don't know).
|
||||
nix::Value vTmp;
|
||||
|
||||
f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &vTmp);
|
||||
|
||||
if (ctx.last_err_code != NIX_OK) {
|
||||
/* TODO: Throw different errors depending on the error code */
|
||||
state.error<nix::EvalError>("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow();
|
||||
}
|
||||
|
||||
if (!vTmp.isValid()) {
|
||||
state.error<nix::EvalError>("Implementation error in custom function: return value was not initialized")
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
if (vTmp.type() == nix::nThunk) {
|
||||
// We might allow this in the future if it makes sense for the evaluator
|
||||
// e.g. implementing tail recursion by returning a thunk to the next
|
||||
// "iteration". Until then, this is most likely a mistake or misunderstanding.
|
||||
state.error<nix::EvalError>("Implementation error in custom function: return value must not be a thunk")
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
v = vTmp;
|
||||
}
|
||||
|
||||
PrimOp * nix_alloc_primop(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue