#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("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(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 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, }); }