libexpr: eval caches should not have an EvalState&

and for the same reason DrvInfo shouldn't, only even more so. the eval
cache infrastructure wants to be a wrapper around evaluation, with its
lifetimes fully decoupled from the lifetime of the wrapped evaluation.
this can't not create problems when eval lifetime must become bounded.

Change-Id: Ib8eec649995b4decd7290c2266322f67d73b6b46
This commit is contained in:
eldritch horrors 2024-12-03 20:38:41 +01:00
parent 8468dc65c7
commit 9ff702554d
7 changed files with 159 additions and 161 deletions

View file

@ -77,12 +77,12 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
auto attr = getCursor(); auto attr = getCursor();
auto attrPath = attr->getAttrPathStr(); auto attrPath = attr->getAttrPathStr(*state);
if (!attr->isDerivation()) { if (!attr->isDerivation(*state)) {
// FIXME: use eval cache? // FIXME: use eval cache?
auto v = attr->forceValue(); auto v = attr->forceValue(*state);
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths( if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
v, v,
@ -100,14 +100,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
} }
} }
auto drvPath = attr->forceDerivation(); auto drvPath = attr->forceDerivation(*state);
std::optional<NixInt::Inner> priority; std::optional<NixInt::Inner> priority;
if (attr->maybeGetAttr("outputSpecified")) { if (attr->maybeGetAttr(*state, "outputSpecified")) {
} else if (auto aMeta = attr->maybeGetAttr("meta")) { } else if (auto aMeta = attr->maybeGetAttr(*state, "meta")) {
if (auto aPriority = aMeta->maybeGetAttr("priority")) if (auto aPriority = aMeta->maybeGetAttr(*state, "priority"))
priority = aPriority->getInt().value; priority = aPriority->getInt(*state).value;
} }
return {{ return {{
@ -116,14 +116,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
.outputs = std::visit(overloaded { .outputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec { [&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall; std::set<std::string> outputsToInstall;
if (auto aOutputSpecified = attr->maybeGetAttr("outputSpecified")) { if (auto aOutputSpecified = attr->maybeGetAttr(*state, "outputSpecified")) {
if (aOutputSpecified->getBool()) { if (aOutputSpecified->getBool(*state)) {
if (auto aOutputName = attr->maybeGetAttr("outputName")) if (auto aOutputName = attr->maybeGetAttr(*state, "outputName"))
outputsToInstall = { aOutputName->getString() }; outputsToInstall = { aOutputName->getString(*state) };
} }
} else if (auto aMeta = attr->maybeGetAttr("meta")) { } else if (auto aMeta = attr->maybeGetAttr(*state, "meta")) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall")) if (auto aOutputsToInstall = aMeta->maybeGetAttr(*state, "outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings()) for (auto & s : aOutputsToInstall->getListOfStrings(*state))
outputsToInstall.insert(s); outputsToInstall.insert(s);
} }
@ -152,7 +152,7 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
std::pair<Value *, PosIdx> InstallableFlake::toValue() std::pair<Value *, PosIdx> InstallableFlake::toValue()
{ {
return {&getCursor()->forceValue(), noPos}; return {&getCursor()->forceValue(*state), noPos};
} }
std::vector<ref<eval_cache::AttrCursor>> std::vector<ref<eval_cache::AttrCursor>>
@ -170,7 +170,7 @@ InstallableFlake::getCursors()
for (auto & attrPath : attrPaths) { for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath); debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(parseAttrPath(attrPath)); auto attr = root->findAlongAttrPath(*state, parseAttrPath(attrPath));
if (attr) { if (attr) {
res.push_back(ref(*attr)); res.push_back(ref(*attr));
} else { } else {

View file

@ -317,12 +317,12 @@ void completeFlakeRefWithFragment(
attrPath.pop_back(); attrPath.pop_back();
} }
auto attr = root->findAlongAttrPath(attrPath); auto attr = root->findAlongAttrPath(*evalState, attrPath);
if (!attr) continue; if (!attr) continue;
for (auto & attr2 : (*attr)->getAttrs()) { for (auto & attr2 : (*attr)->getAttrs(*evalState)) {
if (std::string_view attr2s = attr2; attr2s.starts_with(lastAttr)) { if (std::string_view attr2s = attr2; attr2s.starts_with(lastAttr)) {
auto attrPath2 = (*attr)->getAttrPath(attr2s); auto attrPath2 = (*attr)->getAttrPath(*evalState, attr2s);
/* Strip the attrpath prefix. */ /* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", attrPath2)); completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", attrPath2));
@ -334,7 +334,7 @@ void completeFlakeRefWithFragment(
attrpaths. */ attrpaths. */
if (fragment.empty()) { if (fragment.empty()) {
for (auto & attrPath : defaultFlakeAttrPaths) { for (auto & attrPath : defaultFlakeAttrPaths) {
auto attr = root->findAlongAttrPath(parseAttrPath(attrPath)); auto attr = root->findAlongAttrPath(*evalState, parseAttrPath(attrPath));
if (!attr) continue; if (!attr) continue;
completions.add(flakeRefS + "#" + prefixRoot); completions.add(flakeRefS + "#" + prefixRoot);
} }

View file

@ -344,7 +344,6 @@ EvalCache::EvalCache(
EvalState & state, EvalState & state,
RootLoader rootLoader) RootLoader rootLoader)
: db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr) : db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
, state(state)
, rootLoader(rootLoader) , rootLoader(rootLoader)
{ {
} }
@ -385,15 +384,15 @@ AttrKey AttrCursor::getKey()
return {parent->first->cachedValue->first, parent->second}; return {parent->first->cachedValue->first, parent->second};
} }
Value & AttrCursor::getValue() Value & AttrCursor::getValue(EvalState & state)
{ {
if (!_value) { if (!_value) {
if (parent) { if (parent) {
auto & vParent = parent->first->getValue(); auto & vParent = parent->first->getValue(state);
root->state.forceAttrs(vParent, noPos, "while searching for an attribute"); state.forceAttrs(vParent, noPos, "while searching for an attribute");
auto attr = vParent.attrs->get(root->state.symbols.create(parent->second)); auto attr = vParent.attrs->get(state.symbols.create(parent->second));
if (!attr) if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr(state));
_value = allocRootValue(attr->value); _value = allocRootValue(attr->value);
} else } else
_value = allocRootValue(root->getRootValue()); _value = allocRootValue(root->getRootValue());
@ -401,43 +400,43 @@ Value & AttrCursor::getValue()
return **_value; return **_value;
} }
std::vector<std::string> AttrCursor::getAttrPath() const std::vector<std::string> AttrCursor::getAttrPath(EvalState & state) const
{ {
if (parent) { if (parent) {
auto attrPath = parent->first->getAttrPath(); auto attrPath = parent->first->getAttrPath(state);
attrPath.push_back(parent->second); attrPath.push_back(parent->second);
return attrPath; return attrPath;
} else } else
return {}; return {};
} }
std::vector<std::string> AttrCursor::getAttrPath(std::string_view name) const std::vector<std::string> AttrCursor::getAttrPath(EvalState & state, std::string_view name) const
{ {
auto attrPath = getAttrPath(); auto attrPath = getAttrPath(state);
attrPath.emplace_back(name); attrPath.emplace_back(name);
return attrPath; return attrPath;
} }
std::string AttrCursor::getAttrPathStr() const std::string AttrCursor::getAttrPathStr(EvalState & state) const
{ {
return concatStringsSep(".", getAttrPath()); return concatStringsSep(".", getAttrPath(state));
} }
std::string AttrCursor::getAttrPathStr(std::string_view name) const std::string AttrCursor::getAttrPathStr(EvalState & state, std::string_view name) const
{ {
return concatStringsSep(".", getAttrPath(name)); return concatStringsSep(".", getAttrPath(state, name));
} }
Value & AttrCursor::forceValue() Value & AttrCursor::forceValue(EvalState & state)
{ {
debug("evaluating uncached attribute '%s'", getAttrPathStr()); debug("evaluating uncached attribute '%s'", getAttrPathStr(state));
auto & v = getValue(); auto & v = getValue(state);
try { try {
root->state.forceValue(v, noPos); state.forceValue(v, noPos);
} catch (EvalError &) { } catch (EvalError &) {
debug("setting '%s' to failed", getAttrPathStr()); debug("setting '%s' to failed", getAttrPathStr(state));
if (root->db) if (root->db)
cachedValue = {root->db->setFailed(getKey()), failed_t()}; cachedValue = {root->db->setFailed(getKey()), failed_t()};
throw; throw;
@ -464,13 +463,13 @@ Value & AttrCursor::forceValue()
return v; return v;
} }
Suggestions AttrCursor::getSuggestionsForAttr(const std::string & name) Suggestions AttrCursor::getSuggestionsForAttr(EvalState & state, const std::string & name)
{ {
auto attrNames = getAttrs(); auto attrNames = getAttrs(state);
return Suggestions::bestMatches({attrNames.begin(), attrNames.end()}, name); return Suggestions::bestMatches({attrNames.begin(), attrNames.end()}, name);
} }
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(const std::string & name) std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(EvalState & state, const std::string & name)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
@ -488,7 +487,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(const std::string & name)
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)) {
debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name)); debug("reevaluating failed cached attribute '%s'", getAttrPathStr(state, name));
} else } 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));
@ -501,13 +500,13 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(const std::string & name)
} }
} }
auto & v = forceValue(); auto & v = forceValue(state);
if (v.type() != nAttrs) if (v.type() != nAttrs)
return nullptr; return nullptr;
//errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); //errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
auto attr = v.attrs->get(root->state.symbols.create(name)); auto attr = v.attrs->get(state.symbols.create(name));
if (!attr) { if (!attr) {
if (root->db) { if (root->db) {
@ -529,21 +528,21 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(const std::string & name)
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
} }
ref<AttrCursor> AttrCursor::getAttr(const std::string & name) ref<AttrCursor> AttrCursor::getAttr(EvalState & state, const std::string & name)
{ {
auto p = maybeGetAttr(name); auto p = maybeGetAttr(state, name);
if (!p) if (!p)
throw Error("attribute '%s' does not exist", getAttrPathStr(name)); throw Error("attribute '%s' does not exist", getAttrPathStr(state, name));
return ref(p); return ref(p);
} }
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const Strings & attrPath) OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(EvalState & state, const Strings & attrPath)
{ {
auto res = shared_from_this(); auto res = shared_from_this();
for (auto & attr : attrPath) { for (auto & attr : attrPath) {
auto child = res->maybeGetAttr(attr); auto child = res->maybeGetAttr(state, attr);
if (!child) { if (!child) {
auto suggestions = res->getSuggestionsForAttr(attr); auto suggestions = res->getSuggestionsForAttr(state, attr);
return OrSuggestions<ref<AttrCursor>>::failed(suggestions); return OrSuggestions<ref<AttrCursor>>::failed(suggestions);
} }
res = child; res = child;
@ -551,30 +550,30 @@ OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const Strings & att
return ref(res); return ref(res);
} }
std::string AttrCursor::getString() std::string AttrCursor::getString(EvalState & state)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
cachedValue = root->db->getAttr(getKey()); cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) { if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto s = std::get_if<string_t>(&cachedValue->second)) { if (auto s = std::get_if<string_t>(&cachedValue->second)) {
debug("using cached string attribute '%s'", getAttrPathStr()); debug("using cached string attribute '%s'", getAttrPathStr(state));
return s->first; return s->first;
} else } else
root->state.errors.make<TypeError>("'%s' is not a string", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not a string", getAttrPathStr(state)).debugThrow();
} }
} }
auto & v = forceValue(); auto & v = forceValue(state);
if (v.type() != nString && v.type() != nPath) { if (v.type() != nString && v.type() != nPath) {
root->state.errors.make<TypeError>("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow(); state.errors.make<TypeError>("'%s' is not a string but %s", getAttrPathStr(state), v.type()).debugThrow();
} }
return v.type() == nString ? v.string.s : v.path().to_string(); return v.type() == nString ? v.string.s : v.path().to_string();
} }
string_t AttrCursor::getStringWithContext() string_t AttrCursor::getStringWithContext(EvalState & state)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
@ -594,21 +593,21 @@ string_t AttrCursor::getStringWithContext()
return o.path; return o.path;
}, },
}, c.raw); }, c.raw);
if (!root->state.store->isValidPath(path)) { if (!state.store->isValidPath(path)) {
valid = false; valid = false;
break; break;
} }
} }
if (valid) { if (valid) {
debug("using cached string attribute '%s'", getAttrPathStr()); debug("using cached string attribute '%s'", getAttrPathStr(state));
return *s; return *s;
} }
} else } else
root->state.errors.make<TypeError>("'%s' is not a string", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not a string", getAttrPathStr(state)).debugThrow();
} }
} }
auto & v = forceValue(); auto & v = forceValue(state);
if (v.type() == nString) { if (v.type() == nString) {
NixStringContext context; NixStringContext context;
@ -617,80 +616,80 @@ string_t AttrCursor::getStringWithContext()
} else if (v.type() == nPath) { } else if (v.type() == nPath) {
return {v.path().to_string(), {}}; return {v.path().to_string(), {}};
} else { } else {
root->state.errors.make<TypeError>("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow(); state.errors.make<TypeError>("'%s' is not a string but %s", getAttrPathStr(state), v.type()).debugThrow();
} }
} }
bool AttrCursor::getBool() bool AttrCursor::getBool(EvalState & state)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
cachedValue = root->db->getAttr(getKey()); cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) { if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto b = std::get_if<bool>(&cachedValue->second)) { if (auto b = std::get_if<bool>(&cachedValue->second)) {
debug("using cached Boolean attribute '%s'", getAttrPathStr()); debug("using cached Boolean attribute '%s'", getAttrPathStr(state));
return *b; return *b;
} else } else
root->state.errors.make<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not a Boolean", getAttrPathStr(state)).debugThrow();
} }
} }
auto & v = forceValue(); auto & v = forceValue(state);
if (v.type() != nBool) if (v.type() != nBool)
root->state.errors.make<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not a Boolean", getAttrPathStr(state)).debugThrow();
return v.boolean; return v.boolean;
} }
NixInt AttrCursor::getInt() NixInt AttrCursor::getInt(EvalState & state)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
cachedValue = root->db->getAttr(getKey()); cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) { if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto i = std::get_if<int_t>(&cachedValue->second)) { if (auto i = std::get_if<int_t>(&cachedValue->second)) {
debug("using cached integer attribute '%s'", getAttrPathStr()); debug("using cached integer attribute '%s'", getAttrPathStr(state));
return i->x; return i->x;
} else } else
root->state.errors.make<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not an integer", getAttrPathStr(state)).debugThrow();
} }
} }
auto & v = forceValue(); auto & v = forceValue(state);
if (v.type() != nInt) if (v.type() != nInt)
root->state.errors.make<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not an integer", getAttrPathStr(state)).debugThrow();
return v.integer; return v.integer;
} }
std::vector<std::string> AttrCursor::getListOfStrings() std::vector<std::string> AttrCursor::getListOfStrings(EvalState & state)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
cachedValue = root->db->getAttr(getKey()); cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) { if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto l = std::get_if<std::vector<std::string>>(&cachedValue->second)) { if (auto l = std::get_if<std::vector<std::string>>(&cachedValue->second)) {
debug("using cached list of strings attribute '%s'", getAttrPathStr()); debug("using cached list of strings attribute '%s'", getAttrPathStr(state));
return *l; return *l;
} else } else
root->state.errors.make<TypeError>("'%s' is not a list of strings", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not a list of strings", getAttrPathStr(state)).debugThrow();
} }
} }
debug("evaluating uncached attribute '%s'", getAttrPathStr()); debug("evaluating uncached attribute '%s'", getAttrPathStr(state));
auto & v = getValue(); auto & v = getValue(state);
root->state.forceValue(v, noPos); state.forceValue(v, noPos);
if (v.type() != nList) if (v.type() != nList)
root->state.errors.make<TypeError>("'%s' is not a list", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not a list", getAttrPathStr(state)).debugThrow();
std::vector<std::string> res; std::vector<std::string> res;
for (auto & elem : v.listItems()) for (auto & elem : v.listItems())
res.push_back(std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching"))); res.push_back(std::string(state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
if (root->db) if (root->db)
cachedValue = {root->db->setListOfStrings(getKey(), res), res}; cachedValue = {root->db->setListOfStrings(getKey(), res), res};
@ -698,28 +697,28 @@ std::vector<std::string> AttrCursor::getListOfStrings()
return res; return res;
} }
std::vector<std::string> AttrCursor::getAttrs() std::vector<std::string> AttrCursor::getAttrs(EvalState & state)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
cachedValue = root->db->getAttr(getKey()); cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) { if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto attrs = std::get_if<fullattr_t>(&cachedValue->second)) { if (auto attrs = std::get_if<fullattr_t>(&cachedValue->second)) {
debug("using cached attrset attribute '%s'", getAttrPathStr()); debug("using cached attrset attribute '%s'", getAttrPathStr(state));
return attrs->p; return attrs->p;
} else } else
root->state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr(state)).debugThrow();
} }
} }
auto & v = forceValue(); auto & v = forceValue(state);
if (v.type() != nAttrs) if (v.type() != nAttrs)
root->state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr(state)).debugThrow();
fullattr_t attrs; fullattr_t attrs;
for (auto & attr : *getValue().attrs) for (auto & attr : *getValue(state).attrs)
attrs.p.push_back(root->state.symbols[attr.name]); attrs.p.push_back(state.symbols[attr.name]);
std::sort(attrs.p.begin(), attrs.p.end()); std::sort(attrs.p.begin(), attrs.p.end());
if (root->db) if (root->db)
@ -728,23 +727,23 @@ std::vector<std::string> AttrCursor::getAttrs()
return attrs.p; return attrs.p;
} }
bool AttrCursor::isDerivation() bool AttrCursor::isDerivation(EvalState & state)
{ {
auto aType = maybeGetAttr("type"); auto aType = maybeGetAttr(state, "type");
return aType && aType->getString() == "derivation"; return aType && aType->getString(state) == "derivation";
} }
StorePath AttrCursor::forceDerivation() StorePath AttrCursor::forceDerivation(EvalState & state)
{ {
auto aDrvPath = getAttr("drvPath"); auto aDrvPath = getAttr(state, "drvPath");
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); auto drvPath = state.store->parseStorePath(aDrvPath->getString(state));
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { if (!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
been garbage-collected. So force it to be regenerated. */ been garbage-collected. So force it to be regenerated. */
aDrvPath->forceValue(); aDrvPath->forceValue(state);
if (!root->state.store->isValidPath(drvPath)) if (!state.store->isValidPath(drvPath))
throw Error("don't know how to recreate store derivation '%s'!", throw Error("don't know how to recreate store derivation '%s'!",
root->state.store->printStorePath(drvPath)); state.store->printStorePath(drvPath));
} }
return drvPath; return drvPath;
} }

