Use std::set<StringContextElem> not PathSet for string contexts

Motivation

`PathSet` is not correct because string contexts have other forms
(`Built` and `DrvDeep`) that are not rendered as plain store paths.
Instead of wrongly using `PathSet`, or "stringly typed" using
`StringSet`, use `std::std<StringContextElem>`.

-----

In support of this change, `NixStringContext` is now defined as
`std::std<StringContextElem>` not `std:vector<StringContextElem>`. The
old definition was just used by a `getContext` method which was only
used by the eval cache. It can be deleted altogether since the types are
now unified and the preexisting `copyContext` function already suffices.

Summarizing the previous paragraph:

Old:

  - `value/context.hh`: `NixStringContext = std::vector<StringContextElem>`
  - `value.hh`: `NixStringContext Value::getContext(...)`
  - `value.hh`: `copyContext(...)`

New:

  - `value/context.hh`: `NixStringContext = std::set<StringContextElem>`
  - `value.hh`: `copyContext(...)`
----

The string representation of string context elements no longer contains
the store dir. The diff of `src/libexpr/tests/value/context.cc` should
make clear what the new representation is, so we recommend reviewing
that file first. This was done for two reasons:

Less API churn:

`Value::mkString` and friends did not take a `Store` before. But if
`NixStringContextElem::{parse, to_string}` *do* take a store (as they
did before), then we cannot have the `Value` functions use them (in
order to work with the fully-structured `NixStringContext`) without
adding that argument.

That would have been a lot of churn of threading the store, and this
diff is already large enough, so the easier and less invasive thing to
do was simply make the element `parse` and `to_string` functions not
take the `Store` reference, and the easiest way to do that was to simply
drop the store dir.

Space usage:

Dropping the `/nix/store/` (or similar) from the internal representation
will safe space in the heap of the Nix programming being interpreted. If
the heap contains many strings with non-trivial contexts, the saving
could add up to something significant.

----

The eval cache version is bumped.

The eval cache serialization uses `NixStringContextElem::{parse,
to_string}`, and since those functions are changed per the above, that
means the on-disk representation is also changed.

