Files
lix/src/libexpr/primops/control.cc
T
Tom Hubrecht 4ea0f70b8b primops: Explicit registering of primops
Change-Id: I05e72d2bfdc715d9e27cc672ac35619310b0864d
2024-05-30 12:57:17 +02:00

150 lines
4.2 KiB
C++

#include "eval-settings.hh"
#include "primops.hh"
namespace nix {
/**
* builtins.abort
*/
static void prim_abort(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
NixStringContext context;
auto s = state
.coerceToString(
pos,
*args[0],
context,
"while evaluating the error message passed to builtins.abort"
)
.toOwned();
state.error<Abort>("evaluation aborted with the following error message: '%1%'", s)
.debugThrow();
}
PrimOp primop_abort({
.name = "abort",
.args = {"s"},
.doc = R"(
Abort Nix expression evaluation and print the error message *s*.
)",
.fun = prim_abort,
});
/**
* builtins.break
*/
static void prim_break(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
if (state.debugRepl && !state.debugTraces.empty()) {
auto error = Error(ErrorInfo{
.level = lvlInfo,
.msg = HintFmt("breakpoint reached"),
.pos = state.positions[pos],
});
auto & dt = state.debugTraces.front();
state.runDebugRepl(&error, dt.env, dt.expr);
}
// Return the value we were passed.
v = *args[0];
}
PrimOp primop_break({
.name = "break",
.args = {"v"},
.doc = R"(
In debug mode (enabled using `--debugger`), pause Nix expression evaluation and enter the REPL.
Otherwise, return the argument `v`.
)",
.fun = prim_break,
});
/**
* builtins.throw
*/
static void prim_throw(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
NixStringContext context;
auto s =
state
.coerceToString(
pos, *args[0], context, "while evaluating the error message passed to builtin.throw"
)
.toOwned();
state.error<ThrownError>(s).debugThrow();
}
PrimOp primop_throw({
.name = "throw",
.args = {"s"},
.doc = R"(
Throw an error message *s*. This usually aborts Nix expression
evaluation, but in `nix-env -qa` and other commands that try to
evaluate a set of derivations to get information about those
derivations, a derivation that throws an error is silently skipped
(which is not the case for `abort`).
)",
.fun = prim_throw,
});
/**
* builtins.tryEval
*/
static void prim_tryEval(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
auto attrs = state.buildBindings(2);
/* increment state.trylevel, and decrement it when this function returns. */
MaintainCount trylevel(state.trylevel);
ReplExitStatus (*savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry) {
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
savedDebugRepl = state.debugRepl;
state.debugRepl = nullptr;
}
try {
state.forceValue(*args[0], pos);
attrs.insert(state.sValue, args[0]);
attrs.alloc("success").mkBool(true);
} catch (AssertionError & e) {
attrs.alloc(state.sValue).mkBool(false);
attrs.alloc("success").mkBool(false);
}
// restore the debugRepl pointer if we saved it earlier.
if (savedDebugRepl) {
state.debugRepl = savedDebugRepl;
}
v.mkAttrs(attrs);
}
PrimOp primop_tryEval({
.name = "__tryEval",
.args = {"e"},
.doc = R"(
Try to shallowly evaluate *e*. Return a set containing the
attributes `success` (`true` if *e* evaluated successfully,
`false` if an error was thrown) and `value`, equalling *e* if
successful and `false` otherwise. `tryEval` will only prevent
errors created by `throw` or `assert` from being thrown.
Errors `tryEval` will not catch are for example those created
by `abort` and type errors generated by builtins. Also note that
this doesn't evaluate *e* deeply, so `let e = { x = throw ""; };
in (builtins.tryEval e).success` will be `true`. Using
`builtins.deepSeq` one can get the expected result:
`let e = { x = throw ""; }; in
(builtins.tryEval (builtins.deepSeq e e)).success` will be
`false`.
)",
.fun = prim_tryEval,
});
}