View file

@ -40,7 +40,6 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
friend class AttrCursor; friend class AttrCursor;
std::shared_ptr<AttrDb> db; std::shared_ptr<AttrDb> db;
EvalState & state;
RootLoader rootLoader; RootLoader rootLoader;
RootValue value; RootValue value;
@ -102,7 +101,7 @@ class AttrCursor : public std::enable_shared_from_this<AttrCursor>
AttrKey getKey(); AttrKey getKey();
Value & getValue(); Value & getValue(EvalState & state);
public: public:
@ -112,46 +111,46 @@ public:
Value * value = nullptr, Value * value = nullptr,
std::optional<std::pair<AttrId, AttrValue>> && cachedValue = {}); std::optional<std::pair<AttrId, AttrValue>> && cachedValue = {});
std::vector<std::string> getAttrPath() const; std::vector<std::string> getAttrPath(EvalState & state) const;
std::vector<std::string> getAttrPath(std::string_view name) const; std::vector<std::string> getAttrPath(EvalState & state, std::string_view name) const;
std::string getAttrPathStr() const; std::string getAttrPathStr(EvalState & state) const;
std::string getAttrPathStr(std::string_view name) const; std::string getAttrPathStr(EvalState & state, std::string_view name) const;
Suggestions getSuggestionsForAttr(const std::string & name); Suggestions getSuggestionsForAttr(EvalState & state, const std::string & name);
std::shared_ptr<AttrCursor> maybeGetAttr(const std::string & name); std::shared_ptr<AttrCursor> maybeGetAttr(EvalState & state, const std::string & name);
ref<AttrCursor> getAttr(const std::string & name); ref<AttrCursor> getAttr(EvalState & state, const std::string & name);
/** /**
* 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 Strings & attrPath); OrSuggestions<ref<AttrCursor>> findAlongAttrPath(EvalState & state, const Strings & attrPath);
std::string getString(); std::string getString(EvalState & state);
string_t getStringWithContext(); string_t getStringWithContext(EvalState & state);
bool getBool(); bool getBool(EvalState & state);
NixInt getInt(); NixInt getInt(EvalState & state);
std::vector<std::string> getListOfStrings(); std::vector<std::string> getListOfStrings(EvalState & state);
std::vector<std::string> getAttrs(); std::vector<std::string> getAttrs(EvalState & state);
bool isDerivation(); bool isDerivation(EvalState & state);
Value & forceValue(); Value & forceValue(EvalState & state);
/** /**
* Force creation of the .drv file in the Nix store. * Force creation of the .drv file in the Nix store.
*/ */
StorePath forceDerivation(); StorePath forceDerivation(EvalState & state);
}; };
} }