This is simply done by changing the name of the used for the eval cache
from `eval-cache-v4` to eval-cache-v5`.

----

To avoid some duplication `EvalCache::mkPathString` is added to abstract
over the simple case of turning a store path to a string with just that
string in the context.

Context

This PR picks up where #7543 left off. That one introduced the fully
structured `NixStringContextElem` data type, but kept `PathSet context`
as an awkward middle ground between internal `char[][]` interpreter heap
string contexts and `NixStringContext` fully parsed string contexts.

The infelicity of `PathSet context` was specifically called out during
Nix team group review, but it was agreeing that fixing it could be left
as future work. This is that future work.

A possible follow-up step would be to get rid of the `char[][]`
evaluator heap representation, too, but it is not yet clear how to do
that. To use `NixStringContextElem` there we would need to get the STL
containers to GC pointers in the GC build, and I am not sure how to do
that.

----

PR #7543 effectively is writing the inverse of a `mkPathString`,
`mkOutputString`, and one more such function for the `DrvDeep` case. I
would like that PR to have property tests ensuring it is actually the
inverse as expected.

This PR sets things up nicely so that reworking that PR to be in that
more elegant and better tested way is possible.

Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com>
This commit is contained in:
John Ericson 2023-01-28 20:31:10 -05:00
parent ef432b2b15
commit 85f0cdc370
27 changed files with 219 additions and 204 deletions

View file

@ -96,7 +96,7 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
auto v = attr->forceValue(); auto v = attr->forceValue();
if (v.type() == nPath) { if (v.type() == nPath) {
PathSet context; NixStringContext context;
auto storePath = state->copyPathToStore(context, Path(v.path)); auto storePath = state->copyPathToStore(context, Path(v.path));
return {{ return {{
.path = DerivedPath::Opaque { .path = DerivedPath::Opaque {
@ -107,10 +107,10 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
} }
else if (v.type() == nString) { else if (v.type() == nString) {
PathSet context; NixStringContext context;
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath)); auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
auto storePath = state->store->maybeParseStorePath(s); auto storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) { if (storePath && context.count(NixStringContextElem::Opaque { .path = *storePath })) {
return {{ return {{
.path = DerivedPath::Opaque { .path = DerivedPath::Opaque {
.path = std::move(*storePath), .path = std::move(*storePath),

View file

@ -596,7 +596,7 @@ bool NixRepl::processLine(std::string line)
const auto [path, line] = [&] () -> std::pair<Path, uint32_t> { const auto [path, line] = [&] () -> std::pair<Path, uint32_t> {
if (v.type() == nPath || v.type() == nString) { if (v.type() == nPath || v.type() == nString) {
PathSet context; NixStringContext context;
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit"); auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
return {path, 0}; return {path, 0};
} else if (v.isLambda()) { } else if (v.isLambda()) {
@ -940,7 +940,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
if (isDrv) { if (isDrv) {
str << "«derivation "; str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath); Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context; NixStringContext context;
if (i != v.attrs->end()) if (i != v.attrs->end())
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
else else

View file

@ -47,7 +47,7 @@ struct AttrDb
{ {
auto state(_state->lock()); auto state(_state->lock());
Path cacheDir = getCacheDir() + "/nix/eval-cache-v4"; Path cacheDir = getCacheDir() + "/nix/eval-cache-v5";
createDirs(cacheDir); createDirs(cacheDir);
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
@ -300,7 +300,7 @@ struct AttrDb
NixStringContext context; NixStringContext context;
if (!queryAttribute.isNull(3)) if (!queryAttribute.isNull(3))
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";")) for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
context.push_back(NixStringContextElem::parse(cfg, s)); context.insert(NixStringContextElem::parse(s));
return {{rowId, string_t{queryAttribute.getStr(2), context}}}; return {{rowId, string_t{queryAttribute.getStr(2), context}}};
} }
case AttrType::Bool: case AttrType::Bool:
@ -619,9 +619,11 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue(); auto & v = forceValue();
if (v.type() == nString) if (v.type() == nString) {
return {v.string.s, v.getContext(*root->state.store)}; NixStringContext context;
else if (v.type() == nPath) copyContext(v, context);
return {v.string.s, std::move(context)};
} else if (v.type() == nPath)
return {v.path, {}}; return {v.path, {}};
else else
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>(); root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();

View file

@ -609,8 +609,7 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value &
{ {
allowPath(storePath); allowPath(storePath);
auto path = store->printStorePath(storePath); mkStorePathString(storePath, v);
v.mkString(path, PathSet({path}));
} }
Path EvalState::checkSourcePath(const Path & path_) Path EvalState::checkSourcePath(const Path & path_)
@ -692,7 +691,7 @@ void EvalState::checkURI(const std::string & uri)
} }
Path EvalState::toRealPath(const Path & path, const PathSet & context) Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
{ {
// FIXME: check whether 'path' is in 'context'. // FIXME: check whether 'path' is in 'context'.
return return
@ -944,25 +943,25 @@ void Value::mkString(std::string_view s)
} }
static void copyContextToValue(Value & v, const PathSet & context) static void copyContextToValue(Value & v, const NixStringContext & context)
{ {
if (!context.empty()) { if (!context.empty()) {
size_t n = 0; size_t n = 0;
v.string.context = (const char * *) v.string.context = (const char * *)
allocBytes((context.size() + 1) * sizeof(char *)); allocBytes((context.size() + 1) * sizeof(char *));
for (auto & i : context) for (auto & i : context)
v.string.context[n++] = dupString(i.c_str()); v.string.context[n++] = dupString(i.to_string().c_str());
v.string.context[n] = 0; v.string.context[n] = 0;
} }
} }
void Value::mkString(std::string_view s, const PathSet & context) void Value::mkString(std::string_view s, const NixStringContext & context)
{ {
mkString(s); mkString(s);
copyContextToValue(*this, context); copyContextToValue(*this, context);
} }
void Value::mkStringMove(const char * s, const PathSet & context) void Value::mkStringMove(const char * s, const NixStringContext & context)
{ {
mkString(s); mkString(s);
copyContextToValue(*this, context); copyContextToValue(*this, context);
@ -1038,6 +1037,16 @@ void EvalState::mkPos(Value & v, PosIdx p)
} }
void EvalState::mkStorePathString(const StorePath & p, Value & v)
{
v.mkString(
store->printStorePath(p),
NixStringContext {
NixStringContextElem::Opaque { .path = p },
});
}
/* Create a thunk for the delayed computation of the given expression /* Create a thunk for the delayed computation of the given expression
in the given environment. But if the expression is a variable, in the given environment. But if the expression is a variable,
then look it up right away. This significantly reduces the number then look it up right away. This significantly reduces the number
@ -1900,7 +1909,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{ {
PathSet context; NixStringContext context;
std::vector<BackedStringView> s; std::vector<BackedStringView> s;
size_t sSize = 0; size_t sSize = 0;
NixInt n = 0; NixInt n = 0;
@ -2109,26 +2118,15 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
} }
void copyContext(const Value & v, PathSet & context) void copyContext(const Value & v, NixStringContext & context)
{ {
if (v.string.context) if (v.string.context)
for (const char * * p = v.string.context; *p; ++p) for (const char * * p = v.string.context; *p; ++p)
context.insert(*p); context.insert(NixStringContextElem::parse(*p));
} }
NixStringContext Value::getContext(const Store & store) std::string_view EvalState::forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx)
{
NixStringContext res;
assert(internalType == tString);
if (string.context)
for (const char * * p = string.context; *p; ++p)
res.push_back(NixStringContextElem::parse(store, *p));
return res;
}
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx)
{ {
auto s = forceString(v, pos, errorCtx); auto s = forceString(v, pos, errorCtx);
copyContext(v, context); copyContext(v, context);
@ -2158,7 +2156,7 @@ bool EvalState::isDerivation(Value & v)
std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v, std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore) NixStringContext & context, bool coerceMore, bool copyToStore)
{ {
auto i = v.attrs->find(sToString); auto i = v.attrs->find(sToString);
if (i != v.attrs->end()) { if (i != v.attrs->end()) {
@ -2172,7 +2170,7 @@ std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value &
return {}; return {};
} }
BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &context, BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, NixStringContext &context,
std::string_view errorCtx, bool coerceMore, bool copyToStore, bool canonicalizePath) std::string_view errorCtx, bool coerceMore, bool copyToStore, bool canonicalizePath)
{ {
forceValue(v, pos); forceValue(v, pos);
@ -2249,7 +2247,7 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
} }
StorePath EvalState::copyPathToStore(PathSet & context, const Path & path) StorePath EvalState::copyPathToStore(NixStringContext & context, const Path & path)
{ {
if (nix::isDerivation(path)) if (nix::isDerivation(path))
error("file names are not allowed to end in '%1%'", drvExtension).debugThrow<EvalError>(); error("file names are not allowed to end in '%1%'", drvExtension).debugThrow<EvalError>();
@ -2268,12 +2266,14 @@ StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
return dstPath; return dstPath;
}(); }();
context.insert(store->printStorePath(dstPath)); context.insert(NixStringContextElem::Opaque {
.path = dstPath
});
return dstPath; return dstPath;
} }
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx) Path EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
{ {
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned(); auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (path == "" || path[0] != '/') if (path == "" || path[0] != '/')
@ -2282,7 +2282,7 @@ Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std
} }
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx) StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
{ {
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned(); auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (auto storePath = store->maybeParseStorePath(path)) if (auto storePath = store->maybeParseStorePath(path))
@ -2489,7 +2489,7 @@ void EvalState::printStats()
} }
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const std::string ExternalValueBase::coerceToString(const Pos & pos, NixStringContext & context, bool copyMore, bool copyToStore) const
{ {
throw TypeError({ throw TypeError({
.msg = hintfmt("cannot coerce %1% to a string", showType()) .msg = hintfmt("cannot coerce %1% to a string", showType())

View file

@ -56,7 +56,7 @@ void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env &
std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env); std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env);
void copyContext(const Value & v, PathSet & context); void copyContext(const Value & v, NixStringContext & context);
/** /**
@ -327,7 +327,7 @@ public:
* intended to distinguish between import-from-derivation and * intended to distinguish between import-from-derivation and
* sources stored in the actual /nix/store. * sources stored in the actual /nix/store.
*/ */
Path toRealPath(const Path & path, const PathSet & context); Path toRealPath(const Path & path, const NixStringContext & context);
/** /**
* Parse a Nix expression from the specified file. * Parse a Nix expression from the specified file.
@ -423,7 +423,7 @@ public:
*/ */
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx); void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx);
std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx); std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx); std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx);
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx); std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
[[gnu::noinline]] [[gnu::noinline]]
@ -439,7 +439,7 @@ public:
bool isDerivation(Value & v); bool isDerivation(Value & v);
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v, std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true); NixStringContext & context, bool coerceMore = false, bool copyToStore = true);
/** /**
* String coercion. * String coercion.
@ -449,12 +449,12 @@ public:
* booleans and lists to a string. If `copyToStore` is set, * booleans and lists to a string. If `copyToStore` is set,
* referenced paths are copied to the Nix store as a side effect. * referenced paths are copied to the Nix store as a side effect.
*/ */
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context, BackedStringView coerceToString(const PosIdx pos, Value & v, NixStringContext & context,
std::string_view errorCtx, std::string_view errorCtx,
bool coerceMore = false, bool copyToStore = true, bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true); bool canonicalizePath = true);
StorePath copyPathToStore(PathSet & context, const Path & path); StorePath copyPathToStore(NixStringContext & context, const Path & path);
/** /**
* Path coercion. * Path coercion.
@ -463,12 +463,12 @@ public:
* path. The result is guaranteed to be a canonicalised, absolute * path. The result is guaranteed to be a canonicalised, absolute
* path. Nothing is copied to the store. * path. Nothing is copied to the store.
*/ */
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx); Path coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx);
/** /**
* Like coerceToPath, but the result must be a store path. * Like coerceToPath, but the result must be a store path.
*/ */
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx); StorePath coerceToStorePath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx);
public: public:
@ -573,6 +573,12 @@ public:
void mkThunk_(Value & v, Expr * expr); void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, PosIdx pos); void mkPos(Value & v, PosIdx pos);
/* Create a string representing a store path.
The string is the printed store path with a context containing a single
`Opaque` element of that store path. */
void mkStorePathString(const StorePath & storePath, Value & v);
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx); void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
/** /**
@ -584,7 +590,7 @@ public:
* Realise the given context, and return a mapping from the placeholders * Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path * used to construct the associated value to their final store path
*/ */
[[nodiscard]] StringMap realiseContext(const PathSet & context); [[nodiscard]] StringMap realiseContext(const NixStringContext & context);
private: private:

View file

@ -265,7 +265,7 @@ static Flake getFlake(
state.symbols[setting.name], state.symbols[setting.name],
std::string(state.forceStringNoCtx(*setting.value, setting.pos, ""))); std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
else if (setting.value->type() == nPath) { else if (setting.value->type() == nPath) {
PathSet emptyContext = {}; NixStringContext emptyContext = {};
flake.config.settings.emplace( flake.config.settings.emplace(
state.symbols[setting.name], state.symbols[setting.name],
state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned()); state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned());

View file

@ -71,7 +71,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
{ {
if (!drvPath && attrs) { if (!drvPath && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath); Bindings::iterator i = attrs->find(state->sDrvPath);
PathSet context; NixStringContext context;
if (i == attrs->end()) if (i == attrs->end())
drvPath = {std::nullopt}; drvPath = {std::nullopt};
else else
@ -93,7 +93,7 @@ StorePath DrvInfo::queryOutPath() const
{ {
if (!outPath && attrs) { if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath); Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context; NixStringContext context;
if (i != attrs->end()) if (i != attrs->end())
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation"); outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
} }
@ -124,7 +124,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
/* And evaluate its outPath attribute. */ /* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
PathSet context; NixStringContext context;
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation")); outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
} else } else
outputs.emplace(output, std::nullopt); outputs.emplace(output, std::nullopt);

View file

@ -38,17 +38,16 @@ namespace nix {
InvalidPathError::InvalidPathError(const Path & path) : InvalidPathError::InvalidPathError(const Path & path) :
EvalError("path '%s' is not valid", path), path(path) {} EvalError("path '%s' is not valid", path), path(path) {}
StringMap EvalState::realiseContext(const PathSet & context) StringMap EvalState::realiseContext(const NixStringContext & context)
{ {
std::vector<DerivedPath::Built> drvs; std::vector<DerivedPath::Built> drvs;
StringMap res; StringMap res;
for (auto & c_ : context) { for (auto & c : context) {
auto ensureValid = [&](const StorePath & p) { auto ensureValid = [&](const StorePath & p) {
if (!store->isValidPath(p)) if (!store->isValidPath(p))
debugThrowLastTrace(InvalidPathError(store->printStorePath(p))); debugThrowLastTrace(InvalidPathError(store->printStorePath(p)));
}; };
auto c = NixStringContextElem::parse(*store, c_);
std::visit(overloaded { std::visit(overloaded {
[&](const NixStringContextElem::Built & b) { [&](const NixStringContextElem::Built & b) {
drvs.push_back(DerivedPath::Built { drvs.push_back(DerivedPath::Built {
@ -112,7 +111,7 @@ struct RealisePathFlags {
static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const RealisePathFlags flags = {}) static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const RealisePathFlags flags = {})
{ {
PathSet context; NixStringContext context;
auto path = state.coerceToPath(noPos, v, context, "while realising the context of a path"); auto path = state.coerceToPath(noPos, v, context, "while realising the context of a path");
@ -158,7 +157,12 @@ static void mkOutputString(
/* FIXME: we need to depend on the basic derivation, not /* FIXME: we need to depend on the basic derivation, not
derivation */ derivation */
: downstreamPlaceholder(*state.store, drvPath, o.first), : downstreamPlaceholder(*state.store, drvPath, o.first),
{"!" + o.first + "!" + state.store->printStorePath(drvPath)}); NixStringContext {
NixStringContextElem::Built {
.drvPath = drvPath,
.output = o.first,
}
});
} }
/* Load and evaluate an expression from path specified by the /* Load and evaluate an expression from path specified by the
@ -181,7 +185,9 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
auto storePath = *optStorePath; auto storePath = *optStorePath;
Derivation drv = state.store->readDerivation(storePath); Derivation drv = state.store->readDerivation(storePath);
auto attrs = state.buildBindings(3 + drv.outputs.size()); auto attrs = state.buildBindings(3 + drv.outputs.size());
attrs.alloc(state.sDrvPath).mkString(path, {"=" + path}); attrs.alloc(state.sDrvPath).mkString(path, {
NixStringContextElem::DrvDeep { .drvPath = storePath },
});
attrs.alloc(state.sName).mkString(drv.env["name"]); attrs.alloc(state.sName).mkString(drv.env["name"]);
auto & outputsVal = attrs.alloc(state.sOutputs); auto & outputsVal = attrs.alloc(state.sOutputs);
state.mkList(outputsVal, drv.outputs.size()); state.mkList(outputsVal, drv.outputs.size());
@ -358,7 +364,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto count = args[0]->listSize(); auto count = args[0]->listSize();
if (count == 0) if (count == 0)
state.error("at least one argument to 'exec' required").atPos(pos).debugThrow<EvalError>(); state.error("at least one argument to 'exec' required").atPos(pos).debugThrow<EvalError>();
PathSet context; NixStringContext context;
auto program = state.coerceToString(pos, *elems[0], context, auto program = state.coerceToString(pos, *elems[0], context,
"while evaluating the first element of the argument passed to builtins.exec", "while evaluating the first element of the argument passed to builtins.exec",
false, false).toOwned(); false, false).toOwned();
@ -768,7 +774,7 @@ static RegisterPrimOp primop_abort({
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.abort").toOwned(); "while evaluating the error message passed to builtins.abort").toOwned();
state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s)); state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s));
@ -787,7 +793,7 @@ static RegisterPrimOp primop_throw({
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtin.throw").toOwned(); "while evaluating the error message passed to builtin.throw").toOwned();
state.debugThrowLastTrace(ThrownError(s)); state.debugThrowLastTrace(ThrownError(s));
@ -800,7 +806,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
v = *args[1]; v = *args[1];
} catch (Error & e) { } catch (Error & e) {
PathSet context; NixStringContext context;
auto message = state.coerceToString(pos, *args[0], context, auto message = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.addErrorContext", "while evaluating the error message passed to builtins.addErrorContext",
false, false).toOwned(); false, false).toOwned();
@ -1086,7 +1092,7 @@ drvName, Bindings * attrs, Value & v)
Derivation drv; Derivation drv;
drv.name = drvName; drv.name = drvName;
PathSet context; NixStringContext context;
bool contentAddressed = false; bool contentAddressed = false;
bool isImpure = false; bool isImpure = false;
@ -1232,8 +1238,7 @@ drvName, Bindings * attrs, Value & v)
/* Everything in the context of the strings in the derivation /* Everything in the context of the strings in the derivation
attributes should be added as dependencies of the resulting attributes should be added as dependencies of the resulting
derivation. */ derivation. */
for (auto & c_ : context) { for (auto & c : context) {
auto c = NixStringContextElem::parse(*state.store, c_);
std::visit(overloaded { std::visit(overloaded {
/* Since this allows the builder to gain access to every /* Since this allows the builder to gain access to every
path in the dependency graph of the derivation (including path in the dependency graph of the derivation (including
@ -1392,7 +1397,9 @@ drvName, Bindings * attrs, Value & v)
} }
auto result = state.buildBindings(1 + drv.outputs.size()); auto result = state.buildBindings(1 + drv.outputs.size());
result.alloc(state.sDrvPath).mkString(drvPathS, {"=" + drvPathS}); result.alloc(state.sDrvPath).mkString(drvPathS, {
NixStringContextElem::DrvDeep { .drvPath = drvPath },
});
for (auto & i : drv.outputs) for (auto & i : drv.outputs)
mkOutputString(state, result, drvPath, drv, i); mkOutputString(state, result, drvPath, drv, i);
@ -1437,7 +1444,7 @@ static RegisterPrimOp primop_placeholder({
/* Convert the argument to a path. !!! obsolete? */ /* Convert the argument to a path. !!! obsolete? */
static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
Path path = state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath"); Path path = state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath");
v.mkString(canonPath(path), context); v.mkString(canonPath(path), context);
} }
@ -1468,7 +1475,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args,
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
PathSet context; NixStringContext context;
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.storePath")); Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.storePath"));
/* Resolve symlinks in path, unless path itself is a symlink /* Resolve symlinks in path, unless path itself is a symlink
directly in the store. The latter condition is necessary so directly in the store. The latter condition is necessary so
@ -1482,7 +1489,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args,
auto path2 = state.store->toStorePath(path).first; auto path2 = state.store->toStorePath(path).first;
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(path2); state.store->ensurePath(path2);
context.insert(state.store->printStorePath(path2)); context.insert(NixStringContextElem::Opaque { .path = path2 });
v.mkString(path, context); v.mkString(path, context);
} }
@ -1538,7 +1545,7 @@ static RegisterPrimOp primop_pathExists({
following the last slash. */ following the last slash. */
static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.baseNameOf", "while evaluating the first argument passed to builtins.baseNameOf",
false, false)), context); false, false)), context);
@ -1560,7 +1567,7 @@ static RegisterPrimOp primop_baseNameOf({
of the argument. */ of the argument. */
static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto path = state.coerceToString(pos, *args[0], context, auto path = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.dirOf", "while evaluating the first argument passed to builtins.dirOf",
false, false); false, false);
@ -1597,7 +1604,12 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, V
refsSink << s; refsSink << s;
refs = refsSink.getResultPaths(); refs = refsSink.getResultPaths();
} }
auto context = state.store->printStorePathSet(refs); NixStringContext context;
for (auto && p : std::move(refs)) {
context.insert(NixStringContextElem::Opaque {
.path = std::move((StorePath &&)p),
});
}
v.mkString(s, context); v.mkString(s, context);
} }
@ -1628,7 +1640,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath"); i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath");
PathSet context; NixStringContext context;
auto path = state.coerceToString(pos, *i->value, context, auto path = state.coerceToString(pos, *i->value, context,
"while evaluating the `path` attribute of an element of the list passed to builtins.findFile", "while evaluating the `path` attribute of an element of the list passed to builtins.findFile",
false, false).toOwned(); false, false).toOwned();
@ -1787,7 +1799,7 @@ static RegisterPrimOp primop_readDir({
static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
std::ostringstream out; std::ostringstream out;
PathSet context; NixStringContext context;
printValueAsXML(state, true, false, *args[0], out, context, pos); printValueAsXML(state, true, false, *args[0], out, context, pos);
v.mkString(out.str(), context); v.mkString(out.str(), context);
} }
@ -1895,7 +1907,7 @@ static RegisterPrimOp primop_toXML({
static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
std::ostringstream out; std::ostringstream out;
PathSet context; NixStringContext context;
printValueAsJSON(state, true, *args[0], pos, out, context); printValueAsJSON(state, true, *args[0], pos, out, context);
v.mkString(out.str(), context); v.mkString(out.str(), context);
} }
@ -1945,22 +1957,23 @@ static RegisterPrimOp primop_fromJSON({
as an input by derivations. */ as an input by derivations. */
static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.toFile")); std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.toFile"));
std::string contents(state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.toFile")); std::string contents(state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.toFile"));
StorePathSet refs; StorePathSet refs;
for (auto path : context) { for (auto c : context) {
if (path.at(0) != '/') if (auto p = std::get_if<NixStringContextElem::Opaque>(&c))
refs.insert(p->path);
else
state.debugThrowLastTrace(EvalError({ state.debugThrowLastTrace(EvalError({
.msg = hintfmt( .msg = hintfmt(
"in 'toFile': the file named '%1%' must not contain a reference " "in 'toFile': the file named '%1%' must not contain a reference "
"to a derivation but contains (%2%)", "to a derivation but contains (%2%)",
name, path), name, c.to_string()),
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
refs.insert(state.store->parseStorePath(path));
} }
auto storePath = settings.readOnlyMode auto storePath = settings.readOnlyMode
@ -2061,7 +2074,7 @@ static void addPath(
FileIngestionMethod method, FileIngestionMethod method,
const std::optional<Hash> expectedHash, const std::optional<Hash> expectedHash,
Value & v, Value & v,
const PathSet & context) const NixStringContext & context)
{ {
try { try {
// FIXME: handle CA derivation outputs (where path needs to // FIXME: handle CA derivation outputs (where path needs to
@ -2135,7 +2148,7 @@ static void addPath(
static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
Path path = state.coerceToPath(pos, *args[1], context, "while evaluating the second argument (the path to filter) passed to builtins.filterSource"); Path path = state.coerceToPath(pos, *args[1], context, "while evaluating the second argument (the path to filter) passed to builtins.filterSource");
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource"); state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource");
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context);
@ -2204,7 +2217,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
Value * filterFun = nullptr; Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive; auto method = FileIngestionMethod::Recursive;
std::optional<Hash> expectedHash; std::optional<Hash> expectedHash;
PathSet context; NixStringContext context;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
auto n = state.symbols[attr.name]; auto n = state.symbols[attr.name];
@ -3538,7 +3551,7 @@ static RegisterPrimOp primop_lessThan({
`"/nix/store/whatever..."'. */ `"/nix/store/whatever..."'. */
static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.toString", "while evaluating the first argument passed to builtins.toString",
true, false); true, false);
@ -3577,7 +3590,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args,
{ {
int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring"); int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring");
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring"); int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring"); auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
if (start < 0) if (start < 0)
@ -3611,7 +3624,7 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.stringLength"); auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.stringLength");
v.mkInt(s->size()); v.mkInt(s->size());
} }
@ -3637,7 +3650,7 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args,
.errPos = state.positions[pos] .errPos = state.positions[pos]
})); }));
PathSet context; // discarded NixStringContext context; // discarded
auto s = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString"); auto s = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString");
v.mkString(hashString(*ht, s).to_string(Base16, false)); v.mkString(hashString(*ht, s).to_string(Base16, false));
@ -3683,7 +3696,7 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto regex = state.regexCache->get(re); auto regex = state.regexCache->get(re);
PathSet context; NixStringContext context;
const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.match"); const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.match");
std::cmatch match; std::cmatch match;
@ -3763,7 +3776,7 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto regex = state.regexCache->get(re); auto regex = state.regexCache->get(re);
PathSet context; NixStringContext context;
const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.split"); const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.split");
auto begin = std::cregex_iterator(str.begin(), str.end(), regex); auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
@ -3860,7 +3873,7 @@ static RegisterPrimOp primop_split({
static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto sep = state.forceString(*args[0], context, pos, "while evaluating the first argument (the separator string) passed to builtins.concatStringsSep"); auto sep = state.forceString(*args[0], context, pos, "while evaluating the first argument (the separator string) passed to builtins.concatStringsSep");
state.forceList(*args[1], pos, "while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"); state.forceList(*args[1], pos, "while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep");
@ -3900,15 +3913,15 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
for (auto elem : args[0]->listItems()) for (auto elem : args[0]->listItems())
from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings")); from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings"));
std::vector<std::pair<std::string, PathSet>> to; std::vector<std::pair<std::string, NixStringContext>> to;
to.reserve(args[1]->listSize()); to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
PathSet ctx; NixStringContext ctx;
auto s = state.forceString(*elem, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings"); auto s = state.forceString(*elem, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings");
to.emplace_back(s, std::move(ctx)); to.emplace_back(s, std::move(ctx));
} }
PathSet context; NixStringContext context;
auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings"); auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings");
std::string res; std::string res;

