diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 8b9061df2..3b802952c 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -58,7 +58,7 @@ Nix version: 2.17 # Writing a Nix language plug-in In this example we add a custom primitive operation (*primop*) to `builtins`. -It will increment the argument if it is an integer and return `null` otherwise. +It will increment the argument if it is an integer and throw an error otherwise. **plugin.c:** ```C @@ -66,18 +66,18 @@ It will increment the argument if it is an integer and return `null` otherwise. #include #include -void increment(State* state, int pos, Value** args, Value* v) { +void increment(void* user_data, nix_c_context* ctx, State* state, Value** args, Value* v) { nix_value_force(NULL, state, args[0]); if (nix_get_type(NULL, args[0]) == NIX_TYPE_INT) { nix_set_int(NULL, v, nix_get_int(NULL, args[0]) + 1); } else { - nix_set_null(NULL, v); + nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "First argument should be an integer."); } } void nix_plugin_entry() { const char* args[] = {"n", NULL}; - PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example custom built-in function: increments an integer"); + PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example custom built-in function: increments an integer", NULL); nix_register_primop(NULL, p); nix_gc_decref(NULL, p); } diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index 9a313d8fd..8b73729a5 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -42,7 +42,12 @@ static nix::Value & check_value_not_null(Value * value) static void nix_c_primop_wrapper( PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) { - f(userdata, (State *) &state, *reinterpret_cast(&pos), (Value **) args, (Value *) &v); + nix_c_context ctx; + f(userdata, &ctx, (State *) &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.debugThrowLastTrace(nix::Error( + {.msg = nix::hintfmt("Error from builtin function: %s", *ctx.last_err), .errPos = state.positions[pos]})); } PrimOp * nix_alloc_primop( diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index ffba4c097..ca4e83cf4 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -65,15 +65,17 @@ typedef struct ExternalValue ExternalValue; * @{ */ /** @brief Function pointer for primops - * @param[in] user_data Arbitrary data, passed to nix_alloc_primop and stored. + * When you want to return an error, call nix_set_err_msg(context, NIX_ERR_UNKNOWN, "your error message here"). + * + * @param[in] user_data Arbitrary data that was initially supplied to nix_alloc_primop + * @param[out] context Stores error information. * @param[in] state Evaluator state - * @param[in] pos Call position, opaque index into the state's position table. * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before * use. * @param[out] ret return value * @see nix_alloc_primop, nix_set_primop */ -typedef void (*PrimOpFun)(void * user_data, State * state, int pos, Value ** args, Value * ret); +typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, State * state, Value ** args, Value * ret); /** @brief Allocate a PrimOp * @@ -86,7 +88,7 @@ typedef void (*PrimOpFun)(void * user_data, State * state, int pos, Value ** arg * @param[in] name function name * @param[in] args array of argument names, NULL-terminated * @param[in] doc optional, documentation for this primop - * @param[in] user_data optional, arbitrary data, passed to the function when it's called + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called * @return primop, or null in case of errors * @see nix_set_primop */ diff --git a/src/libutil/c/nix_api_util.h b/src/libutil/c/nix_api_util.h index de029ba10..c288654fd 100644 --- a/src/libutil/c/nix_api_util.h +++ b/src/libutil/c/nix_api_util.h @@ -263,6 +263,20 @@ nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context */ nix_err nix_err_code(const nix_c_context * read_context); +/** + * @brief Set an error message on a nix context. + * + * This should be used when you want to throw an error from a PrimOp callback. + * + * All other use is internal to the API. + * + * @param context context to write the error message to, or NULL + * @param err The error code to set and return + * @param msg The error message to set. + * @returns the error code set + */ +nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg); + /** * @} */ diff --git a/src/libutil/c/nix_api_util_internal.h b/src/libutil/c/nix_api_util_internal.h index 1aaf328c1..53c260e35 100644 --- a/src/libutil/c/nix_api_util_internal.h +++ b/src/libutil/c/nix_api_util_internal.h @@ -17,18 +17,6 @@ struct nix_c_context nix_err nix_context_error(nix_c_context * context); -/** - * Internal use only. - * - * Sets the most recent error message. - * - * @param context context to write the error message to, or NULL - * @param err The error code to set and return - * @param msg The error message to set. - * @returns the error code set - */ -nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg); - /** * Internal use only. *