libexpr: mark eval error handling NeverAsync
technically it doesn't *have* to be NeverAsync, but not marking it as such unconditionally requires templating DebugState over asyncness of its callback (which then requires templating EvalState, which, *NO*.) Change-Id: I4980d45b541c2e40328beac139b18c6c1ba0957c
This commit is contained in:
+2
-1
@@ -2,10 +2,11 @@
|
||||
///@file
|
||||
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct AbstractNixRepl
|
||||
struct AbstractNixRepl : NeverAsync
|
||||
{
|
||||
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lix/libexpr/eval-error.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -72,7 +73,7 @@ EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const A
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void EvalErrorBuilder<T>::debugThrow() &&
|
||||
void EvalErrorBuilder<T>::debugThrow(NeverAsync) &&
|
||||
{
|
||||
if (debug) {
|
||||
if (auto last = debug->traces().next()) {
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
/**
|
||||
* Throw the underlying exception, invoking the debug state callback.
|
||||
*/
|
||||
[[gnu::noinline, gnu::noreturn]] void debugThrow() &&;
|
||||
[[gnu::noinline, gnu::noreturn]] void debugThrow(NeverAsync = {}) &&;
|
||||
|
||||
/**
|
||||
* Throw the underlying exception, bypassing the debug state callback.
|
||||
|
||||
+9
-6
@@ -1,5 +1,6 @@
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/eval-settings.hh"
|
||||
#include "lix/libstore/path.hh"
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "lix/libutil/ansicolor.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
@@ -337,7 +338,7 @@ Evaluator::Evaluator(
|
||||
debugRepl ? std::make_unique<DebugState>(
|
||||
positions,
|
||||
symbols,
|
||||
[this, debugRepl](const ValMap & extraEnv) {
|
||||
[this, debugRepl](const ValMap & extraEnv, NeverAsync) {
|
||||
return activeEval
|
||||
? debugRepl(*activeEval, extraEnv)
|
||||
: ReplExitStatus::Continue;
|
||||
@@ -731,7 +732,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void DebugState::onEvalError(const EvalError * error, const Env & env, const Expr & expr)
|
||||
void DebugState::onEvalError(
|
||||
const EvalError * error, const Env & env, const Expr & expr, NeverAsync
|
||||
)
|
||||
{
|
||||
// Make sure we have a debugger to run and we're not already in a debugger.
|
||||
if (inDebugger)
|
||||
@@ -765,7 +768,7 @@ void DebugState::onEvalError(const EvalError * error, const Env & env, const Exp
|
||||
if (se) {
|
||||
auto vm = mapStaticEnvBindings(symbols, *se.get(), env);
|
||||
DebuggerGuard _guard(inDebugger);
|
||||
auto exitStatus = errorCallback(*vm);
|
||||
auto exitStatus = errorCallback(*vm, {});
|
||||
switch (exitStatus) {
|
||||
case ReplExitStatus::QuitAll:
|
||||
if (error)
|
||||
@@ -2458,7 +2461,7 @@ std::pair<SingleDerivedPath, std::string_view> EvalState::coerceToSingleDerivedP
|
||||
[&](NixStringContextElem::DrvDeep &&) -> SingleDerivedPath {
|
||||
ctx.errors.make<EvalError>(
|
||||
"string '%s' has a context which refers to a complete source and binary closure. This is not supported at this time",
|
||||
s).withTrace(pos, errorCtx).debugThrow();
|
||||
s).withTrace(pos, errorCtx).debugThrow(always_progresses);
|
||||
},
|
||||
[&](NixStringContextElem::Built && b) -> SingleDerivedPath {
|
||||
return std::move(b);
|
||||
@@ -2484,13 +2487,13 @@ SingleDerivedPath EvalState::coerceToSingleDerivedPath(const PosIdx pos, Value &
|
||||
ctx.errors.make<EvalError>(
|
||||
"path string '%s' has context with the different path '%s'",
|
||||
s, sExpected)
|
||||
.withTrace(pos, errorCtx).debugThrow();
|
||||
.withTrace(pos, errorCtx).debugThrow(always_progresses);
|
||||
},
|
||||
[&](const SingleDerivedPath::Built & b) {
|
||||
ctx.errors.make<EvalError>(
|
||||
"string '%s' has context with the output '%s' from derivation '%s', but the string is not the right placeholder for this derivation output. It should be '%s'",
|
||||
s, b.output, b.drvPath->to_string(*ctx.store), sExpected)
|
||||
.withTrace(pos, errorCtx).debugThrow();
|
||||
.withTrace(pos, errorCtx).debugThrow(always_progresses);
|
||||
}
|
||||
}, derivedPath.raw());
|
||||
}
|
||||
|
||||
+5
-4
@@ -7,6 +7,7 @@
|
||||
#include "lix/libutil/box_ptr.hh"
|
||||
#include "lix/libutil/generator.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/source-path.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libexpr/nixexpr.hh"
|
||||
@@ -149,7 +150,7 @@ private:
|
||||
const SymbolTable & symbols;
|
||||
|
||||
public:
|
||||
std::function<ReplExitStatus(ValMap const & extraEnv)> errorCallback;
|
||||
std::function<ReplExitStatus(ValMap const & extraEnv, NeverAsync)> errorCallback;
|
||||
bool stop = false;
|
||||
bool inDebugger = false;
|
||||
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
@@ -158,7 +159,7 @@ public:
|
||||
explicit DebugState(
|
||||
const PosTable & positions,
|
||||
const SymbolTable & symbols,
|
||||
std::function<ReplExitStatus(ValMap const & extraEnv)> errorCallback
|
||||
std::function<ReplExitStatus(ValMap const & extraEnv, NeverAsync)> errorCallback
|
||||
)
|
||||
: positions(positions)
|
||||
, symbols(symbols)
|
||||
@@ -167,7 +168,7 @@ public:
|
||||
assert(errorCallback);
|
||||
}
|
||||
|
||||
void onEvalError(const EvalError * error, const Env & env, const Expr & expr);
|
||||
void onEvalError(const EvalError * error, const Env & env, const Expr & expr, NeverAsync = {});
|
||||
|
||||
const std::shared_ptr<const StaticEnv> staticEnvFor(const Expr & expr) const
|
||||
{
|
||||
@@ -469,7 +470,7 @@ public:
|
||||
overloaded{
|
||||
[](T & p) -> T { return std::move(p); },
|
||||
[](EvalErrorBuilder<E> & e) -> T {
|
||||
std::move(e).debugThrow();
|
||||
std::move(e).debugThrow(always_progresses);
|
||||
}
|
||||
},
|
||||
static_cast<std::variant<T, EvalErrorBuilder<E>> &>(*this)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "lix/libexpr/primops.hh"
|
||||
#include "lix/libfetchers/fetch-to-store.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <kj/async.h>
|
||||
@@ -50,7 +51,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
||||
for (auto & c : context) {
|
||||
auto ensureValid = [&](const StorePath & p) {
|
||||
if (!aio.blockOn(ctx.store->isValidPath(p)))
|
||||
ctx.errors.make<InvalidPathError>(ctx.store->printStorePath(p)).debugThrow();
|
||||
ctx.errors.make<InvalidPathError>(ctx.store->printStorePath(p)).debugThrow(always_progresses);
|
||||
};
|
||||
std::visit(overloaded {
|
||||
[&](const NixStringContextElem::Built & b) {
|
||||
@@ -432,7 +433,7 @@ template<typename Callable>
|
||||
}
|
||||
}
|
||||
|
||||
struct CompareValues
|
||||
struct CompareValues : NeverAsync
|
||||
{
|
||||
EvalState & state;
|
||||
const PosIdx pos;
|
||||
@@ -827,7 +828,7 @@ drvName, Bindings * attrs, Value & v)
|
||||
const std::string & key = state.ctx.symbols[i->name];
|
||||
vomit("processing attribute '%1%'", key);
|
||||
|
||||
auto handleHashMode = [&](const std::string_view s) {
|
||||
auto handleHashMode = [&](const std::string_view s, NeverAsync = {}) {
|
||||
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
||||
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
||||
else if (s == "text") {
|
||||
@@ -839,7 +840,7 @@ drvName, Bindings * attrs, Value & v)
|
||||
).atPos(v).debugThrow();
|
||||
};
|
||||
|
||||
auto handleOutputs = [&](const Strings & ss) {
|
||||
auto handleOutputs = [&](const Strings & ss, NeverAsync = {}) {
|
||||
outputs.clear();
|
||||
for (auto & j : ss) {
|
||||
if (outputs.find(j) != outputs.end())
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "lix/libexpr/extra-primops.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -69,7 +70,7 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value *
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"path '%s' is not a derivation",
|
||||
state.ctx.store->printStorePath(c.path)
|
||||
).atPos(pos).debugThrow();
|
||||
).atPos(pos).debugThrow(always_progresses);
|
||||
}
|
||||
return NixStringContextElem::DrvDeep {
|
||||
.drvPath = c.path,
|
||||
@@ -79,7 +80,7 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value *
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
|
||||
c.output
|
||||
).atPos(pos).debugThrow();
|
||||
).atPos(pos).debugThrow(always_progresses);
|
||||
},
|
||||
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
|
||||
/* Reuse original item because we want this to be idempotent. */
|
||||
|
||||
+5
-4
@@ -7,6 +7,7 @@
|
||||
#include "lix/libexpr/eval-inline.hh"
|
||||
#include "lix/libexpr/value-to-json.hh"
|
||||
#include "eval.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -80,9 +81,9 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption
|
||||
if (pathExists(*writeTo))
|
||||
throw Error("path '%s' already exists", *writeTo);
|
||||
|
||||
std::function<void(Value & v, const PosIdx pos, const Path & path)> recurse;
|
||||
std::function<void(Value & v, const PosIdx pos, const Path & path, NeverAsync)> recurse;
|
||||
|
||||
recurse = [&](Value & v, const PosIdx pos, const Path & path)
|
||||
recurse = [&](Value & v, const PosIdx pos, const Path & path, NeverAsync)
|
||||
{
|
||||
state->forceValue(v, pos);
|
||||
if (v.type() == nString)
|
||||
@@ -96,7 +97,7 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption
|
||||
try {
|
||||
if (name == "." || name == "..")
|
||||
throw Error("invalid file name '%s'", name);
|
||||
recurse(*attr.value, attr.pos, concatStrings(path, "/", name));
|
||||
recurse(*attr.value, attr.pos, concatStrings(path, "/", name), {});
|
||||
} catch (Error & e) {
|
||||
e.addTrace(
|
||||
evaluator->positions[attr.pos],
|
||||
@@ -109,7 +110,7 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption
|
||||
evaluator->errors.make<TypeError>("value at '%s' is not a string or an attribute set", evaluator->positions[pos]).debugThrow();
|
||||
};
|
||||
|
||||
recurse(*v, pos, *writeTo);
|
||||
recurse(*v, pos, *writeTo, {});
|
||||
}
|
||||
|
||||
else if (raw) {
|
||||
|
||||
+9
-6
@@ -19,6 +19,7 @@
|
||||
#include "lix/libutil/terminal.hh"
|
||||
#include "lix/libutil/signals.hh"
|
||||
#include "flake.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
|
||||
#include <limits>
|
||||
#include <iomanip>
|
||||
@@ -1194,13 +1195,15 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||
eval_cache::AttrCursor & visitor,
|
||||
const std::vector<std::string> & attrPath,
|
||||
const std::string & headerPrefix,
|
||||
const std::string & nextPrefix)> visit;
|
||||
const std::string & nextPrefix,
|
||||
NeverAsync)> visit;
|
||||
|
||||
visit = [&](
|
||||
eval_cache::AttrCursor & visitor,
|
||||
const std::vector<std::string> & attrPath,
|
||||
const std::string & headerPrefix,
|
||||
const std::string & nextPrefix)
|
||||
const std::string & nextPrefix,
|
||||
NeverAsync)
|
||||
-> JSON
|
||||
{
|
||||
auto j = JSON::object();
|
||||
@@ -1209,7 +1212,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
|
||||
|
||||
try {
|
||||
auto recurse = [&]()
|
||||
auto recurse = [&](NeverAsync = {})
|
||||
{
|
||||
if (!json)
|
||||
logger->cout("%s", headerPrefix);
|
||||
@@ -1226,12 +1229,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||
attrPath2.push_back(attr);
|
||||
auto j2 = visit(*visitor2, attrPath2,
|
||||
fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attr),
|
||||
nextPrefix + (last ? treeNull : treeLine));
|
||||
nextPrefix + (last ? treeNull : treeLine), {});
|
||||
if (json) j.emplace(attr, std::move(j2));
|
||||
}
|
||||
};
|
||||
|
||||
auto showDerivation = [&]()
|
||||
auto showDerivation = [&](NeverAsync = {})
|
||||
{
|
||||
auto name = visitor.getAttr(*state, "name")->getString(*state);
|
||||
std::optional<std::string> description;
|
||||
@@ -1410,7 +1413,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||
|
||||
auto cache = openEvalCache(*evaluator, flake);
|
||||
|
||||
auto j = visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), "");
|
||||
auto j = visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), "", {});
|
||||
if (json)
|
||||
logger->cout("%s", j.dump());
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ readConstituents(const nix::Value *v, nix::box_ptr<nix::EvalState> &state,
|
||||
state->ctx.errors
|
||||
.make<nix::EvalError>("derivation must have a ‘constituents’ "
|
||||
"attribute")
|
||||
.debugThrow();
|
||||
.debugThrow(nix::always_progresses); // we can't have a debugger here
|
||||
|
||||
nix::NixStringContext context;
|
||||
state->coerceToString(a->pos, *a->value, context,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "lix/libstore/filetransfer.hh"
|
||||
#include "lix/libmain/shared.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
|
||||
constexpr std::string_view INVALID_CHANNEL = "channel:example";
|
||||
constexpr std::string_view CHANNEL_URL = "https://nixos.org/channels/example/nixexprs.tar.xz";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "tests/libexpr.hh"
|
||||
|
||||
namespace nix {
|
||||
@@ -12,19 +13,23 @@ namespace nix {
|
||||
|
||||
TEST_F(ErrorTraceTest, TraceBuilder) {
|
||||
ASSERT_THROW(
|
||||
evaluator.errors.make<EvalError>("puppy").debugThrow(),
|
||||
evaluator.errors.make<EvalError>("puppy").debugThrow(always_progresses),
|
||||
EvalError
|
||||
);
|
||||
|
||||
ASSERT_THROW(
|
||||
evaluator.errors.make<EvalError>("puppy").withTrace(noPos, "doggy").debugThrow(),
|
||||
evaluator.errors.make<EvalError>("puppy")
|
||||
.withTrace(noPos, "doggy")
|
||||
.debugThrow(always_progresses),
|
||||
EvalError
|
||||
);
|
||||
|
||||
ASSERT_THROW(
|
||||
try {
|
||||
try {
|
||||
evaluator.errors.make<EvalError>("puppy").withTrace(noPos, "doggy").debugThrow();
|
||||
evaluator.errors.make<EvalError>("puppy")
|
||||
.withTrace(noPos, "doggy")
|
||||
.debugThrow(always_progresses);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(evaluator.positions[noPos], "beans");
|
||||
throw;
|
||||
@@ -47,10 +52,12 @@ namespace nix {
|
||||
|
||||
TEST_F(ErrorTraceTest, NestedThrows) {
|
||||
try {
|
||||
evaluator.errors.make<EvalError>("puppy").withTrace(noPos, "doggy").debugThrow();
|
||||
evaluator.errors.make<EvalError>("puppy")
|
||||
.withTrace(noPos, "doggy")
|
||||
.debugThrow(always_progresses);
|
||||
} catch (BaseError & e) {
|
||||
try {
|
||||
evaluator.errors.make<EvalError>("beans").debugThrow();
|
||||
evaluator.errors.make<EvalError>("beans").debugThrow(always_progresses);
|
||||
} catch (Error & e2) {
|
||||
e.addTrace(evaluator.positions[noPos], "beans2");
|
||||
//e2.addTrace(state.positions[noPos], "Something", "");
|
||||
|
||||
Reference in New Issue
Block a user