View file

@ -7,7 +7,7 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext"); auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
v.mkString(*s); v.mkString(*s);
} }
@ -17,7 +17,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext"); state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
v.mkBool(!context.empty()); v.mkBool(!context.empty());
} }
@ -33,17 +33,18 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
drv.inputDrvs. */ drv.inputDrvs. */
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
PathSet context2; NixStringContext context2;
for (auto && p : context) { for (auto && c : context) {
auto c = NixStringContextElem::parse(*state.store, p);
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c)) { if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c)) {
context2.emplace(state.store->printStorePath(ptr->drvPath)); context2.emplace(NixStringContextElem::Opaque {
.path = ptr->drvPath
});
} else { } else {
/* Can reuse original item */ /* Can reuse original item */
context2.emplace(std::move(p)); context2.emplace(std::move(c));
} }
} }
@ -79,22 +80,21 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
bool allOutputs = false; bool allOutputs = false;
Strings outputs; Strings outputs;
}; };
PathSet context; NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext"); state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
auto contextInfos = std::map<StorePath, ContextInfo>(); auto contextInfos = std::map<StorePath, ContextInfo>();
for (const auto & p : context) { for (auto && i : context) {
NixStringContextElem ctx = NixStringContextElem::parse(*state.store, p);
std::visit(overloaded { std::visit(overloaded {
[&](NixStringContextElem::DrvDeep & d) { [&](NixStringContextElem::DrvDeep && d) {
contextInfos[d.drvPath].allOutputs = true; contextInfos[std::move(d.drvPath)].allOutputs = true;
}, },
[&](NixStringContextElem::Built & b) { [&](NixStringContextElem::Built && b) {
contextInfos[b.drvPath].outputs.emplace_back(std::move(b.output)); contextInfos[std::move(b.drvPath)].outputs.emplace_back(std::move(b.output));
}, },
[&](NixStringContextElem::Opaque & o) { [&](NixStringContextElem::Opaque && o) {
contextInfos[o.path].path = true; contextInfos[std::move(o.path)].path = true;
}, },
}, ctx.raw()); }, ((NixStringContextElem &&) i).raw());
} }
auto attrs = state.buildBindings(contextInfos.size()); auto attrs = state.buildBindings(contextInfos.size());
@ -129,7 +129,7 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
*/ */
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; NixStringContext context;
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext"); auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
@ -143,13 +143,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
.msg = hintfmt("context key '%s' is not a store path", name), .msg = hintfmt("context key '%s' is not a store path", name),
.errPos = state.positions[i.pos] .errPos = state.positions[i.pos]
}); });
auto namePath = state.store->parseStorePath(name);
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(name)); state.store->ensurePath(namePath);
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context"); state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
auto iter = i.value->attrs->find(sPath); auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context")) if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context"))
context.emplace(name); context.emplace(NixStringContextElem::Opaque {
.path = namePath,
});
} }
iter = i.value->attrs->find(sAllOutputs); iter = i.value->attrs->find(sAllOutputs);
@ -161,7 +164,9 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
.errPos = state.positions[i.pos] .errPos = state.positions[i.pos]
}); });
} }
context.insert(concatStrings("=", name)); context.emplace(NixStringContextElem::DrvDeep {
.drvPath = namePath,
});
} }
} }
@ -176,7 +181,10 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
} }
for (auto elem : iter->value->listItems()) { for (auto elem : iter->value->listItems()) {
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context"); auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context");
context.insert(concatStrings("!", outputName, "!", name)); context.emplace(NixStringContextElem::Built {
.drvPath = namePath,
.output = std::string { outputName },
});
} }
} }
} }

