Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AttrCursor: Remove forceErrors #10564

Merged
merged 3 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions src/libexpr/eval-cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,25 @@

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(
create table if not exists Attributes (
parent integer not null,
Expand Down Expand Up @@ -470,7 +489,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol 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 (!cachedValue)
Expand All @@ -487,12 +506,9 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
if (attr) {
if (std::get_if<missing_t>(&attr->second))
return nullptr;
else if (std::get_if<failed_t>(&attr->second)) {
if (forceErrors)
debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name));
else
throw CachedEvalError(root->state, "cached failure of attribute '%s'", getAttrPathStr(name));
} else
else if (std::get_if<failed_t>(&attr->second))
throw CachedEvalError(ref(shared_from_this()), name);
else
return std::make_shared<AttrCursor>(root,
std::make_pair(shared_from_this(), name), nullptr, std::move(attr));
}
Expand Down Expand Up @@ -537,9 +553,9 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view 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)
throw Error("attribute '%s' does not exist", getAttrPathStr(name));
return ref(p);
Expand All @@ -550,11 +566,11 @@ ref<AttrCursor> AttrCursor::getAttr(std::string_view 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();
for (auto & attr : attrPath) {
auto child = res->maybeGetAttr(attr, force);
auto child = res->maybeGetAttr(attr);
if (!child) {
auto suggestions = res->getSuggestionsForAttr(attr);
return OrSuggestions<ref<AttrCursor>>::failed(suggestions);
Expand Down Expand Up @@ -751,7 +767,7 @@ bool AttrCursor::isDerivation()

StorePath AttrCursor::forceDerivation()
{
auto aDrvPath = getAttr(root->state.sDrvPath, true);
auto aDrvPath = getAttr(root->state.sDrvPath);
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
/* The eval cache contains 'drvPath', but the actual path has
Expand Down
25 changes: 20 additions & 5 deletions src/libexpr/eval-cache.hh
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,28 @@

namespace nix::eval_cache {

MakeError(CachedEvalError, EvalError);

struct AttrDb;
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>
{
friend class AttrCursor;
friend class CachedEvalError;

std::shared_ptr<AttrDb> db;
EvalState & state;
Expand Down Expand Up @@ -73,6 +87,7 @@ typedef std::variant<
class AttrCursor : public std::enable_shared_from_this<AttrCursor>
{
friend class EvalCache;
friend class CachedEvalError;

ref<EvalCache> root;
typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, Symbol>> Parent;
Expand Down Expand Up @@ -102,19 +117,19 @@ public:

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);

ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
ref<AttrCursor> getAttr(Symbol name);

ref<AttrCursor> getAttr(std::string_view name);

/**
* Get an attribute along a chain of attrsets. Note that this does
* 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();

Expand Down
1 change: 0 additions & 1 deletion src/libexpr/eval-error.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ template class EvalErrorBuilder<TypeError>;
template class EvalErrorBuilder<UndefinedVarError>;
template class EvalErrorBuilder<MissingArgumentError>;
template class EvalErrorBuilder<InfiniteRecursionError>;
template class EvalErrorBuilder<CachedEvalError>;
template class EvalErrorBuilder<InvalidPathError>;

}
1 change: 0 additions & 1 deletion src/libexpr/eval-error.hh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ MakeError(Abort, EvalError);
MakeError(TypeError, EvalError);
MakeError(UndefinedVarError, EvalError);
MakeError(MissingArgumentError, EvalError);
MakeError(CachedEvalError, EvalError);
MakeError(InfiniteRecursionError, EvalError);

struct InvalidPathError : public EvalError
Expand Down
2 changes: 1 addition & 1 deletion src/nix/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
// If we don't recognize it, it's probably content
return true;
} catch (EvalError & e) {
// Some attrs may contain errors, eg. legacyPackages of
// Some attrs may contain errors, e.g. legacyPackages of
// nixpkgs. We still want to recurse into it, instead of
// skipping it at all.
return true;
Expand Down
11 changes: 10 additions & 1 deletion src/nix/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "terminal.hh"
#include "users.hh"
#include "network-proxy.hh"
#include "eval-cache.hh"

#include <sys/types.h>
#include <regex>
Expand Down Expand Up @@ -515,7 +516,15 @@ void mainWrapped(int argc, char * * argv)
if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) {
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();
}
}

}
Expand Down
22 changes: 22 additions & 0 deletions tests/functional/flakes/eval-cache.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
source ./common.sh

requireGit

flake1Dir="$TEST_ROOT/eval-cache-flake"

createGitRepo "$flake1Dir" ""

cat >"$flake1Dir/flake.nix" <<EOF
{
description = "Fnord";
outputs = { self }: {
foo.bar = throw "breaks";
};
}
EOF

git -C "$flake1Dir" add flake.nix
git -C "$flake1Dir" commit -m "Init"

expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks'
expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks'
1 change: 1 addition & 0 deletions tests/functional/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ nix_tests = \
flakes/build-paths.sh \
flakes/flake-in-submodule.sh \
flakes/prefetch.sh \
flakes/eval-cache.sh \
gc.sh \
nix-collect-garbage-d.sh \
remote-store.sh \
Expand Down
Loading