View file

@ -54,18 +54,18 @@ std::string resolveString(
UnresolvedApp InstallableValue::toApp() UnresolvedApp InstallableValue::toApp()
{ {
auto cursor = getCursor(); auto cursor = getCursor();
auto attrPath = cursor->getAttrPath(); auto attrPath = cursor->getAttrPath(*state);
auto type = cursor->getAttr("type")->getString(); auto type = cursor->getAttr(*state, "type")->getString(*state);
std::string expected = !attrPath.empty() && std::string expected = !attrPath.empty() &&
(attrPath[0] == "apps" || attrPath[0] == "defaultApp") (attrPath[0] == "apps" || attrPath[0] == "defaultApp")
? "app" : "derivation"; ? "app" : "derivation";
if (type != expected) if (type != expected)
throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(), expected); throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(*state), expected);
if (type == "app") { if (type == "app") {
auto [program, context] = cursor->getAttr("program")->getStringWithContext(); auto [program, context] = cursor->getAttr(*state, "program")->getStringWithContext(*state);
std::vector<DerivedPath> context2; std::vector<DerivedPath> context2;
for (auto & c : context) { for (auto & c : context) {
@ -98,18 +98,18 @@ UnresolvedApp InstallableValue::toApp()
} }
else if (type == "derivation") { else if (type == "derivation") {
auto drvPath = cursor->forceDerivation(); auto drvPath = cursor->forceDerivation(*state);
auto outPath = cursor->getAttr("outPath")->getString(); auto outPath = cursor->getAttr(*state, "outPath")->getString(*state);
auto outputName = cursor->getAttr("outputName")->getString(); auto outputName = cursor->getAttr(*state, "outputName")->getString(*state);
auto name = cursor->getAttr("name")->getString(); auto name = cursor->getAttr(*state, "name")->getString(*state);
auto aPname = cursor->maybeGetAttr("pname"); auto aPname = cursor->maybeGetAttr(*state, "pname");
auto aMeta = cursor->maybeGetAttr("meta"); auto aMeta = cursor->maybeGetAttr(*state, "meta");
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr; auto aMainProgram = aMeta ? aMeta->maybeGetAttr(*state, "mainProgram") : nullptr;
auto mainProgram = auto mainProgram =
aMainProgram aMainProgram
? aMainProgram->getString() ? aMainProgram->getString(*state)
: aPname : aPname
? aPname->getString() ? aPname->getString(*state)
: DrvName(name).name; : DrvName(name).name;
auto program = outPath + "/bin/" + mainProgram; auto program = outPath + "/bin/" + mainProgram;
return UnresolvedApp { App { return UnresolvedApp { App {
@ -122,7 +122,7 @@ UnresolvedApp InstallableValue::toApp()
} }
else else
throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type); throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(*state), type);
} }
// FIXME: move to libcmd // FIXME: move to libcmd

View file

@ -864,14 +864,14 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
auto cursor = installable.getCursor(); auto cursor = installable.getCursor();
auto templateDirAttr = cursor->getAttr("path"); auto templateDirAttr = cursor->getAttr(*evalState, "path");
auto templateDir = templateDirAttr->getString(); auto templateDir = templateDirAttr->getString(*evalState);
if (!store->isInStore(templateDir)) if (!store->isInStore(templateDir))
evalState->errors.make<TypeError>( evalState->errors.make<TypeError>(
"'%s' was not found in the Nix store\n" "'%s' was not found in the Nix store\n"
"If you've set '%s' to a string, try using a path instead.", "If you've set '%s' to a string, try using a path instead.",
templateDir, templateDirAttr->getAttrPathStr()).debugThrow(); templateDir, templateDirAttr->getAttrPathStr(*evalState)).debugThrow();
std::vector<Path> changedFiles; std::vector<Path> changedFiles;
std::vector<Path> conflictedFiles; std::vector<Path> conflictedFiles;
@ -928,10 +928,10 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
for (auto & s : changedFiles) args.push_back(s); for (auto & s : changedFiles) args.push_back(s);
runProgram("git", true, args); runProgram("git", true, args);
} }
auto welcomeText = cursor->maybeGetAttr("welcomeText"); auto welcomeText = cursor->maybeGetAttr(*evalState, "welcomeText");
if (welcomeText) { if (welcomeText) {
notice("\n"); notice("\n");
notice(renderMarkdownToTerminal(welcomeText->getString())); notice(renderMarkdownToTerminal(welcomeText->getString(*evalState)));
} }
if (!conflictedFiles.empty()) if (!conflictedFiles.empty())
@ -1152,7 +1152,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
{ {
attrPath.push_back(attr); attrPath.push_back(attr);
auto visitor2 = visitor.getAttr(attr); auto visitor2 = visitor.getAttr(*state, attr);
try { try {
if ((attrPath[0] == "apps" if ((attrPath[0] == "apps"
@ -1161,7 +1161,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|| attrPath[0] == "legacyPackages" || attrPath[0] == "legacyPackages"
|| attrPath[0] == "packages") || attrPath[0] == "packages")
&& (attrPath.size() == 1 || attrPath.size() == 2)) { && (attrPath.size() == 1 || attrPath.size() == 2)) {
for (const auto &subAttr : visitor2->getAttrs()) { for (const auto &subAttr : visitor2->getAttrs(*state)) {
if (hasContent(*visitor2, attrPath, subAttr)) { if (hasContent(*visitor2, attrPath, subAttr)) {
return true; return true;
} }
@ -1175,7 +1175,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|| attrPath[0] == "nixosModules" || attrPath[0] == "nixosModules"
|| attrPath[0] == "overlays" || attrPath[0] == "overlays"
)) { )) {
for (const auto &subAttr : visitor2->getAttrs()) { for (const auto &subAttr : visitor2->getAttrs(*state)) {
if (hasContent(*visitor2, attrPath, subAttr)) { if (hasContent(*visitor2, attrPath, subAttr)) {
return true; return true;
} }
@ -1217,14 +1217,14 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
if (!json) if (!json)
logger->cout("%s", headerPrefix); logger->cout("%s", headerPrefix);
std::vector<std::string> attrs; std::vector<std::string> attrs;
for (const auto &attr : visitor.getAttrs()) { for (const auto &attr : visitor.getAttrs(*state)) {
if (hasContent(visitor, attrPath, attr)) if (hasContent(visitor, attrPath, attr))
attrs.push_back(attr); attrs.push_back(attr);
} }
for (const auto & [i, attr] : enumerate(attrs)) { for (const auto & [i, attr] : enumerate(attrs)) {
bool last = i + 1 == attrs.size(); bool last = i + 1 == attrs.size();
auto visitor2 = visitor.getAttr(attr); auto visitor2 = visitor.getAttr(*state, attr);
auto attrPath2(attrPath); auto attrPath2(attrPath);
attrPath2.push_back(attr); attrPath2.push_back(attr);
auto j2 = visit(*visitor2, attrPath2, auto j2 = visit(*visitor2, attrPath2,
@ -1236,11 +1236,11 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
auto showDerivation = [&]() auto showDerivation = [&]()
{ {
auto name = visitor.getAttr("name")->getString(); auto name = visitor.getAttr(*state, "name")->getString(*state);
std::optional<std::string> description; std::optional<std::string> description;
if (auto aMeta = visitor.maybeGetAttr("meta")) { if (auto aMeta = visitor.maybeGetAttr(*state, "meta")) {
if (auto aDescription = aMeta->maybeGetAttr("description")) if (auto aDescription = aMeta->maybeGetAttr(*state, "description"))
description = aDescription->getString(); description = aDescription->getString(*state);
} }
if (json) { if (json) {
@ -1324,7 +1324,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPath))); logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPath)));
} }
} else { } else {
if (visitor.isDerivation()) if (visitor.isDerivation(*state))
showDerivation(); showDerivation();
else else
throw Error("expected a derivation"); throw Error("expected a derivation");
@ -1332,7 +1332,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
} }
else if (attrPath.size() > 0 && attrPath[0] == "hydraJobs") { else if (attrPath.size() > 0 && attrPath[0] == "hydraJobs") {
if (visitor.isDerivation()) if (visitor.isDerivation(*state))
showDerivation(); showDerivation();
else else
recurse(); recurse();
@ -1354,7 +1354,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPath))); logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPath)));
} }
} else { } else {
if (visitor.isDerivation()) if (visitor.isDerivation(*state))
showDerivation(); showDerivation();
else if (attrPath.size() <= 2) else if (attrPath.size() <= 2)
// FIXME: handle recurseIntoAttrs // FIXME: handle recurseIntoAttrs
@ -1366,8 +1366,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
(attrPath.size() == 2 && attrPath[0] == "defaultApp") || (attrPath.size() == 2 && attrPath[0] == "defaultApp") ||
(attrPath.size() == 3 && attrPath[0] == "apps")) (attrPath.size() == 3 && attrPath[0] == "apps"))
{ {
auto aType = visitor.maybeGetAttr("type"); auto aType = visitor.maybeGetAttr(*state, "type");
if (!aType || aType->getString() != "app") if (!aType || aType->getString(*state) != "app")
state->errors.make<EvalError>("not an app definition").debugThrow(); state->errors.make<EvalError>("not an app definition").debugThrow();
if (json) { if (json) {
j.emplace("type", "app"); j.emplace("type", "app");
@ -1380,7 +1380,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
(attrPath.size() == 1 && attrPath[0] == "defaultTemplate") || (attrPath.size() == 1 && attrPath[0] == "defaultTemplate") ||
(attrPath.size() == 2 && attrPath[0] == "templates")) (attrPath.size() == 2 && attrPath[0] == "templates"))
{ {
auto description = visitor.getAttr("description")->getString(); auto description = visitor.getAttr(*state, "description")->getString(*state);
if (json) { if (json) {
j.emplace("type", "template"); j.emplace("type", "template");
j.emplace("description", description); j.emplace("description", description);

View file

@ -100,20 +100,20 @@ struct CmdSearch : InstallableCommand, MixJSON
try { try {
auto recurse = [&]() auto recurse = [&]()
{ {
for (const auto & attr : cursor.getAttrs()) { for (const auto & attr : cursor.getAttrs(*state)) {
auto cursor2 = cursor.getAttr(attr); auto cursor2 = cursor.getAttr(*state, attr);
auto attrPath2(attrPath); auto attrPath2(attrPath);
attrPath2.emplace_back(attr); attrPath2.emplace_back(attr);
visit(*cursor2, attrPath2, false); visit(*cursor2, attrPath2, false);
} }
}; };
if (cursor.isDerivation()) { if (cursor.isDerivation(*state)) {
DrvName name(cursor.getAttr("name")->getString()); DrvName name(cursor.getAttr(*state, "name")->getString(*state));
auto aMeta = cursor.maybeGetAttr("meta"); auto aMeta = cursor.maybeGetAttr(*state, "meta");
auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr; auto aDescription = aMeta ? aMeta->maybeGetAttr(*state, "description") : nullptr;
auto description = aDescription ? aDescription->getString() : ""; auto description = aDescription ? aDescription->getString(*state) : "";
std::replace(description.begin(), description.end(), '\n', ' '); std::replace(description.begin(), description.end(), '\n', ' ');
auto attrPath2 = concatStringsSep(".", attrPath); auto attrPath2 = concatStringsSep(".", attrPath);
@ -181,8 +181,8 @@ struct CmdSearch : InstallableCommand, MixJSON
recurse(); recurse();
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) { else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
auto attr = cursor.maybeGetAttr("recurseForDerivations"); auto attr = cursor.maybeGetAttr(*state, "recurseForDerivations");
if (attr && attr->getBool()) if (attr && attr->getBool(*state))
recurse(); recurse();
} }
@ -193,7 +193,7 @@ struct CmdSearch : InstallableCommand, MixJSON
}; };
for (auto & cursor : installableValue->getCursors()) for (auto & cursor : installableValue->getCursors())
visit(*cursor, cursor->getAttrPath(), true); visit(*cursor, cursor->getAttrPath(*state), true);
if (json) if (json)
logger->cout("%s", *jsonOut); logger->cout("%s", *jsonOut);