View file

@ -18,7 +18,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
const auto & attrName = state.symbols[attr.name]; const auto & attrName = state.symbols[attr.name];
if (attrName == "fromPath") { if (attrName == "fromPath") {
PathSet context; NixStringContext context;
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context, fromPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"while evaluating the 'fromPath' attribute passed to builtins.fetchClosure"); "while evaluating the 'fromPath' attribute passed to builtins.fetchClosure");
} }
@ -27,7 +27,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
toCA = true; toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) { if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
PathSet context; NixStringContext context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, toPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"while evaluating the 'toPath' attribute passed to builtins.fetchClosure"); "while evaluating the 'toPath' attribute passed to builtins.fetchClosure");
} }
@ -114,8 +114,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
}); });
} }
auto toPathS = state.store->printStorePath(*toPath); state.mkStorePathString(*toPath, v);
v.mkString(toPathS, {toPathS});
} }
static RegisterPrimOp primop_fetchClosure({ static RegisterPrimOp primop_fetchClosure({

View file

@ -13,7 +13,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
std::optional<Hash> rev; std::optional<Hash> rev;
std::optional<std::string> ref; std::optional<std::string> ref;
std::string_view name = "source"; std::string_view name = "source";
PathSet context; NixStringContext context;
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
@ -73,8 +73,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
auto [tree, input2] = input.fetch(state.store); auto [tree, input2] = input.fetch(state.store);
auto attrs2 = state.buildBindings(8); auto attrs2 = state.buildBindings(8);
auto storePath = state.store->printStorePath(tree.storePath); state.mkStorePathString(tree.storePath, attrs2.alloc(state.sOutPath));
attrs2.alloc(state.sOutPath).mkString(storePath, {storePath});
if (input2.getRef()) if (input2.getRef())
attrs2.alloc("branch").mkString(*input2.getRef()); attrs2.alloc("branch").mkString(*input2.getRef());
// Backward compatibility: set 'rev' to // Backward compatibility: set 'rev' to

View file

@ -24,9 +24,8 @@ void emitTreeAttrs(
auto attrs = state.buildBindings(8); auto attrs = state.buildBindings(8);
auto storePath = state.store->printStorePath(tree.storePath);
attrs.alloc(state.sOutPath).mkString(storePath, {storePath}); state.mkStorePathString(tree.storePath, attrs.alloc(state.sOutPath));
// FIXME: support arbitrary input attributes. // FIXME: support arbitrary input attributes.
@ -107,7 +106,7 @@ static void fetchTree(
const FetchTreeParams & params = FetchTreeParams{} const FetchTreeParams & params = FetchTreeParams{}
) { ) {
fetchers::Input input; fetchers::Input input;
PathSet context; NixStringContext context;
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);

View file

@ -8,7 +8,7 @@ namespace nix {
protected: protected:
std::string getJSONValue(Value& value) { std::string getJSONValue(Value& value) {
std::stringstream ss; std::stringstream ss;
PathSet ps; NixStringContext ps;
printValueAsJSON(state, true, value, noPos, ss, ps); printValueAsJSON(state, true, value, noPos, ss, ps);
return ss.str(); return ss.str();
} }

View file

@ -8,69 +8,62 @@
namespace nix { namespace nix {
// Testing of trivial expressions TEST(NixStringContextElemTest, empty_invalid) {
struct NixStringContextElemTest : public LibExprTest {
const Store & store() const {
return *LibExprTest::store;
}
};
TEST_F(NixStringContextElemTest, empty_invalid) {
EXPECT_THROW( EXPECT_THROW(
NixStringContextElem::parse(store(), ""), NixStringContextElem::parse(""),
BadNixStringContextElem); BadNixStringContextElem);
} }
TEST_F(NixStringContextElemTest, single_bang_invalid) { TEST(NixStringContextElemTest, single_bang_invalid) {
EXPECT_THROW( EXPECT_THROW(
NixStringContextElem::parse(store(), "!"), NixStringContextElem::parse("!"),
BadNixStringContextElem); BadNixStringContextElem);
} }
TEST_F(NixStringContextElemTest, double_bang_invalid) { TEST(NixStringContextElemTest, double_bang_invalid) {
EXPECT_THROW( EXPECT_THROW(
NixStringContextElem::parse(store(), "!!/"), NixStringContextElem::parse("!!/"),
BadStorePath); BadStorePath);
} }
TEST_F(NixStringContextElemTest, eq_slash_invalid) { TEST(NixStringContextElemTest, eq_slash_invalid) {
EXPECT_THROW( EXPECT_THROW(
NixStringContextElem::parse(store(), "=/"), NixStringContextElem::parse("=/"),
BadStorePath); BadStorePath);
} }
TEST_F(NixStringContextElemTest, slash_invalid) { TEST(NixStringContextElemTest, slash_invalid) {
EXPECT_THROW( EXPECT_THROW(
NixStringContextElem::parse(store(), "/"), NixStringContextElem::parse("/"),
BadStorePath); BadStorePath);
} }
TEST_F(NixStringContextElemTest, opaque) { TEST(NixStringContextElemTest, opaque) {
std::string_view opaque = "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x"; std::string_view opaque = "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
auto elem = NixStringContextElem::parse(store(), opaque); auto elem = NixStringContextElem::parse(opaque);
auto * p = std::get_if<NixStringContextElem::Opaque>(&elem); auto * p = std::get_if<NixStringContextElem::Opaque>(&elem);
ASSERT_TRUE(p); ASSERT_TRUE(p);
ASSERT_EQ(p->path, store().parseStorePath(opaque)); ASSERT_EQ(p->path, StorePath { opaque });
ASSERT_EQ(elem.to_string(store()), opaque); ASSERT_EQ(elem.to_string(), opaque);
} }
TEST_F(NixStringContextElemTest, drvDeep) { TEST(NixStringContextElemTest, drvDeep) {
std::string_view drvDeep = "=/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"; std::string_view drvDeep = "=g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
auto elem = NixStringContextElem::parse(store(), drvDeep); auto elem = NixStringContextElem::parse(drvDeep);
auto * p = std::get_if<NixStringContextElem::DrvDeep>(&elem); auto * p = std::get_if<NixStringContextElem::DrvDeep>(&elem);
ASSERT_TRUE(p); ASSERT_TRUE(p);
ASSERT_EQ(p->drvPath, store().parseStorePath(drvDeep.substr(1))); ASSERT_EQ(p->drvPath, StorePath { drvDeep.substr(1) });
ASSERT_EQ(elem.to_string(store()), drvDeep); ASSERT_EQ(elem.to_string(), drvDeep);
} }
TEST_F(NixStringContextElemTest, built) { TEST(NixStringContextElemTest, built) {
std::string_view built = "!foo!/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"; std::string_view built = "!foo!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
auto elem = NixStringContextElem::parse(store(), built); auto elem = NixStringContextElem::parse(built);
auto * p = std::get_if<NixStringContextElem::Built>(&elem); auto * p = std::get_if<NixStringContextElem::Built>(&elem);
ASSERT_TRUE(p); ASSERT_TRUE(p);
ASSERT_EQ(p->output, "foo"); ASSERT_EQ(p->output, "foo");
ASSERT_EQ(p->drvPath, store().parseStorePath(built.substr(5))); ASSERT_EQ(p->drvPath, StorePath { built.substr(5) });
ASSERT_EQ(elem.to_string(store()), built); ASSERT_EQ(elem.to_string(), built);
} }
} }
@ -116,12 +109,12 @@ Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
namespace nix { namespace nix {
RC_GTEST_FIXTURE_PROP( RC_GTEST_PROP(
NixStringContextElemTest, NixStringContextElemTest,
prop_round_rip, prop_round_rip,
(const NixStringContextElem & o)) (const NixStringContextElem & o))
{ {
RC_ASSERT(o == NixStringContextElem::parse(store(), o.to_string(store()))); RC_ASSERT(o == NixStringContextElem::parse(o.to_string()));
} }
} }

View file

@ -11,7 +11,7 @@
namespace nix { namespace nix {
using json = nlohmann::json; using json = nlohmann::json;
json printValueAsJSON(EvalState & state, bool strict, json printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, PathSet & context, bool copyToStore) Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore)
{ {
checkInterrupt(); checkInterrupt();
@ -94,13 +94,13 @@ json printValueAsJSON(EvalState & state, bool strict,
} }
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, std::ostream & str, PathSet & context, bool copyToStore) Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore)
{ {
str << printValueAsJSON(state, strict, v, pos, context, copyToStore); str << printValueAsJSON(state, strict, v, pos, context, copyToStore);
} }
json ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, json ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
PathSet & context, bool copyToStore) const NixStringContext & context, bool copyToStore) const
{ {
state.debugThrowLastTrace(TypeError("cannot convert %1% to JSON", showType())); state.debugThrowLastTrace(TypeError("cannot convert %1% to JSON", showType()));
} }

View file

@ -11,9 +11,9 @@
namespace nix { namespace nix {
nlohmann::json printValueAsJSON(EvalState & state, bool strict, nlohmann::json printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, PathSet & context, bool copyToStore = true); Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore = true);
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, std::ostream & str, PathSet & context, bool copyToStore = true); Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore = true);
} }

