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();
if (v.type() == nPath) {
PathSet context;
NixStringContext context;
auto storePath = state->copyPathToStore(context, Path(v.path));
return {{
.path = DerivedPath::Opaque {
@ -107,10 +107,10 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
}
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 storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) {
if (storePath && context.count(NixStringContextElem::Opaque { .path = *storePath })) {
return {{
.path = DerivedPath::Opaque {
.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> {
if (v.type() == nPath || v.type() == nString) {
PathSet context;
NixStringContext context;
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
return {path, 0};
} else if (v.isLambda()) {
@ -940,7 +940,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
if (isDrv) {
str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context;
NixStringContext context;
if (i != v.attrs->end())
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
else

View file

@ -47,7 +47,7 @@ struct AttrDb
{
auto state(_state->lock());
Path cacheDir = getCacheDir() + "/nix/eval-cache-v4";
Path cacheDir = getCacheDir() + "/nix/eval-cache-v5";
createDirs(cacheDir);
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
@ -300,7 +300,7 @@ struct AttrDb
NixStringContext context;
if (!queryAttribute.isNull(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}}};
}
case AttrType::Bool:
@ -619,9 +619,11 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue();
if (v.type() == nString)
return {v.string.s, v.getContext(*root->state.store)};
else if (v.type() == nPath)
if (v.type() == nString) {
NixStringContext context;
copyContext(v, context);
return {v.string.s, std::move(context)};
} else if (v.type() == nPath)
return {v.path, {}};
else
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);
auto path = store->printStorePath(storePath);
v.mkString(path, PathSet({path}));
mkStorePathString(storePath, v);
}
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'.
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()) {
size_t n = 0;
v.string.context = (const char * *)
allocBytes((context.size() + 1) * sizeof(char *));
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;
}
}
void Value::mkString(std::string_view s, const PathSet & context)
void Value::mkString(std::string_view s, const NixStringContext & context)
{
mkString(s);
copyContextToValue(*this, context);
}
void Value::mkStringMove(const char * s, const PathSet & context)
void Value::mkStringMove(const char * s, const NixStringContext & context)
{
mkString(s);
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
in the given environment. But if the expression is a variable,
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)
{
PathSet context;
NixStringContext context;
std::vector<BackedStringView> s;
size_t sSize = 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)
for (const char * * p = v.string.context; *p; ++p)
context.insert(*p);
context.insert(NixStringContextElem::parse(*p));
}
NixStringContext Value::getContext(const Store & store)
{
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)
std::string_view EvalState::forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx)
{
auto s = forceString(v, pos, errorCtx);
copyContext(v, context);
@ -2158,7 +2156,7 @@ bool EvalState::isDerivation(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);
if (i != v.attrs->end()) {
@ -2172,7 +2170,7 @@ std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value &
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)
{
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))
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;
}();
context.insert(store->printStorePath(dstPath));
context.insert(NixStringContextElem::Opaque {
.path = 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();
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();
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({
.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);
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
* 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.
@ -423,7 +423,7 @@ public:
*/
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, 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);
[[gnu::noinline]]
@ -439,7 +439,7 @@ public:
bool isDerivation(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.
@ -449,12 +449,12 @@ public:
* booleans and lists to a string. If `copyToStore` is set,
* 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,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
StorePath copyPathToStore(PathSet & context, const Path & path);
StorePath copyPathToStore(NixStringContext & context, const Path & path);
/**
* Path coercion.
@ -463,12 +463,12 @@ public:
* path. The result is guaranteed to be a canonicalised, absolute
* 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.
*/
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:
@ -573,6 +573,12 @@ public:
void mkThunk_(Value & v, Expr * expr);
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);
/**
@ -584,7 +590,7 @@ public:
* Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path
*/
[[nodiscard]] StringMap realiseContext(const PathSet & context);
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
private:

View file

@ -265,7 +265,7 @@ static Flake getFlake(
state.symbols[setting.name],
std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
NixStringContext emptyContext = {};
flake.config.settings.emplace(
state.symbols[setting.name],
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) {
Bindings::iterator i = attrs->find(state->sDrvPath);
PathSet context;
NixStringContext context;
if (i == attrs->end())
drvPath = {std::nullopt};
else
@ -93,7 +93,7 @@ StorePath DrvInfo::queryOutPath() const
{
if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context;
NixStringContext context;
if (i != attrs->end())
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. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
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"));
} else
outputs.emplace(output, std::nullopt);

View file

@ -38,17 +38,16 @@ namespace nix {
InvalidPathError::InvalidPathError(const 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;
StringMap res;
for (auto & c_ : context) {
for (auto & c : context) {
auto ensureValid = [&](const StorePath & p) {
if (!store->isValidPath(p))
debugThrowLastTrace(InvalidPathError(store->printStorePath(p)));
};
auto c = NixStringContextElem::parse(*store, c_);
std::visit(overloaded {
[&](const NixStringContextElem::Built & b) {
drvs.push_back(DerivedPath::Built {
@ -112,7 +111,7 @@ struct RealisePathFlags {
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");
@ -158,7 +157,12 @@ static void mkOutputString(
/* FIXME: we need to depend on the basic derivation, not
derivation */
: 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
@ -181,7 +185,9 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
auto storePath = *optStorePath;
Derivation drv = state.store->readDerivation(storePath);
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"]);
auto & outputsVal = attrs.alloc(state.sOutputs);
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();
if (count == 0)
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,
"while evaluating the first element of the argument passed to builtins.exec",
false, false).toOwned();
@ -768,7 +774,7 @@ static RegisterPrimOp primop_abort({
)",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.abort").toOwned();
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)
{
PathSet context;
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtin.throw").toOwned();
state.debugThrowLastTrace(ThrownError(s));
@ -800,7 +806,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
state.forceValue(*args[1], pos);
v = *args[1];
} catch (Error & e) {
PathSet context;
NixStringContext context;
auto message = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.addErrorContext",
false, false).toOwned();
@ -1086,7 +1092,7 @@ drvName, Bindings * attrs, Value & v)
Derivation drv;
drv.name = drvName;
PathSet context;
NixStringContext context;
bool contentAddressed = false;
bool isImpure = false;
@ -1232,8 +1238,7 @@ drvName, Bindings * attrs, Value & v)
/* Everything in the context of the strings in the derivation
attributes should be added as dependencies of the resulting
derivation. */
for (auto & c_ : context) {
auto c = NixStringContextElem::parse(*state.store, c_);
for (auto & c : context) {
std::visit(overloaded {
/* Since this allows the builder to gain access to every
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());
result.alloc(state.sDrvPath).mkString(drvPathS, {"=" + drvPathS});
result.alloc(state.sDrvPath).mkString(drvPathS, {
NixStringContextElem::DrvDeep { .drvPath = drvPath },
});
for (auto & i : drv.outputs)
mkOutputString(state, result, drvPath, drv, i);
@ -1437,7 +1444,7 @@ static RegisterPrimOp primop_placeholder({
/* Convert the argument to a path. !!! obsolete? */
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");
v.mkString(canonPath(path), context);
}
@ -1468,7 +1475,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args,
.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"));
/* Resolve symlinks in path, unless path itself is a symlink
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;
if (!settings.readOnlyMode)
state.store->ensurePath(path2);
context.insert(state.store->printStorePath(path2));
context.insert(NixStringContextElem::Opaque { .path = path2 });
v.mkString(path, context);
}
@ -1538,7 +1545,7 @@ static RegisterPrimOp primop_pathExists({
following the last slash. */
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,
"while evaluating the first argument passed to builtins.baseNameOf",
false, false)), context);
@ -1560,7 +1567,7 @@ static RegisterPrimOp primop_baseNameOf({
of the argument. */
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,
"while evaluating the first argument passed to builtins.dirOf",
false, false);
@ -1597,7 +1604,12 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, V
refsSink << s;
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);
}
@ -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");
PathSet context;
NixStringContext context;
auto path = state.coerceToString(pos, *i->value, context,
"while evaluating the `path` attribute of an element of the list passed to builtins.findFile",
false, false).toOwned();
@ -1787,7 +1799,7 @@ static RegisterPrimOp primop_readDir({
static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::ostringstream out;
PathSet context;
NixStringContext context;
printValueAsXML(state, true, false, *args[0], out, context, pos);
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)
{
std::ostringstream out;
PathSet context;
NixStringContext context;
printValueAsJSON(state, true, *args[0], pos, out, context);
v.mkString(out.str(), context);
}
@ -1945,22 +1957,23 @@ static RegisterPrimOp primop_fromJSON({
as an input by derivations. */
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 contents(state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.toFile"));
StorePathSet refs;
for (auto path : context) {
if (path.at(0) != '/')
for (auto c : context) {
if (auto p = std::get_if<NixStringContextElem::Opaque>(&c))
refs.insert(p->path);
else
state.debugThrowLastTrace(EvalError({
.msg = hintfmt(
"in 'toFile': the file named '%1%' must not contain a reference "
"to a derivation but contains (%2%)",
name, path),
name, c.to_string()),
.errPos = state.positions[pos]
}));
refs.insert(state.store->parseStorePath(path));
}
auto storePath = settings.readOnlyMode
@ -2061,7 +2074,7 @@ static void addPath(
FileIngestionMethod method,
const std::optional<Hash> expectedHash,
Value & v,
const PathSet & context)
const NixStringContext & context)
{
try {
// 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)
{
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");
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);
@ -2204,7 +2217,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive;
std::optional<Hash> expectedHash;
PathSet context;
NixStringContext context;
for (auto & attr : *args[0]->attrs) {
auto n = state.symbols[attr.name];
@ -3538,7 +3551,7 @@ static RegisterPrimOp primop_lessThan({
`"/nix/store/whatever..."'. */
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,
"while evaluating the first argument passed to builtins.toString",
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 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");
if (start < 0)
@ -3611,7 +3624,7 @@ static RegisterPrimOp primop_substring({
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");
v.mkInt(s->size());
}
@ -3637,7 +3650,7 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args,
.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");
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);
PathSet context;
NixStringContext context;
const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.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);
PathSet context;
NixStringContext context;
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);
@ -3860,7 +3873,7 @@ static RegisterPrimOp primop_split({
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");
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())
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());
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");
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");
std::string res;

View file

@ -7,7 +7,7 @@ namespace nix {
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");
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)
{
PathSet context;
NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
v.mkBool(!context.empty());
}
@ -33,17 +33,18 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
drv.inputDrvs. */
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");
PathSet context2;
for (auto && p : context) {
auto c = NixStringContextElem::parse(*state.store, p);
NixStringContext context2;
for (auto && c : context) {
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c)) {
context2.emplace(state.store->printStorePath(ptr->drvPath));
context2.emplace(NixStringContextElem::Opaque {
.path = ptr->drvPath
});
} else {
/* 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;
Strings outputs;
};
PathSet context;
NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
auto contextInfos = std::map<StorePath, ContextInfo>();
for (const auto & p : context) {
NixStringContextElem ctx = NixStringContextElem::parse(*state.store, p);
for (auto && i : context) {
std::visit(overloaded {
[&](NixStringContextElem::DrvDeep & d) {
contextInfos[d.drvPath].allOutputs = true;
[&](NixStringContextElem::DrvDeep && d) {
contextInfos[std::move(d.drvPath)].allOutputs = true;
},
[&](NixStringContextElem::Built & b) {
contextInfos[b.drvPath].outputs.emplace_back(std::move(b.output));
[&](NixStringContextElem::Built && b) {
contextInfos[std::move(b.drvPath)].outputs.emplace_back(std::move(b.output));
},
[&](NixStringContextElem::Opaque & o) {
contextInfos[o.path].path = true;
[&](NixStringContextElem::Opaque && o) {
contextInfos[std::move(o.path)].path = true;
},
}, ctx.raw());
}, ((NixStringContextElem &&) i).raw());
}
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)
{
PathSet context;
NixStringContext context;
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");
@ -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),
.errPos = state.positions[i.pos]
});
auto namePath = state.store->parseStorePath(name);
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");
auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) {
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);
@ -161,7 +164,9 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
.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()) {
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];
if (attrName == "fromPath") {
PathSet context;
NixStringContext context;
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"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);
toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
PathSet context;
NixStringContext context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"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);
v.mkString(toPathS, {toPathS});
state.mkStorePathString(*toPath, v);
}
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<std::string> ref;
std::string_view name = "source";
PathSet context;
NixStringContext context;
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 attrs2 = state.buildBindings(8);
auto storePath = state.store->printStorePath(tree.storePath);
attrs2.alloc(state.sOutPath).mkString(storePath, {storePath});
state.mkStorePathString(tree.storePath, attrs2.alloc(state.sOutPath));
if (input2.getRef())
attrs2.alloc("branch").mkString(*input2.getRef());
// Backward compatibility: set 'rev' to

View file

@ -24,9 +24,8 @@ void emitTreeAttrs(
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.
@ -107,7 +106,7 @@ static void fetchTree(
const FetchTreeParams & params = FetchTreeParams{}
) {
fetchers::Input input;
PathSet context;
NixStringContext context;
state.forceValue(*args[0], pos);

View file

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

View file

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

View file

@ -11,9 +11,9 @@
namespace nix {
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,
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,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
Value & v, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
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,
Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen)
{
StringSet names;
@ -54,7 +54,7 @@ static void showAttrs(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)
{
checkInterrupt();
@ -166,7 +166,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
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
{
doc.writeEmptyElement("unevaluated");
@ -174,7 +174,7 @@ void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
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);
XMLOpenElement root(doc, "expr");

View file

@ -10,6 +10,6 @@
namespace nix {
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
* 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,
@ -112,13 +112,13 @@ class ExternalValueBase
* Print the value as JSON. Defaults to unconvertable, i.e. throws an error
*/
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
*/
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;
virtual ~ExternalValueBase()
@ -268,9 +268,9 @@ public:
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)
{
@ -394,8 +394,6 @@ public:
*/
bool isTrivial() const;
NixStringContext getContext(const Store &);
auto listItems()
{
struct ListIterable

View file

@ -1,11 +1,10 @@
#include "value/context.hh"
#include "store-api.hh"
#include <optional>
namespace nix {
NixStringContextElem NixStringContextElem::parse(const Store & store, std::string_view s0)
NixStringContextElem NixStringContextElem::parse(std::string_view 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 '!'");
}
return NixStringContextElem::Built {
.drvPath = store.parseStorePath(s.substr(index + 1)),
.drvPath = StorePath { s.substr(index + 1) },
.output = std::string(s.substr(0, index)),
};
}
case '=': {
return NixStringContextElem::DrvDeep {
.drvPath = store.parseStorePath(s.substr(1)),
.drvPath = StorePath { s.substr(1) },
};
}
default: {
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 {
[&](const NixStringContextElem::Built & b) {
std::string res;
res += '!';
res += b.output;
res += '!';
res += store.printStorePath(b.drvPath);
res += b.drvPath.to_string();
return res;
},
[&](const NixStringContextElem::DrvDeep & d) {
std::string res;
res += '=';
res += store.printStorePath(d.drvPath);
res += d.drvPath.to_string();
return res;
},
[&](const NixStringContextElem::Opaque & o) {
return store.printStorePath(o.path);
return std::string { o.path.to_string() };
},
}, raw());
}

View file

@ -26,8 +26,6 @@ public:
}
};
class Store;
/**
* Plain opaque path to some store object.
*
@ -80,12 +78,15 @@ struct NixStringContextElem : _NixStringContextElem_Raw {
using DrvDeep = NixStringContextElem_DrvDeep;
using Built = NixStringContextElem_Built;
inline const Raw & raw() const {
inline const Raw & raw() const & {
return static_cast<const Raw &>(*this);
}
inline Raw & raw() {
inline Raw & raw() & {
return static_cast<Raw &>(*this);
}
inline Raw && raw() && {
return static_cast<Raw &&>(*this);
}
/**
* Decode a context string, one of:
@ -93,10 +94,10 @@ struct NixStringContextElem : _NixStringContextElem_Raw {
* - =<path>
* - !<name>!<path>
*/
static NixStringContextElem parse(const Store & store, std::string_view s);
std::string to_string(const Store & store) const;
static NixStringContextElem parse(std::string_view s);
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);
metaObj[j] = nullptr;
} else {
PathSet context;
NixStringContext 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
builder with the manifest as argument. */
auto attrs = state.buildBindings(3);
attrs.alloc("manifest").mkString(
state.store->printStorePath(manifestFile),
{state.store->printStorePath(manifestFile)});
state.mkStorePathString(manifestFile, attrs.alloc("manifest"));
attrs.insert(state.symbols.create("derivations"), &manifest);
Value args;
args.mkAttrs(attrs);
@ -132,7 +130,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Evaluate it. */
debug("evaluating user environment builder");
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
PathSet context;
NixStringContext context;
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
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);
state.forceValue(v, [&]() { return v.determinePos(noPos); });
PathSet context;
NixStringContext context;
if (evalOnly) {
Value vRes;
if (autoArgs.empty())

View file

@ -98,7 +98,7 @@ struct CmdBundle : InstallableValueCommand
if (!attr1)
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 attr2 = vRes->attrs->get(evalState->sOutPath);

View file

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

View file

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