AttrCursor: Remove forceErrors

Instead, force evaluation of the original value only if we need to
show the exception to the user.
This commit is contained in:
Eelco Dolstra 2024-04-19 19:34:07 +02:00
parent 8b86f415c1
commit 2c88930ef2
5 changed files with 58 additions and 20 deletions

View File

@ -7,6 +7,25 @@
namespace nix::eval_cache { namespace nix::eval_cache {
CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr)
: EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
, cursor(cursor), attr(attr)
{ }
void CachedEvalError::force()
{
auto & v = cursor->forceValue();
if (v.type() == nAttrs) {
auto a = v.attrs()->get(this->attr);
state.forceValue(*a->value, a->pos);
}
// Shouldn't happen.
throw EvalError(state, "evaluation of cached failed attribute '%s' unexpectedly succeeded", cursor->getAttrPathStr(attr));
}
static const char * schema = R"sql( static const char * schema = R"sql(
create table if not exists Attributes ( create table if not exists Attributes (
parent integer not null, parent integer not null,
@ -470,7 +489,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]); return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]);
} }
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors) std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
@ -487,12 +506,9 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
if (attr) { if (attr) {
if (std::get_if<missing_t>(&attr->second)) if (std::get_if<missing_t>(&attr->second))
return nullptr; return nullptr;
else if (std::get_if<failed_t>(&attr->second)) { else if (std::get_if<failed_t>(&attr->second))
if (forceErrors) throw CachedEvalError(ref(shared_from_this()), name);
debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name)); else
else
throw CachedEvalError(root->state, "cached failure of attribute '%s'", getAttrPathStr(name));
} else
return std::make_shared<AttrCursor>(root, return std::make_shared<AttrCursor>(root,
std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); std::make_pair(shared_from_this(), name), nullptr, std::move(attr));
} }
@ -537,9 +553,9 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name)
return maybeGetAttr(root->state.symbols.create(name)); return maybeGetAttr(root->state.symbols.create(name));
} }
ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors) ref<AttrCursor> AttrCursor::getAttr(Symbol name)
{ {
auto p = maybeGetAttr(name, forceErrors); auto p = maybeGetAttr(name);
if (!p) if (!p)
throw Error("attribute '%s' does not exist", getAttrPathStr(name)); throw Error("attribute '%s' does not exist", getAttrPathStr(name));
return ref(p); return ref(p);
@ -550,11 +566,11 @@ ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
return getAttr(root->state.symbols.create(name)); return getAttr(root->state.symbols.create(name));
} }
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force) OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath)
{ {
auto res = shared_from_this(); auto res = shared_from_this();
for (auto & attr : attrPath) { for (auto & attr : attrPath) {
auto child = res->maybeGetAttr(attr, force); auto child = res->maybeGetAttr(attr);
if (!child) { if (!child) {
auto suggestions = res->getSuggestionsForAttr(attr); auto suggestions = res->getSuggestionsForAttr(attr);
return OrSuggestions<ref<AttrCursor>>::failed(suggestions); return OrSuggestions<ref<AttrCursor>>::failed(suggestions);
@ -751,7 +767,7 @@ bool AttrCursor::isDerivation()
StorePath AttrCursor::forceDerivation() StorePath AttrCursor::forceDerivation()
{ {
auto aDrvPath = getAttr(root->state.sDrvPath, true); auto aDrvPath = getAttr(root->state.sDrvPath);
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
/* The eval cache contains 'drvPath', but the actual path has /* The eval cache contains 'drvPath', but the actual path has

View File

@ -10,14 +10,28 @@
namespace nix::eval_cache { namespace nix::eval_cache {
MakeError(CachedEvalError, EvalError);
struct AttrDb; struct AttrDb;
class AttrCursor; class AttrCursor;
struct CachedEvalError : EvalError
{
const ref<AttrCursor> cursor;
const Symbol attr;
CachedEvalError(ref<AttrCursor> cursor, Symbol attr);
/**
* Evaluate this attribute, which should result in a regular
* `EvalError` exception being thrown.
*/
[[noreturn]]
void force();
};
class EvalCache : public std::enable_shared_from_this<EvalCache> class EvalCache : public std::enable_shared_from_this<EvalCache>
{ {
friend class AttrCursor; friend class AttrCursor;
friend class CachedEvalError;
std::shared_ptr<AttrDb> db; std::shared_ptr<AttrDb> db;
EvalState & state; EvalState & state;
@ -73,6 +87,7 @@ typedef std::variant<
class AttrCursor : public std::enable_shared_from_this<AttrCursor> class AttrCursor : public std::enable_shared_from_this<AttrCursor>
{ {
friend class EvalCache; friend class EvalCache;
friend class CachedEvalError;
ref<EvalCache> root; ref<EvalCache> root;
typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, Symbol>> Parent; typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, Symbol>> Parent;
@ -102,11 +117,11 @@ public:
Suggestions getSuggestionsForAttr(Symbol name); Suggestions getSuggestionsForAttr(Symbol name);
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name, bool forceErrors = false); std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name);
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name); std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name);
ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false); ref<AttrCursor> getAttr(Symbol name);
ref<AttrCursor> getAttr(std::string_view name); ref<AttrCursor> getAttr(std::string_view name);
@ -114,7 +129,7 @@ public:
* Get an attribute along a chain of attrsets. Note that this does * Get an attribute along a chain of attrsets. Note that this does
* not auto-call functors or functions. * not auto-call functors or functions.
*/ */
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false); OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath);
std::string getString(); std::string getString();

View File

@ -99,7 +99,6 @@ template class EvalErrorBuilder<TypeError>;
template class EvalErrorBuilder<UndefinedVarError>; template class EvalErrorBuilder<UndefinedVarError>;
template class EvalErrorBuilder<MissingArgumentError>; template class EvalErrorBuilder<MissingArgumentError>;
template class EvalErrorBuilder<InfiniteRecursionError>; template class EvalErrorBuilder<InfiniteRecursionError>;
template class EvalErrorBuilder<CachedEvalError>;
template class EvalErrorBuilder<InvalidPathError>; template class EvalErrorBuilder<InvalidPathError>;
} }

View File

@ -43,7 +43,6 @@ MakeError(Abort, EvalError);
MakeError(TypeError, EvalError); MakeError(TypeError, EvalError);
MakeError(UndefinedVarError, EvalError); MakeError(UndefinedVarError, EvalError);
MakeError(MissingArgumentError, EvalError); MakeError(MissingArgumentError, EvalError);
MakeError(CachedEvalError, EvalError);
MakeError(InfiniteRecursionError, EvalError); MakeError(InfiniteRecursionError, EvalError);
struct InvalidPathError : public EvalError struct InvalidPathError : public EvalError

View File

@ -18,6 +18,7 @@
#include "terminal.hh" #include "terminal.hh"
#include "users.hh" #include "users.hh"
#include "network-proxy.hh" #include "network-proxy.hh"
#include "eval-cache.hh"
#include <sys/types.h> #include <sys/types.h>
#include <regex> #include <regex>
@ -515,7 +516,15 @@ void mainWrapped(int argc, char * * argv)
if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) { if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) {
evalSettings.pureEval = false; evalSettings.pureEval = false;
} }
args.command->second->run();
try {
args.command->second->run();
} catch (eval_cache::CachedEvalError & e) {
/* Evaluate the original attribute that resulted in this
cached error so that we can show the original error to the
user. */
e.force();
}
} }
} }