View file

@ -18,7 +18,7 @@ static XMLAttrs singletonAttrs(const std::string & name, const std::string & val
static void printValueAsXML(EvalState & state, bool strict, bool location, static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, Value & v, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
const PosIdx pos); const PosIdx pos);
@ -32,7 +32,7 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
static void showAttrs(EvalState & state, bool strict, bool location, static void showAttrs(EvalState & state, bool strict, bool location,
Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen)
{ {
StringSet names; StringSet names;
@ -54,7 +54,7 @@ static void showAttrs(EvalState & state, bool strict, bool location,
static void printValueAsXML(EvalState & state, bool strict, bool location, static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, Value & v, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
const PosIdx pos) const PosIdx pos)
{ {
checkInterrupt(); checkInterrupt();
@ -166,7 +166,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, bool location, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
const PosIdx pos) const const PosIdx pos) const
{ {
doc.writeEmptyElement("unevaluated"); doc.writeEmptyElement("unevaluated");
@ -174,7 +174,7 @@ void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
void printValueAsXML(EvalState & state, bool strict, bool location, void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context, const PosIdx pos) Value & v, std::ostream & out, NixStringContext & context, const PosIdx pos)
{ {
XMLWriter doc(true, out); XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr"); XMLOpenElement root(doc, "expr");

View file

@ -10,6 +10,6 @@
namespace nix { namespace nix {
void printValueAsXML(EvalState & state, bool strict, bool location, void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context, const PosIdx pos); Value & v, std::ostream & out, NixStringContext & context, const PosIdx pos);
} }

View file

@ -100,7 +100,7 @@ class ExternalValueBase
* Coerce the value to a string. Defaults to uncoercable, i.e. throws an * Coerce the value to a string. Defaults to uncoercable, i.e. throws an
* error. * error.
*/ */
virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const; virtual std::string coerceToString(const Pos & pos, NixStringContext & context, bool copyMore, bool copyToStore) const;
/** /**
* Compare to another value of the same type. Defaults to uncomparable, * Compare to another value of the same type. Defaults to uncomparable,
@ -112,13 +112,13 @@ class ExternalValueBase
* Print the value as JSON. Defaults to unconvertable, i.e. throws an error * Print the value as JSON. Defaults to unconvertable, i.e. throws an error
*/ */
virtual nlohmann::json printValueAsJSON(EvalState & state, bool strict, virtual nlohmann::json printValueAsJSON(EvalState & state, bool strict,
PathSet & context, bool copyToStore = true) const; NixStringContext & context, bool copyToStore = true) const;
/** /**
* Print the value as XML. Defaults to unevaluated * Print the value as XML. Defaults to unevaluated
*/ */
virtual void printValueAsXML(EvalState & state, bool strict, bool location, virtual void printValueAsXML(EvalState & state, bool strict, bool location,
XMLWriter & doc, PathSet & context, PathSet & drvsSeen, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
const PosIdx pos) const; const PosIdx pos) const;
virtual ~ExternalValueBase() virtual ~ExternalValueBase()
@ -268,9 +268,9 @@ public:
void mkString(std::string_view s); void mkString(std::string_view s);
void mkString(std::string_view s, const PathSet & context); void mkString(std::string_view s, const NixStringContext & context);
void mkStringMove(const char * s, const PathSet & context); void mkStringMove(const char * s, const NixStringContext & context);
inline void mkPath(const char * s) inline void mkPath(const char * s)
{ {
@ -394,8 +394,6 @@ public:
*/ */
bool isTrivial() const; bool isTrivial() const;
NixStringContext getContext(const Store &);
auto listItems() auto listItems()
{ {
struct ListIterable struct ListIterable

View file

@ -1,11 +1,10 @@
#include "value/context.hh" #include "value/context.hh"
#include "store-api.hh"
#include <optional> #include <optional>
namespace nix { namespace nix {
NixStringContextElem NixStringContextElem::parse(const Store & store, std::string_view s0) NixStringContextElem NixStringContextElem::parse(std::string_view s0)
{ {
std::string_view s = s0; std::string_view s = s0;
@ -25,41 +24,41 @@ NixStringContextElem NixStringContextElem::parse(const Store & store, std::strin
"String content element beginning with '!' should have a second '!'"); "String content element beginning with '!' should have a second '!'");
} }
return NixStringContextElem::Built { return NixStringContextElem::Built {
.drvPath = store.parseStorePath(s.substr(index + 1)), .drvPath = StorePath { s.substr(index + 1) },
.output = std::string(s.substr(0, index)), .output = std::string(s.substr(0, index)),
}; };
} }
case '=': { case '=': {
return NixStringContextElem::DrvDeep { return NixStringContextElem::DrvDeep {
.drvPath = store.parseStorePath(s.substr(1)), .drvPath = StorePath { s.substr(1) },
}; };
} }
default: { default: {
return NixStringContextElem::Opaque { return NixStringContextElem::Opaque {
.path = store.parseStorePath(s), .path = StorePath { s },
}; };
} }
} }
} }
std::string NixStringContextElem::to_string(const Store & store) const { std::string NixStringContextElem::to_string() const {
return std::visit(overloaded { return std::visit(overloaded {
[&](const NixStringContextElem::Built & b) { [&](const NixStringContextElem::Built & b) {
std::string res; std::string res;
res += '!'; res += '!';
res += b.output; res += b.output;
res += '!'; res += '!';
res += store.printStorePath(b.drvPath); res += b.drvPath.to_string();
return res; return res;
}, },
[&](const NixStringContextElem::DrvDeep & d) { [&](const NixStringContextElem::DrvDeep & d) {
std::string res; std::string res;
res += '='; res += '=';
res += store.printStorePath(d.drvPath); res += d.drvPath.to_string();
return res; return res;
}, },
[&](const NixStringContextElem::Opaque & o) { [&](const NixStringContextElem::Opaque & o) {
return store.printStorePath(o.path); return std::string { o.path.to_string() };
}, },
}, raw()); }, raw());
} }

View file

@ -26,8 +26,6 @@ public:
} }
}; };
class Store;
/** /**
* Plain opaque path to some store object. * Plain opaque path to some store object.
* *
@ -80,12 +78,15 @@ struct NixStringContextElem : _NixStringContextElem_Raw {
using DrvDeep = NixStringContextElem_DrvDeep; using DrvDeep = NixStringContextElem_DrvDeep;
using Built = NixStringContextElem_Built; using Built = NixStringContextElem_Built;
inline const Raw & raw() const { inline const Raw & raw() const & {
return static_cast<const Raw &>(*this); return static_cast<const Raw &>(*this);
} }
inline Raw & raw() { inline Raw & raw() & {
return static_cast<Raw &>(*this); return static_cast<Raw &>(*this);
} }
inline Raw && raw() && {
return static_cast<Raw &&>(*this);
}
/** /**
* Decode a context string, one of: * Decode a context string, one of:
@ -93,10 +94,10 @@ struct NixStringContextElem : _NixStringContextElem_Raw {
* - =<path> * - =<path>
* - !<name>!<path> * - !<name>!<path>
*/ */
static NixStringContextElem parse(const Store & store, std::string_view s); static NixStringContextElem parse(std::string_view s);
std::string to_string(const Store & store) const; std::string to_string() const;
}; };
typedef std::vector<NixStringContextElem> NixStringContext; typedef std::set<NixStringContextElem> NixStringContext;
} }

View file

@ -960,7 +960,7 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin
printError("derivation '%s' has invalid meta attribute '%s'", i.queryName(), j); printError("derivation '%s' has invalid meta attribute '%s'", i.queryName(), j);
metaObj[j] = nullptr; metaObj[j] = nullptr;
} else { } else {
PathSet context; NixStringContext context;
metaObj[j] = printValueAsJSON(*globals.state, true, *v, noPos, context); metaObj[j] = printValueAsJSON(*globals.state, true, *v, noPos, context);
} }
} }

View file

@ -119,9 +119,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Construct a Nix expression that calls the user environment /* Construct a Nix expression that calls the user environment
builder with the manifest as argument. */ builder with the manifest as argument. */
auto attrs = state.buildBindings(3); auto attrs = state.buildBindings(3);
attrs.alloc("manifest").mkString( state.mkStorePathString(manifestFile, attrs.alloc("manifest"));
state.store->printStorePath(manifestFile),
{state.store->printStorePath(manifestFile)});
attrs.insert(state.symbols.create("derivations"), &manifest); attrs.insert(state.symbols.create("derivations"), &manifest);
Value args; Value args;
args.mkAttrs(attrs); args.mkAttrs(attrs);
@ -132,7 +130,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Evaluate it. */ /* Evaluate it. */
debug("evaluating user environment builder"); debug("evaluating user environment builder");
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); }); state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
PathSet context; NixStringContext context;
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, ""); auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));

View file

@ -43,7 +43,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first); Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first);
state.forceValue(v, [&]() { return v.determinePos(noPos); }); state.forceValue(v, [&]() { return v.determinePos(noPos); });
PathSet context; NixStringContext context;
if (evalOnly) { if (evalOnly) {
Value vRes; Value vRes;
if (autoArgs.empty()) if (autoArgs.empty())

View file

@ -98,7 +98,7 @@ struct CmdBundle : InstallableValueCommand
if (!attr1) if (!attr1)
throw Error("the bundler '%s' does not produce a derivation", bundler.what()); throw Error("the bundler '%s' does not produce a derivation", bundler.what());
PathSet context2; NixStringContext context2;
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, ""); auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, "");
auto attr2 = vRes->attrs->get(evalState->sOutPath); auto attr2 = vRes->attrs->get(evalState->sOutPath);

View file

@ -62,7 +62,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
auto state = getEvalState(); auto state = getEvalState();
auto [v, pos] = installable->toValue(*state); auto [v, pos] = installable->toValue(*state);
PathSet context; NixStringContext context;
if (apply) { if (apply) {
auto vApply = state->allocValue(); auto vApply = state->allocValue();

View file

@ -438,7 +438,7 @@ struct CmdFlakeCheck : FlakeCommand
if (auto attr = v.attrs->get(state->symbols.create("path"))) { if (auto attr = v.attrs->get(state->symbols.create("path"))) {
if (attr->name == state->symbols.create("path")) { if (attr->name == state->symbols.create("path")) {
PathSet context; NixStringContext context;
auto path = state->coerceToPath(attr->pos, *attr->value, context, ""); auto path = state->coerceToPath(attr->pos, *attr->value, context, "");
if (!store->isInStore(path)) if (!store->isInStore(path))
throw Error("template '%s' has a bad 'path' attribute"); throw Error("template '%s' has a bad 'path' attribute");