aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomberek <tomberek@users.noreply.github.com>2022-02-03 02:39:18 -0500
committerGitHub <noreply@github.com>2022-02-03 02:39:18 -0500
commit12ff354d016058b25245d7e75d5b0c8ff7323839 (patch)
tree006cd6d3ff1e7781ba4583d5ebbf1245a21837bf
parent6e5e64fc7485cc12dd98be07e9abcc52d63dfd86 (diff)
parentfcb33440b6d3038e6761e546fc9434fa8e9a1666 (diff)
Merge branch 'master' into bundler_drv
-rw-r--r--Makefile.config.in1
-rw-r--r--configure.ac2
-rw-r--r--src/libcmd/local.mk2
-rw-r--r--src/libexpr/attr-path.cc2
-rw-r--r--src/libexpr/eval.cc62
-rw-r--r--src/libexpr/eval.hh17
-rw-r--r--src/libexpr/flake/flake.cc97
-rw-r--r--src/libexpr/get-drvs.cc2
-rw-r--r--src/libexpr/json-to-value.cc2
-rw-r--r--src/libexpr/json-to-value.hh2
-rw-r--r--src/libexpr/lexer.l18
-rw-r--r--src/libexpr/nixexpr.hh35
-rw-r--r--src/libexpr/parser.y175
-rw-r--r--src/libexpr/primops.cc123
-rw-r--r--src/libexpr/primops/context.cc9
-rw-r--r--src/libexpr/primops/fetchMercurial.cc12
-rw-r--r--src/libexpr/primops/fetchTree.cc4
-rw-r--r--src/libexpr/primops/fromTOML.cc2
-rw-r--r--src/libexpr/value-to-xml.cc2
-rw-r--r--src/libstore/derivations.cc4
-rw-r--r--src/libstore/derivations.hh2
-rw-r--r--src/libstore/globals.hh2
-rw-r--r--src/libstore/names.cc24
-rw-r--r--src/libstore/names.hh6
-rw-r--r--src/libstore/optimise-store.cc9
-rw-r--r--src/libstore/parsed-derivations.cc2
-rw-r--r--src/libutil/hash.cc2
-rw-r--r--src/libutil/hash.hh2
-rw-r--r--src/libutil/types.hh60
-rw-r--r--src/libutil/util.cc22
-rw-r--r--src/libutil/util.hh10
-rw-r--r--src/nix/develop.cc6
-rw-r--r--src/nix/develop.md2
-rw-r--r--src/nix/eval.cc2
-rw-r--r--src/nix/flake.md6
-rw-r--r--src/nix/nix.md2
-rw-r--r--src/nix/prefetch.cc2
-rw-r--r--src/nix/realisation/info.md2
-rw-r--r--src/nix/repl.cc2
-rw-r--r--src/resolve-system-dependencies/resolve-system-dependencies.cc2
-rw-r--r--tests/check.nix2
-rw-r--r--tests/flakes.sh6
-rw-r--r--tests/lang/eval-okay-xml.exp.xml2
43 files changed, 437 insertions, 313 deletions
diff --git a/Makefile.config.in b/Makefile.config.in
index c8c4446b4..3505f337e 100644
--- a/Makefile.config.in
+++ b/Makefile.config.in
@@ -16,6 +16,7 @@ LDFLAGS = @LDFLAGS@
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
LIBCURL_LIBS = @LIBCURL_LIBS@
+LOWDOWN_LIBS = @LOWDOWN_LIBS@
OPENSSL_LIBS = @OPENSSL_LIBS@
LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
PACKAGE_NAME = @PACKAGE_NAME@
diff --git a/configure.ac b/configure.ac
index 01d674879..8a01c33ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -272,7 +272,7 @@ AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation
AC_SUBST(doc_generate)
# Look for lowdown library.
-PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.8.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
+PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
# Setuid installations.
AC_CHECK_FUNCS([setresuid setreuid lchown])
diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk
index 8b0662753..7a2f83cc7 100644
--- a/src/libcmd/local.mk
+++ b/src/libcmd/local.mk
@@ -8,7 +8,7 @@ libcmd_SOURCES := $(wildcard $(d)/*.cc)
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
-libcmd_LDFLAGS += -llowdown -pthread
+libcmd_LDFLAGS += $(LOWDOWN_LIBS) -pthread
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index c50c6d92b..edef4d9f8 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -121,7 +121,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
std::string filename(pos, 0, colon);
unsigned int lineno;
try {
- lineno = std::stoi(std::string(pos, colon + 1));
+ lineno = std::stoi(std::string(pos, colon + 1, string::npos));
} catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos);
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index b884b4001..3ebbdca63 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1,5 +1,6 @@
#include "eval.hh"
#include "hash.hh"
+#include "types.hh"
#include "util.hh"
#include "store-api.hh"
#include "derivations.hh"
@@ -1373,7 +1374,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Nope, so show the first unexpected argument to the
user. */
for (auto & i : *args[0]->attrs)
- if (!lambda.formals->argNames.count(i.name))
+ if (!lambda.formals->has(i.name))
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
abort(); // can't happen
}
@@ -1694,7 +1695,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
PathSet context;
- std::vector<std::string> s;
+ std::vector<BackedStringView> s;
size_t sSize = 0;
NixInt n = 0;
NixFloat nf = 0;
@@ -1705,7 +1706,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
const auto str = [&] {
std::string result;
result.reserve(sSize);
- for (const auto & part : s) result += part;
+ for (const auto & part : s) result += *part;
return result;
};
/* c_str() is not str().c_str() because we want to create a string
@@ -1715,15 +1716,18 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
char * result = allocString(sSize + 1);
char * tmp = result;
for (const auto & part : s) {
- memcpy(tmp, part.c_str(), part.size());
- tmp += part.size();
+ memcpy(tmp, part->data(), part->size());
+ tmp += part->size();
}
*tmp = 0;
return result;
};
+ Value values[es->size()];
+ Value * vTmpP = values;
+
for (auto & [i_pos, i] : *es) {
- Value vTmp;
+ Value & vTmp = *vTmpP++;
i->eval(state, env, vTmp);
/* If the first element is a path, then the result will also
@@ -1756,9 +1760,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
/* skip canonization of first path, which would only be not
canonized in the first place if it's coming from a ./${foo} type
path */
- s.emplace_back(
- state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
- sSize += s.back().size();
+ auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
+ sSize += part->size();
+ s.emplace_back(std::move(part));
}
first = false;
@@ -1857,7 +1861,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
}
-string EvalState::forceString(Value & v, const Pos & pos)
+std::string_view EvalState::forceString(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type() != nString) {
@@ -1866,7 +1870,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
else
throwTypeError("value is %1% while a string was expected", v);
}
- return string(v.string.s);
+ return v.string.s;
}
@@ -1901,17 +1905,17 @@ std::vector<std::pair<Path, std::string>> Value::getContext()
}
-string EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
+std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
{
- string s = forceString(v, pos);
+ auto s = forceString(v, pos);
copyContext(v, context);
return s;
}
-string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
+std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
{
- string s = forceString(v, pos);
+ auto s = forceString(v, pos);
if (v.string.context) {
if (pos)
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
@@ -1942,34 +1946,35 @@ std::optional<string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
if (i != v.attrs->end()) {
Value v1;
callFunction(*i->value, v, v1, pos);
- return coerceToString(pos, v1, context, coerceMore, copyToStore);
+ return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
}
return {};
}
-string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
+BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath)
{
forceValue(v, pos);
- string s;
-
if (v.type() == nString) {
copyContext(v, context);
- return v.string.s;
+ return std::string_view(v.string.s);
}
if (v.type() == nPath) {
- Path path(canonicalizePath ? canonPath(v.path) : v.path);
- return copyToStore ? copyPathToStore(context, path) : path;
+ BackedStringView path(PathView(v.path));
+ if (canonicalizePath)
+ path = canonPath(*path);
+ if (copyToStore)
+ path = copyPathToStore(context, std::move(path).toOwned());
+ return path;
}
if (v.type() == nAttrs) {
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
- if (maybeString) {
- return *maybeString;
- }
+ if (maybeString)
+ return std::move(*maybeString);
auto i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
@@ -1991,14 +1996,13 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.isList()) {
string result;
for (auto [n, v2] : enumerate(v.listItems())) {
- result += coerceToString(pos, *v2,
- context, coerceMore, copyToStore);
+ result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
if (n < v.listSize() - 1
/* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0))
result += " ";
}
- return result;
+ return std::move(result);
}
}
@@ -2032,7 +2036,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{
- string path = coerceToString(pos, v, context, false, false);
+ string path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path;
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index c59203aa5..04acc5728 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -1,6 +1,7 @@
#pragma once
#include "attr-set.hh"
+#include "types.hh"
#include "value.hh"
#include "nixexpr.hh"
#include "symbol-table.hh"
@@ -201,8 +202,8 @@ public:
void resetFileCache();
/* Look up a file in the search path. */
- Path findFile(const string & path);
- Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos);
+ Path findFile(const std::string_view path);
+ Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
@@ -236,9 +237,9 @@ public:
inline void forceList(Value & v);
inline void forceList(Value & v, const Pos & pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
- string forceString(Value & v, const Pos & pos = noPos);
- string forceString(Value & v, PathSet & context, const Pos & pos = noPos);
- string forceStringNoCtx(Value & v, const Pos & pos = noPos);
+ std::string_view forceString(Value & v, const Pos & pos = noPos);
+ std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
+ std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
@@ -251,7 +252,7 @@ public:
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
- string coerceToString(const Pos & pos, Value & v, PathSet & context,
+ BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
@@ -309,8 +310,8 @@ private:
friend struct ExprAttrs;
friend struct ExprLet;
- Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path,
- const Path & basePath, StaticEnv & staticEnv);
+ Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
+ const PathView basePath, StaticEnv & staticEnv);
public:
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 0fbe9b960..9f3b58909 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -89,11 +89,11 @@ static void expectType(EvalState & state, ValueType type,
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos,
- const std::optional<Path> & baseDir);
+ const std::optional<Path> & baseDir, InputPath lockRootPath);
static FlakeInput parseFlakeInput(EvalState & state,
const std::string & inputName, Value * value, const Pos & pos,
- const std::optional<Path> & baseDir)
+ const std::optional<Path> & baseDir, InputPath lockRootPath)
{
expectType(state, nAttrs, *value, pos);
@@ -117,10 +117,12 @@ static FlakeInput parseFlakeInput(EvalState & state,
expectType(state, nBool, *attr.value, *attr.pos);
input.isFlake = attr.value->boolean;
} else if (attr.name == sInputs) {
- input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir);
+ input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath);
} else if (attr.name == sFollows) {
expectType(state, nString, *attr.value, *attr.pos);
- input.follows = parseInputPath(attr.value->string.s);
+ auto follows(parseInputPath(attr.value->string.s));
+ follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
+ input.follows = follows;
} else {
switch (attr.value->type()) {
case nString:
@@ -166,7 +168,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos,
- const std::optional<Path> & baseDir)
+ const std::optional<Path> & baseDir, InputPath lockRootPath)
{
std::map<FlakeId, FlakeInput> inputs;
@@ -178,7 +180,8 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
inputAttr.name,
inputAttr.value,
*inputAttr.pos,
- baseDir));
+ baseDir,
+ lockRootPath));
}
return inputs;
@@ -188,7 +191,8 @@ static Flake getFlake(
EvalState & state,
const FlakeRef & originalRef,
bool allowLookup,
- FlakeCache & flakeCache)
+ FlakeCache & flakeCache,
+ InputPath lockRootPath)
{
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, originalRef, allowLookup, flakeCache);
@@ -223,7 +227,7 @@ static Flake getFlake(
auto sInputs = state.symbols.create("inputs");
if (auto inputs = vInfo.attrs->get(sInputs))
- flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir);
+ flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath);
auto sOutputs = state.symbols.create("outputs");
@@ -250,10 +254,12 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString)
- flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
+ flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))});
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
- flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)});
+ flake.config.settings.emplace(
+ setting.name,
+ state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
}
else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
@@ -265,7 +271,7 @@ static Flake getFlake(
if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value));
- ss.push_back(state.forceStringNoCtx(*elem, *setting.pos));
+ ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
}
flake.config.settings.insert({setting.name, ss});
}
@@ -287,6 +293,11 @@ static Flake getFlake(
return flake;
}
+Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
+{
+ return getFlake(state, originalRef, allowLookup, flakeCache, {});
+}
+
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
{
FlakeCache flakeCache;
@@ -332,22 +343,12 @@ LockedFlake lockFlake(
std::vector<FlakeRef> parents;
- struct LockParent {
- /* The path to this parent. */
- InputPath path;
-
- /* Whether we are currently inside a top-level lockfile
- (inputs absolute) or subordinate lockfile (inputs
- relative). */
- bool absolute;
- };
-
std::function<void(
const FlakeInputs & flakeInputs,
std::shared_ptr<Node> node,
const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode,
- const LockParent & parent,
+ const InputPath & lockRootPath,
const Path & parentPath,
bool trustLock)>
computeLocks;
@@ -357,7 +358,7 @@ LockedFlake lockFlake(
std::shared_ptr<Node> node,
const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode,
- const LockParent & parent,
+ const InputPath & lockRootPath,
const Path & parentPath,
bool trustLock)
{
@@ -402,17 +403,7 @@ LockedFlake lockFlake(
if (input.follows) {
InputPath target;
- if (parent.absolute && !hasOverride) {
- target = *input.follows;
- } else {
- if (hasOverride) {
- target = inputPathPrefix;
- target.pop_back();
- } else
- target = parent.path;
-
- for (auto & i : *input.follows) target.push_back(i);
- }
+ target.insert(target.end(), input.follows->begin(), input.follows->end());
debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
node->inputs.insert_or_assign(id, target);
@@ -485,23 +476,25 @@ LockedFlake lockFlake(
break;
}
}
+ auto absoluteFollows(lockRootPath);
+ absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
fakeInputs.emplace(i.first, FlakeInput {
- .follows = *follows,
+ .follows = absoluteFollows,
});
}
}
}
- LockParent newParent {
- .path = inputPath,
- .absolute = true
- };
-
+ auto localPath(parentPath);
+ // If this input is a path, recurse it down.
+ // This allows us to resolve path inputs relative to the current flake.
+ if ((*input.ref).input.getType() == "path")
+ localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
computeLocks(
mustRefetch
- ? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
+ ? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
: fakeInputs,
- childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch);
+ childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch);
} else {
/* We need to create a new lock file entry. So fetch
@@ -520,7 +513,7 @@ LockedFlake lockFlake(
if (localRef.input.getType() == "path")
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
- auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache);
+ auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
/* Note: in case of an --override-input, we use
the *original* ref (input2.ref) for the
@@ -541,13 +534,6 @@ LockedFlake lockFlake(
parents.push_back(*input.ref);
Finally cleanup([&]() { parents.pop_back(); });
- // Follows paths from existing inputs in the top-level lockfile are absolute,
- // whereas paths in subordinate lockfiles are relative to those lockfiles.
- LockParent newParent {
- .path = inputPath,
- .absolute = oldLock ? true : false
- };
-
/* Recursively process the inputs of this
flake. Also, unless we already have this flake
in the top-level lock file, use this flake's
@@ -558,7 +544,7 @@ LockedFlake lockFlake(
? std::dynamic_pointer_cast<const Node>(oldLock)
: LockFile::read(
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
- newParent, localPath, false);
+ oldLock ? lockRootPath : inputPath, localPath, false);
}
else {
@@ -576,17 +562,12 @@ LockedFlake lockFlake(
}
};
- LockParent parent {
- .path = {},
- .absolute = true
- };
-
// Bring in the current ref for relative path resolution if we have it
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
computeLocks(
flake.inputs, newLockFile.root, {},
- lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false);
+ lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false);
for (auto & i : lockFlags.inputOverrides)
if (!overridesUsed.count(i.first))
@@ -726,7 +707,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
{
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
- auto flakeRefS = state.forceStringNoCtx(*args[0], pos);
+ string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 25fd9b949..2651266b2 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -104,7 +104,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* For each output... */
for (auto elem : i->value->listItems()) {
/* Evaluate the corresponding set. */
- string name = state->forceStringNoCtx(*elem, *i->pos);
+ string name(state->forceStringNoCtx(*elem, *i->pos));
Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value);
diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc
index 88716250c..99a475ff9 100644
--- a/src/libexpr/json-to-value.cc
+++ b/src/libexpr/json-to-value.cc
@@ -163,7 +163,7 @@ public:
}
};
-void parseJSON(EvalState & state, const string & s_, Value & v)
+void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
{
JSONSax parser(state, v);
bool res = json::sax_parse(s_, &parser);
diff --git a/src/libexpr/json-to-value.hh b/src/libexpr/json-to-value.hh
index 3b0fdae11..84bec4eba 100644
--- a/src/libexpr/json-to-value.hh
+++ b/src/libexpr/json-to-value.hh
@@ -8,6 +8,6 @@ namespace nix {
MakeError(JSONParseError, EvalError);
-void parseJSON(EvalState & state, const string & s, Value & v);
+void parseJSON(EvalState & state, const std::string_view & s, Value & v);
}
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index a0e7a1877..e276b0467 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -66,7 +66,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
// we make use of the fact that the parser receives a private copy of the input
// string and can munge around in it.
-static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
+static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
{
char * result = s;
char * t = s;
@@ -89,7 +89,7 @@ static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
else *t = c;
t++;
}
- return new ExprString(symbols.create({result, size_t(t - result)}));
+ return {result, size_t(t - result)};
}
@@ -176,7 +176,7 @@ or { return OR_KW; }
/* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */
- yylval->e = unescapeStr(data->symbols, yytext, yyleng);
+ yylval->str = unescapeStr(data->symbols, yytext, yyleng);
return STR;
}
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
@@ -191,26 +191,26 @@ or { return OR_KW; }
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
- yylval->e = new ExprIndStr(yytext);
+ yylval->str = {yytext, (size_t) yyleng, true};
return IND_STR;
}
<IND_STRING>\'\'\$ |
<IND_STRING>\$ {
- yylval->e = new ExprIndStr("$");
+ yylval->str = {"$", 1};
return IND_STR;
}
<IND_STRING>\'\'\' {
- yylval->e = new ExprIndStr("''");
+ yylval->str = {"''", 2};
return IND_STR;
}
<IND_STRING>\'\'\\{ANY} {
- yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
+ yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
return IND_STR;
}
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
<IND_STRING>\' {
- yylval->e = new ExprIndStr("'");
+ yylval->str = {"'", 1};
return IND_STR;
}
@@ -264,7 +264,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
- yylval->e = new ExprString(data->symbols.create(string(yytext)));
+ yylval->str = {yytext, (size_t) yyleng};
return STR;
}
<INPATH>{ANY} |
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 0a60057e5..4e923ac89 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -110,20 +110,13 @@ struct ExprFloat : Expr
struct ExprString : Expr
{
- Symbol s;
+ string s;
Value v;
- ExprString(const Symbol & s) : s(s) { v.mkString(s); };
+ ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
-/* Temporary class used during parsing of indented strings. */
-struct ExprIndStr : Expr
-{
- string s;
- ExprIndStr(const string & s) : s(s) { };
-};
-
struct ExprPath : Expr
{
string s;
@@ -223,10 +216,25 @@ struct Formal
struct Formals
{
- typedef std::list<Formal> Formals_;
+ typedef std::vector<Formal> Formals_;
Formals_ formals;
- std::set<Symbol> argNames; // used during parsing
bool ellipsis;
+
+ bool has(Symbol arg) const {
+ auto it = std::lower_bound(formals.begin(), formals.end(), arg,
+ [] (const Formal & f, const Symbol & sym) { return f.name < sym; });
+ return it != formals.end() && it->name == arg;
+ }
+
+ std::vector<Formal> lexicographicOrder() const
+ {
+ std::vector<Formal> result(formals.begin(), formals.end());
+ std::sort(result.begin(), result.end(),
+ [] (const Formal & a, const Formal & b) {
+ return std::string_view(a.name) < std::string_view(b.name);
+ });
+ return result;
+ }
};
struct ExprLambda : Expr
@@ -239,11 +247,6 @@ struct ExprLambda : Expr
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body)
{
- if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
- throw ParseError({
- .msg = hintfmt("duplicate formal function argument '%1%'", arg),
- .errPos = pos
- });
};
void setName(Symbol & name);
string showNamePos() const;
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 7a8e93c12..f0c80ebd5 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -16,6 +16,8 @@
#ifndef BISON_HEADER
#define BISON_HEADER
+#include <variant>
+
#include "util.hh"
#include "nixexpr.hh"
@@ -39,8 +41,22 @@ namespace nix {
{ };
};
+ struct ParserFormals {
+ std::vector<Formal> formals;
+ bool ellipsis = false;
+ };
+
}
+// using C a struct allows us to avoid having to define the special
+// members that using string_view here would implicitly delete.
+struct StringToken {
+ const char * p;
+ size_t l;
+ bool hasIndentation;
+ operator std::string_view() const { return {p, l}; }
+};
+
#define YY_DECL int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
@@ -140,21 +156,46 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
}
-static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
+static Formals * toFormals(ParseData & data, ParserFormals * formals,
+ Pos pos = noPos, Symbol arg = {})
{
- if (!formals->argNames.insert(formal.name).second)
+ std::sort(formals->formals.begin(), formals->formals.end(),
+ [] (const auto & a, const auto & b) {
+ return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
+ });
+
+ std::optional<std::pair<Symbol, Pos>> duplicate;
+ for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
+ if (formals->formals[i].name != formals->formals[i + 1].name)
+ continue;
+ std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
+ duplicate = std::min(thisDup, duplicate.value_or(thisDup));
+ }
+ if (duplicate)
+ throw ParseError({
+ .msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
+ .errPos = duplicate->second
+ });
+
+ Formals result;
+ result.ellipsis = formals->ellipsis;
+ result.formals = std::move(formals->formals);
+
+ if (arg.set() && result.has(arg))
throw ParseError({
- .msg = hintfmt("duplicate formal function argument '%1%'",
- formal.name),
+ .msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos
});
- formals->formals.push_front(formal);
+
+ delete formals;
+ return new Formals(std::move(result));
}
-static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
+static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
+ vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
{
- if (es.empty()) return new ExprString(symbols.create(""));
+ if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -163,20 +204,20 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
size_t minIndent = 1000000;
size_t curIndent = 0;
for (auto & [i_pos, i] : es) {
- ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
- if (!e) {
- /* Anti-quotations end the current start-of-line whitespace. */
+ auto * str = std::get_if<StringToken>(&i);
+ if (!str || !str->hasIndentation) {
+ /* Anti-quotations and escaped characters end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
continue;
}
- for (size_t j = 0; j < e->s.size(); ++j) {
+ for (size_t j = 0; j < str->l; ++j) {
if (atStartOfLine) {
- if (e->s[j] == ' ')
+ if (str->p[j] == ' ')
curIndent++;
- else if (e->s[j] == '\n') {
+ else if (str->p[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
@@ -184,7 +225,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
- } else if (e->s[j] == '\n') {
+ } else if (str->p[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
@@ -196,33 +237,31 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
- for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
- ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
- if (!e) {
- atStartOfLine = false;
- curDropped = 0;
- es2->push_back(*i);
- continue;
- }
-
+ auto i = es.begin();
+ const auto trimExpr = [&] (Expr * e) {
+ atStartOfLine = false;
+ curDropped = 0;
+ es2->emplace_back(i->first, e);
+ };
+ const auto trimString = [&] (const StringToken & t) {
string s2;
- for (size_t j = 0; j < e->s.size(); ++j) {
+ for (size_t j = 0; j < t.l; ++j) {
if (atStartOfLine) {
- if (e->s[j] == ' ') {
+ if (t.p[j] == ' ') {
if (curDropped++ >= minIndent)
- s2 += e->s[j];
+ s2 += t.p[j];
}
- else if (e->s[j] == '\n') {
+ else if (t.p[j] == '\n') {
curDropped = 0;
- s2 += e->s[j];
+ s2 += t.p[j];
} else {
atStartOfLine = false;
curDropped = 0;
- s2 += e->s[j];
+ s2 += t.p[j];
}
} else {
- s2 += e->s[j];
- if (e->s[j] == '\n') atStartOfLine = true;
+ s2 += t.p[j];
+ if (t.p[j] == '\n') atStartOfLine = true;
}
}
@@ -234,7 +273,10 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
s2 = string(s2, 0, p + 1);
}
- es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
+ es2->emplace_back(i->first, new ExprString(s2));
+ };
+ for (; i != es.end(); ++i, --n) {
+ std::visit(overloaded { trimExpr, trimString }, i->second);
}
/* If this is a single string, then don't do a concatenation. */
@@ -269,22 +311,17 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Expr * e;
nix::ExprList * list;
nix::ExprAttrs * attrs;
- nix::Formals * formals;
+ nix::ParserFormals * formals;
nix::Formal * formal;
nix::NixInt n;
nix::NixFloat nf;
- // using C a struct allows us to avoid having to define the special
- // members that using string_view here would implicitly delete.
- struct StringToken {
- const char * p;
- size_t l;
- operator std::string_view() const { return {p, l}; }
- };
StringToken id; // !!! -> Symbol
StringToken path;
StringToken uri;
+ StringToken str;
std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
+ std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
}
%type <e> start expr expr_function expr_if expr_op
@@ -294,11 +331,12 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%type <formals> formals
%type <formal> formal
%type <attrNames> attrs attrpath
-%type <string_parts> string_parts_interpolated ind_string_parts
+%type <string_parts> string_parts_interpolated
+%type <ind_string_parts> ind_string_parts
%type <e> path_start string_parts string_attr
%type <id> attr
%token <id> ID ATTRPATH
-%token <e> STR IND_STR
+%token <str> STR IND_STR
%token <n> INT
%token <nf> FLOAT
%token <path> PATH HPATH SPATH PATH_END
@@ -331,11 +369,17 @@ expr_function
: ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
| '{' formals '}' ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
+ { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), toFormals(*data, $2), $5); }
| '{' formals '}' '@' ID ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
+ {
+ Symbol arg = data->symbols.create($5);
+ $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
+ }
| ID '@' '{' formals '}' ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
+ {
+ Symbol arg = data->symbols.create($1);
+ $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
+ }
| ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
@@ -426,7 +470,7 @@ expr_simple
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
- new ExprString(data->symbols.create(path))});
+ new ExprString(path)});
}
| URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
@@ -435,7 +479,7 @@ expr_simple
.msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS
});
- $$ = new ExprString(data->symbols.create($1));
+ $$ = new ExprString(string($1));
}
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
@@ -450,18 +494,19 @@ expr_simple
;
string_parts
- : STR
+ : STR { $$ = new ExprString(string($1)); }
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
- | { $$ = new ExprString(data->symbols.create("")); }
+ | { $$ = new ExprString(""); }
;
string_parts_interpolated
- : string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
+ : string_parts_interpolated STR
+ { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
$$ = new vector<std::pair<Pos, Expr *> >;
- $$->emplace_back(makeCurPos(@1, data), $1);
+ $$->emplace_back(makeCurPos(@1, data), new ExprString(string($1)));
$$->emplace_back(makeCurPos(@2, data), $3);
}
;
@@ -483,7 +528,7 @@ path_start
ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
- | { $$ = new vector<std::pair<Pos, Expr *> >; }
+ | { $$ = new vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
;
binds
@@ -515,7 +560,7 @@ attrs
{ $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($2);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
throw ParseError({
@@ -532,7 +577,7 @@ attrpath
{ $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($3);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
$$->push_back(AttrName($3));
@@ -542,7 +587,7 @@ attrpath
{ $$ = new vector<AttrName>;
ExprString *str = dynamic_cast<ExprString *>($1);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
$$->push_back(AttrName($1));
@@ -566,13 +611,13 @@ expr_list
formals
: formal ',' formals
- { $$ = $3; addFormal(CUR_POS, $$, *$1); }
+ { $$ = $3; $$->formals.push_back(*$1); }
| formal
- { $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
+ { $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
- { $$ = new Formals; $$->ellipsis = false; }
+ { $$ = new ParserFormals; $$->ellipsis = false; }
| ELLIPSIS
- { $$ = new Formals; $$->ellipsis = true; }
+ { $$ = new ParserFormals; $$->ellipsis = true; }
;
formal
@@ -598,7 +643,7 @@ namespace nix {
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
- const Path & path, const Path & basePath, StaticEnv & staticEnv)
+ const PathView path, const PathView basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
ParseData data(*this);
@@ -709,24 +754,24 @@ void EvalState::addToSearchPath(const string & s)
}
-Path EvalState::findFile(const string & path)
+Path EvalState::findFile(const std::string_view path)
{
return findFile(searchPath, path);
}
-Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos)
+Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
{
for (auto & i : searchPath) {
std::string suffix;
if (i.first.empty())
- suffix = "/" + path;
+ suffix = concatStrings("/", path);
else {
auto s = i.first.size();
if (path.compare(0, s, i.first) != 0 ||
(path.size() > s && path[s] != '/'))
continue;
- suffix = path.size() == s ? "" : "/" + string(path, s);
+ suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
}
auto r = resolveSearchPathElem(i);
if (!r.first) continue;
@@ -735,7 +780,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
}
if (hasPrefix(path, "nix/"))
- return corepkgsPrefix + path.substr(4);
+ return concatStrings(corepkgsPrefix, path.substr(4));
throw ThrownError({
.msg = hintfmt(evalSettings.pureEval
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index b53425510..10d162cbb 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
{
auto path = realisePath(state, pos, *args[0]);
- string sym = state.forceStringNoCtx(*args[1], pos);
+ string sym(state.forceStringNoCtx(*args[1], pos));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
@@ -350,10 +350,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
});
}
PathSet context;
- auto program = state.coerceToString(pos, *elems[0], context, false, false);
+ auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned();
Strings commandArgs;
- for (unsigned int i = 1; i < args[0]->listSize(); ++i)
- commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
+ for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
+ commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned());
+ }
try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
@@ -706,7 +707,7 @@ static RegisterPrimOp primop_abort({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
+ string s = state.coerceToString(pos, *args[0], context).toOwned();
throw Abort("evaluation aborted with the following error message: '%1%'", s);
}
});
@@ -724,7 +725,7 @@ static RegisterPrimOp primop_throw({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
+ string s = state.coerceToString(pos, *args[0], context).toOwned();
throw ThrownError(s);
}
});
@@ -736,7 +737,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
v = *args[1];
} catch (Error & e) {
PathSet context;
- e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context));
+ e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
throw;
}
}
@@ -825,7 +826,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string name = state.forceStringNoCtx(*args[0], pos);
+ string name(state.forceStringNoCtx(*args[0], pos));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
}
@@ -975,7 +976,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
const string & key = i->name;
vomit("processing attribute '%1%'", key);
- auto handleHashMode = [&](const std::string & s) {
+ auto handleHashMode = [&](const std::string_view s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else
@@ -1030,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else if (i->name == state.sArgs) {
state.forceList(*i->value, pos);
for (auto elem : i->value->listItems()) {
- string s = state.coerceToString(posDrvName, *elem, context, true);
+ string s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
drv.args.push_back(s);
}
}
@@ -1066,7 +1067,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} else {
- auto s = state.coerceToString(*i->pos, *i->value, context, true);
+ auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned();
drv.env.emplace(key, s);
if (i->name == state.sBuilder) drv.builder = std::move(s);
else if (i->name == state.sSystem) drv.platform = std::move(s);
@@ -1269,7 +1270,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
substituted by the corresponding output path at build time. For
example, 'placeholder "out"' returns the string
/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
- time, any occurence of this string in an derivation attribute will
+ time, any occurrence of this string in an derivation attribute will
be replaced with the concrete path in the Nix store of the output
‘out’. */
static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -1399,7 +1400,7 @@ static RegisterPrimOp primop_pathExists({
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- v.mkString(baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context);
+ v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context);
}
static RegisterPrimOp primop_baseNameOf({
@@ -1419,7 +1420,8 @@ static RegisterPrimOp primop_baseNameOf({
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
+ auto path = state.coerceToString(pos, *args[0], context, false, false);
+ auto dir = dirOf(*path);
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context);
}
@@ -1486,7 +1488,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
);
PathSet context;
- string path = state.coerceToString(pos, *i->value, context, false, false);
+ string path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try {
auto rewrites = state.realiseContext(context);
@@ -1502,7 +1504,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
searchPath.emplace_back(prefix, path);
}
- string path = state.forceStringNoCtx(*args[1], pos);
+ auto path = state.forceStringNoCtx(*args[1], pos);
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
}
@@ -1516,7 +1518,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
/* Return the cryptographic hash of a file in base-16. */
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string type = state.forceStringNoCtx(*args[0], pos);
+ auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
if (!ht)
throw Error({
@@ -1723,7 +1725,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string s = state.forceStringNoCtx(*args[0], pos);
+ auto s = state.forceStringNoCtx(*args[0], pos);
try {
parseJSON(state, s, v);
} catch (JSONParseError &e) {
@@ -1752,8 +1754,8 @@ static RegisterPrimOp primop_fromJSON({
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string name = state.forceStringNoCtx(*args[0], pos);
- string contents = state.forceString(*args[1], context, pos);
+ string name(state.forceStringNoCtx(*args[0], pos));
+ string contents(state.forceString(*args[1], context, pos));
StorePathSet refs;
@@ -2153,7 +2155,7 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */
void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string attr = state.forceStringNoCtx(*args[0], pos);
+ auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
Bindings::iterator i = getAttr(
state,
@@ -2183,7 +2185,7 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */
static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string attr = state.forceStringNoCtx(*args[0], pos);
+ auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end())
@@ -2201,7 +2203,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
/* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string attr = state.forceStringNoCtx(*args[0], pos);
+ auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
}
@@ -2279,7 +2281,7 @@ static RegisterPrimOp primop_removeAttrs({
/* Builds a set from a list specifying (name, value) pairs. To be
precise, a list [{name = "name1"; value = value1;} ... {name =
"nameN"; value = valueN;}] is transformed to {name1 = value1;
- ... nameN = valueN;}. In case of duplicate occurences of the same
+ ... nameN = valueN;}. In case of duplicate occurrences of the same
name, the first takes precedence. */
static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
@@ -2300,7 +2302,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
pos
);
- string name = state.forceStringNoCtx(*j->value, *j->pos);
+ auto name = state.forceStringNoCtx(*j->value, *j->pos);
Symbol sym = state.symbols.create(name);
if (seen.insert(sym).second) {
@@ -3032,7 +3034,7 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val
for (auto vElem : args[1]->listItems()) {
Value res;
state.callFunction(*args[0], *vElem, res, pos);
- string name = state.forceStringNoCtx(res, pos);
+ auto name = state.forceStringNoCtx(res, pos);
Symbol sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first;
vector->second.push_back(vElem);
@@ -3288,8 +3290,8 @@ static RegisterPrimOp primop_lessThan({
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string s = state.coerceToString(pos, *args[0], context, true, false);
- v.mkString(s, context);
+ auto s = state.coerceToString(pos, *args[0], context, true, false);
+ v.mkString(*s, context);
}
static RegisterPrimOp primop_toString({
@@ -3325,7 +3327,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
int start = state.forceInt(*args[0], pos);
int len = state.forceInt(*args[1], pos);
PathSet context;
- string s = state.coerceToString(pos, *args[2], context);
+ auto s = state.coerceToString(pos, *args[2], context);
if (start < 0)
throw EvalError({
@@ -3333,7 +3335,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
.errPos = pos
});
- v.mkString((unsigned int) start >= s.size() ? "" : string(s, start, len), context);
+ v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
}
static RegisterPrimOp primop_substring({
@@ -3359,8 +3361,8 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
- v.mkInt(s.size());
+ auto s = state.coerceToString(pos, *args[0], context);
+ v.mkInt(s->size());
}
static RegisterPrimOp primop_stringLength({
@@ -3376,7 +3378,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string type = state.forceStringNoCtx(*args[0], pos);
+ auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
if (!ht)
throw Error({
@@ -3385,7 +3387,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
});
PathSet context; // discarded
- string s = state.forceString(*args[1], context, pos);
+ auto s = state.forceString(*args[1], context, pos);
v.mkString(hashString(*ht, s).to_string(Base16, false));
}
@@ -3403,7 +3405,18 @@ static RegisterPrimOp primop_hashString({
struct RegexCache
{
- std::unordered_map<std::string, std::regex> cache;
+ // TODO use C++20 transparent comparison when available
+ std::unordered_map<std::string_view, std::regex> cache;
+ std::list<std::string> keys;
+
+ std::regex get(std::string_view re)
+ {
+ auto it = cache.find(re);
+ if (it != cache.end())
+ return it->second;
+ keys.emplace_back(re);
+ return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second;
+ }
};
std::shared_ptr<RegexCache> makeRegexCache()
@@ -3417,15 +3430,13 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
- auto regex = state.regexCache->cache.find(re);
- if (regex == state.regexCache->cache.end())
- regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
+ auto regex = state.regexCache->get(re);
PathSet context;
- const std::string str = state.forceString(*args[1], context, pos);
+ const auto str = state.forceString(*args[1], context, pos);
- std::smatch match;
- if (!std::regex_match(str, match, regex->second)) {
+ std::cmatch match;
+ if (!std::regex_match(str.begin(), str.end(), match, regex)) {
v.mkNull();
return;
}
@@ -3500,15 +3511,13 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
- auto regex = state.regexCache->cache.find(re);
- if (regex == state.regexCache->cache.end())
- regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
+ auto regex = state.regexCache->get(re);
PathSet context;
- const std::string str = state.forceString(*args[1], context, pos);
+ const auto str = state.forceString(*args[1], context, pos);
- auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second);
- auto end = std::sregex_iterator();
+ auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
+ auto end = std::cregex_iterator();
// Any matches results are surrounded by non-matching results.
const size_t len = std::distance(begin, end);
@@ -3520,9 +3529,9 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
return;
}
- for (std::sregex_iterator i = begin; i != end; ++i) {
+ for (auto i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3);
- std::smatch match = *i;
+ auto match = *i;
// Add a string for non-matched characters.
(v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str());
@@ -3613,7 +3622,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
for (auto elem : args[1]->listItems()) {
if (first) first = false; else res += sep;
- res += state.coerceToString(pos, *elem, context);
+ res += *state.coerceToString(pos, *elem, context);
}
v.mkString(res, context);
@@ -3643,14 +3652,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
vector<string> from;
from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems())
- from.push_back(state.forceString(*elem, pos));
+ from.emplace_back(state.forceString(*elem, pos));
vector<std::pair<string, PathSet>> to;
to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
PathSet ctx;
auto s = state.forceString(*elem, ctx, pos);
- to.push_back(std::make_pair(std::move(s), std::move(ctx)));
+ to.emplace_back(s, std::move(ctx));
}
PathSet context;
@@ -3712,7 +3721,7 @@ static RegisterPrimOp primop_replaceStrings({
static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string name = state.forceStringNoCtx(*args[0], pos);
+ auto name = state.forceStringNoCtx(*args[0], pos);
DrvName parsed(name);
auto attrs = state.buildBindings(2);
attrs.alloc(state.sName).mkString(parsed.name);
@@ -3736,8 +3745,8 @@ static RegisterPrimOp primop_parseDrvName({
static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string version1 = state.forceStringNoCtx(*args[0], pos);
- string version2 = state.forceStringNoCtx(*args[1], pos);
+ auto version1 = state.forceStringNoCtx(*args[0], pos);
+ auto version2 = state.forceStringNoCtx(*args[1], pos);
v.mkInt(compareVersions(version1, version2));
}
@@ -3756,14 +3765,14 @@ static RegisterPrimOp primop_compareVersions({
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string version = state.forceStringNoCtx(*args[0], pos);
+ auto version = state.forceStringNoCtx(*args[0], pos);
auto iter = version.cbegin();
Strings components;
while (iter != version.cend()) {
auto component = nextComponent(iter, version.cend());
if (component.empty())
break;
- components.emplace_back(std::move(component));
+ components.emplace_back(component);
}
state.mkList(v, components.size());
for (const auto & [n, component] : enumerate(components))
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index a239c06da..654251c23 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -7,7 +7,8 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- v.mkString(state.coerceToString(pos, *args[0], context));
+ auto s = state.coerceToString(pos, *args[0], context);
+ v.mkString(*s);
}
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
@@ -32,13 +33,13 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
+ auto s = state.coerceToString(pos, *args[0], context);
PathSet context2;
for (auto & p : context)
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
- v.mkString(s, context2);
+ v.mkString(*s, context2);
}
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
@@ -180,7 +181,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
}
for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, *iter->pos);
- context.insert("!" + name + "!" + string(i.name));
+ context.insert(concatStrings("!", name, "!", i.name));
}
}
}
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index 42214c207..c4e1a7bf0 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -12,7 +12,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
std::string url;
std::optional<Hash> rev;
std::optional<std::string> ref;
- std::string name = "source";
+ std::string_view name = "source";
PathSet context;
state.forceValue(*args[0], pos);
@@ -22,14 +22,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
- string n(attr.name);
+ std::string_view n(attr.name);
if (n == "url")
- url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
+ url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
- if (std::regex_match(value, revRegex))
+ if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, htSHA1);
else
ref = value;
@@ -50,7 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
});
} else
- url = state.coerceToString(pos, *args[0], context, false, false);
+ url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.
@@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
fetchers::Attrs attrs;
attrs.insert_or_assign("type", "hg");
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
- attrs.insert_or_assign("name", name);
+ attrs.insert_or_assign("name", string(name));
if (ref) attrs.insert_or_assign("ref", *ref);
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(std::move(attrs));
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 6647bd35c..d09e2d9e1 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -125,7 +125,7 @@ static void fetchTree(
if (attr.name == state.sType) continue;
state.forceValue(*attr.value, *attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) {
- auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false);
+ auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
attrs.emplace(attr.name,
attr.name == "url"
? type == "git"
@@ -151,7 +151,7 @@ static void fetchTree(
input = fetchers::Input::fromAttrs(std::move(attrs));
} else {
- auto url = state.coerceToString(pos, *args[0], context, false, false);
+ auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
if (type == "git") {
fetchers::Attrs attrs;
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index 80c7e0b82..c0e858b61 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
{
auto toml = state.forceStringNoCtx(*args[0], pos);
- std::istringstream tomlStream(toml);
+ std::istringstream tomlStream(string{toml});
std::function<void(Value &, toml::value)> visit;
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
index a875f82d7..a9fb60b0e 100644
--- a/src/libexpr/value-to-xml.cc
+++ b/src/libexpr/value-to-xml.cc
@@ -142,7 +142,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
- for (auto & i : v.lambda.fun->formals->formals)
+ for (auto & i : v.lambda.fun->formals->lexicographicOrder())
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 3e3d50144..40af6a775 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -699,10 +699,10 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
}
-std::string hashPlaceholder(const std::string & outputName)
+std::string hashPlaceholder(const std::string_view outputName)
{
// FIXME: memoize?
- return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false);
+ return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
}
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName)
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index b1cb68194..a644cec60 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -236,7 +236,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
It is used as a placeholder to allow derivations to refer to their
own outputs without needing to use the hash of a derivation in
itself, making the hash near-impossible to calculate. */
-std::string hashPlaceholder(const std::string & outputName);
+std::string hashPlaceholder(const std::string_view outputName);
/* This creates an opaque and almost certainly unique string
deterministically from a derivation path and output name.
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index f65893b10..893c95bd6 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -970,7 +970,7 @@ public:
Setting<std::string> commitLockFileSummary{
this, "", "commit-lockfile-summary",
R"(
- The commit summary to use when commiting changed flake lock files. If
+ The commit summary to use when committing changed flake lock files. If
empty, the summary is generated based on the action performed.
)"};
};
diff --git a/src/libstore/names.cc b/src/libstore/names.cc
index 54c95055d..277aabf0f 100644
--- a/src/libstore/names.cc
+++ b/src/libstore/names.cc
@@ -56,8 +56,8 @@ bool DrvName::matches(const DrvName & n)
}
-string nextComponent(string::const_iterator & p,
- const string::const_iterator end)
+std::string_view nextComponent(std::string_view::const_iterator & p,
+ const std::string_view::const_iterator end)
{
/* Skip any dots and dashes (component separators). */
while (p != end && (*p == '.' || *p == '-')) ++p;
@@ -67,18 +67,18 @@ string nextComponent(string::const_iterator & p,
/* If the first character is a digit, consume the longest sequence
of digits. Otherwise, consume the longest sequence of
non-digit, non-separator characters. */
- string s;
+ auto s = p;
if (isdigit(*p))
- while (p != end && isdigit(*p)) s += *p++;
+ while (p != end && isdigit(*p)) p++;
else
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
- s += *p++;
+ p++;
- return s;
+ return {s, size_t(p - s)};
}
-static bool componentsLT(const string & c1, const string & c2)
+static bool componentsLT(const std::string_view c1, const std::string_view c2)
{
auto n1 = string2Int<int>(c1);
auto n2 = string2Int<int>(c2);
@@ -94,14 +94,14 @@ static bool componentsLT(const string & c1, const string & c2)
}
-int compareVersions(const string & v1, const string & v2)
+int compareVersions(const std::string_view v1, const std::string_view v2)
{
- string::const_iterator p1 = v1.begin();
- string::const_iterator p2 = v2.begin();
+ auto p1 = v1.begin();
+ auto p2 = v2.begin();
while (p1 != v1.end() || p2 != v2.end()) {
- string c1 = nextComponent(p1, v1.end());
- string c2 = nextComponent(p2, v2.end());
+ auto c1 = nextComponent(p1, v1.end());
+ auto c2 = nextComponent(p2, v2.end());
if (componentsLT(c1, c2)) return -1;
else if (componentsLT(c2, c1)) return 1;
}
diff --git a/src/libstore/names.hh b/src/libstore/names.hh
index 3f861bc44..6f01fe2a1 100644
--- a/src/libstore/names.hh
+++ b/src/libstore/names.hh
@@ -27,9 +27,9 @@ private:
typedef list<DrvName> DrvNames;
-string nextComponent(string::const_iterator & p,
- const string::const_iterator end);
-int compareVersions(const string & v1, const string & v2);
+std::string_view nextComponent(std::string_view::const_iterator & p,
+ const std::string_view::const_iterator end);
+int compareVersions(const std::string_view v1, const std::string_view v2);
DrvNames drvNamesFromArgs(const Strings & opArgs);
}
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 1833c954e..13cb142f8 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -26,7 +26,7 @@ static void makeWritable(const Path & path)
struct MakeReadOnly
{
Path path;
- MakeReadOnly(const Path & path) : path(path) { }
+ MakeReadOnly(const PathView path) : path(path) { }
~MakeReadOnly()
{
try {
@@ -205,12 +205,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its
permissions). */
- bool mustToggle = dirOf(path) != realStoreDir.get();
- if (mustToggle) makeWritable(dirOf(path));
+ const Path dirOfPath(dirOf(path));
+ bool mustToggle = dirOfPath != realStoreDir.get();
+ if (mustToggle) makeWritable(dirOfPath);
/* When we're done, make the directory read-only again and reset
its timestamp back to 0. */
- MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
+ MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% realStoreDir % getpid() % random()).str();
diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc
index caddba9b1..8c65053e4 100644
--- a/src/libstore/parsed-derivations.cc
+++ b/src/libstore/parsed-derivations.cc
@@ -170,7 +170,7 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json)
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
if (value.is_string())
- return shellEscape(value);
+ return shellEscape(value.get<std::string_view>());
if (value.is_number()) {
auto f = value.get<float>();
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 4df8b4ecb..6ed00d43c 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -259,7 +259,7 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI)
throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
}
-Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
+Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht)
{
if (hashStr.empty()) {
if (!ht)
diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh
index 1b626dd85..dff46542f 100644
--- a/src/libutil/hash.hh
+++ b/src/libutil/hash.hh
@@ -107,7 +107,7 @@ public:
};
/* Helper that defaults empty hashes to the 0 hash. */
-Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht);
+Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash);
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index 8f72c926f..e3aca20c9 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -6,6 +6,7 @@
#include <set>
#include <string>
#include <map>
+#include <variant>
#include <vector>
namespace nix {
@@ -47,4 +48,63 @@ struct Explicit {
}
};
+
+/* This wants to be a little bit like rust's Cow type.
+ Some parts of the evaluator benefit greatly from being able to reuse
+ existing allocations for strings, but have to be able to also use
+ newly allocated storage for values.
+
+ We do not define implicit conversions, even with ref qualifiers,
+ since those can easily become ambiguous to the reader and can degrade
+ into copying behaviour we want to avoid. */
+class BackedStringView {
+private:
+ std::variant<std::string, std::string_view> data;
+
+ /* Needed to introduce a temporary since operator-> must return
+ a pointer. Without this we'd need to store the view object
+ even when we already own a string. */
+ class Ptr {
+ private:
+ std::string_view view;
+ public:
+ Ptr(std::string_view view): view(view) {}
+ const std::string_view * operator->() const { return &view; }
+ };
+
+public:
+ BackedStringView(std::string && s): data(std::move(s)) {}
+ BackedStringView(std::string_view sv): data(sv) {}
+ template<size_t N>
+ BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
+
+ BackedStringView(const BackedStringView &) = delete;
+ BackedStringView & operator=(const BackedStringView &) = delete;
+
+ /* We only want move operations defined since the sole purpose of
+ this type is to avoid copies. */
+ BackedStringView(BackedStringView && other) = default;
+ BackedStringView & operator=(BackedStringView && other) = default;
+
+ bool isOwned() const
+ {
+ return std::holds_alternative<std::string>(data);
+ }
+
+ std::string toOwned() &&
+ {
+ return isOwned()
+ ? std::move(std::get<std::string>(data))
+ : std::string(std::get<std::string_view>(data));
+ }
+
+ std::string_view operator*() const
+ {
+ return isOwned()
+ ? std::get<std::string>(data)
+ : std::get<std::string_view>(data);
+ }
+ Ptr operator->() const { return Ptr(**this); }
+};
+
}
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 1f1f2c861..cd359cfee 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -81,7 +81,7 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
}
-Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
+Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
{
if (path[0] != '/') {
if (!dir) {
@@ -95,12 +95,12 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
if (!getcwd(buf, sizeof(buf)))
#endif
throw SysError("cannot get cwd");
- dir = buf;
+ path = concatStrings(buf, "/", path);
#ifdef __GNU__
free(buf);
#endif
- }
- path = *dir + "/" + path;
+ } else
+ path = concatStrings(*dir, "/", path);
}
return canonPath(path, resolveSymlinks);
}
@@ -147,7 +147,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
path = {};
} else {
s += path.substr(0, slash);
- path = path.substr(slash + 1);
+ path = path.substr(slash);
}
/* If s points to a symlink, resolve it and continue from there */
@@ -172,7 +172,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
}
-Path dirOf(const Path & path)
+Path dirOf(const PathView path)
{
Path::size_type pos = path.rfind('/');
if (pos == string::npos)
@@ -1344,9 +1344,11 @@ std::string toLower(const std::string & s)
}
-std::string shellEscape(const std::string & s)
+std::string shellEscape(const std::string_view s)
{
- std::string r = "'";
+ std::string r;
+ r.reserve(s.size() + 2);
+ r += "'";
for (auto & i : s)
if (i == '\'') r += "'\\''"; else r += i;
r += '\'';
@@ -1751,7 +1753,7 @@ void bind(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() {
- auto dir = dirOf(path);
+ Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path));
@@ -1780,7 +1782,7 @@ void connect(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() {
- auto dir = dirOf(path);
+ Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path));
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 369c44f78..579a42785 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -49,7 +49,7 @@ void clearEnv();
specified directory, or the current directory otherwise. The path
is also canonicalised. */
Path absPath(Path path,
- std::optional<Path> dir = {},
+ std::optional<PathView> dir = {},
bool resolveSymlinks = false);
/* Canonicalise a path by removing all `.' or `..' components and
@@ -62,7 +62,7 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
everything before the final `/'. If the path is the root or an
immediate child thereof (e.g., `/foo'), this means `/'
is returned.*/
-Path dirOf(const Path & path);
+Path dirOf(const PathView path);
/* Return the base name of the given canonical path, i.e., everything
following the final `/' (trailing slashes are removed). */
@@ -148,6 +148,9 @@ Path getDataDir();
/* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */
Paths createDirs(const Path & path);
+inline Paths createDirs(PathView path) {
+ return createDirs(Path(path));
+}
/* Create a symlink. */
void createSymlink(const Path & target, const Path & link,
@@ -187,6 +190,7 @@ public:
void cancel();
void reset(const Path & p, bool recursive = true);
operator Path() const { return path; }
+ operator PathView() const { return path; }
};
@@ -491,7 +495,7 @@ std::string toLower(const std::string & s);
/* Escape a string as a shell word. */
-std::string shellEscape(const std::string & s);
+std::string shellEscape(const std::string_view s);
/* Exception handling in destructors: print an error message, then
diff --git a/src/nix/develop.cc b/src/nix/develop.cc
index a8ca1cac2..42e13436a 100644
--- a/src/nix/develop.cc
+++ b/src/nix/develop.cc
@@ -472,9 +472,11 @@ struct CmdDevelop : Common, MixEnvironment
else {
script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
if (developSettings.bashPrompt != "")
- script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt));
+ script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n",
+ shellEscape(developSettings.bashPrompt.get()));
if (developSettings.bashPromptSuffix != "")
- script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", shellEscape(developSettings.bashPromptSuffix));
+ script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n",
+ shellEscape(developSettings.bashPromptSuffix.get()));
}
writeFull(rcFileFd.get(), script);
diff --git a/src/nix/develop.md b/src/nix/develop.md
index 1f214966a..3e7e339d5 100644
--- a/src/nix/develop.md
+++ b/src/nix/develop.md
@@ -55,7 +55,7 @@ R""(
# nix develop /tmp/my-build-env
```
-* Replace all occurences of the store path corresponding to
+* Replace all occurrences of the store path corresponding to
`glibc.dev` with a writable directory:
```console
diff --git a/src/nix/eval.cc b/src/nix/eval.cc
index c7517cf79..c0435461f 100644
--- a/src/nix/eval.cc
+++ b/src/nix/eval.cc
@@ -107,7 +107,7 @@ struct CmdEval : MixJSON, InstallableCommand
else if (raw) {
stopProgressBar();
- std::cout << state->coerceToString(noPos, *v, context);
+ std::cout << *state->coerceToString(noPos, *v, context);
}
else if (json) {
diff --git a/src/nix/flake.md b/src/nix/flake.md
index a8436bcaa..accddd436 100644
--- a/src/nix/flake.md
+++ b/src/nix/flake.md
@@ -292,6 +292,12 @@ The following attributes are supported in `flake.nix`:
value (e.g. `packages.x86_64-linux` must be an attribute set of
derivations built for the `x86_64-linux` platform).
+* `nixConfig`: a set of `nix.conf` options to be set when evaluating any
+ part of a flake. In the interests of security, only a small set of
+ whitelisted options (currently `bash-prompt`, `bash-prompt-suffix`,
+ and `flake-registry`) are allowed to be set without confirmation so long as
+ `accept-flake-config` is not set in the global configuration.
+
## Flake inputs
The attribute `inputs` specifies the dependencies of a flake, as an
diff --git a/src/nix/nix.md b/src/nix/nix.md
index 2f54c5e2c..1dc59362d 100644
--- a/src/nix/nix.md
+++ b/src/nix/nix.md
@@ -76,7 +76,7 @@ the Nix store. Here are the recognised types of installables:
Note that the search will only include files indexed by git. In particular, files
which are matched by `.gitignore` or have never been `git add`-ed will not be
- available in the flake. If this is undesireable, specify `path:<directory>` explicitly;
+ available in the flake. If this is undesirable, specify `path:<directory>` explicitly;
For example, if `/foo/bar` is a git repository with the following structure:
```
diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc
index 768d37595..669be0709 100644
--- a/src/nix/prefetch.cc
+++ b/src/nix/prefetch.cc
@@ -38,7 +38,7 @@ string resolveMirrorUrl(EvalState & state, string url)
if (mirrorList->value->listSize() < 1)
throw Error("mirror URL '%s' did not expand to anything", url);
- auto mirror = state.forceString(*mirrorList->value->listElems()[0]);
+ string mirror(state.forceString(*mirrorList->value->listElems()[0]));
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
}
diff --git a/src/nix/realisation/info.md b/src/nix/realisation/info.md
index 852240f44..8aa986516 100644
--- a/src/nix/realisation/info.md
+++ b/src/nix/realisation/info.md
@@ -1,7 +1,7 @@
R"MdBoundary(
# Description
-Display some informations about the given realisation
+Display some information about the given realisation
# Examples
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index be9f96836..2d983e98e 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -463,7 +463,7 @@ bool NixRepl::processLine(string line)
if (v.type() == nPath || v.type() == nString) {
PathSet context;
auto filename = state->coerceToString(noPos, v, context);
- pos.file = state->symbols.create(filename);
+ pos.file = state->symbols.create(*filename);
} else if (v.isLambda()) {
pos = v.lambda.fun->pos;
} else {
diff --git a/src/resolve-system-dependencies/resolve-system-dependencies.cc b/src/resolve-system-dependencies/resolve-system-dependencies.cc
index 27cf53a45..98c969437 100644
--- a/src/resolve-system-dependencies/resolve-system-dependencies.cc
+++ b/src/resolve-system-dependencies/resolve-system-dependencies.cc
@@ -107,7 +107,7 @@ Path resolveSymlink(const Path & path)
auto target = readLink(path);
return hasPrefix(target, "/")
? target
- : dirOf(path) + "/" + target;
+ : concatStrings(dirOf(path), "/", target);
}
std::set<string> resolveTree(const Path & path, PathSet & deps)
diff --git a/tests/check.nix b/tests/check.nix
index ec455ae2d..ed91ff845 100644
--- a/tests/check.nix
+++ b/tests/check.nix
@@ -50,6 +50,6 @@ with import ./config.nix;
fetchurl = import <nix/fetchurl.nix> {
url = "file://" + toString ./lang/eval-okay-xml.exp.xml;
- sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v";
+ sha256 = "sha256-behBlX+DQK/Pjvkuc8Tx68Jwi4E5v86wDq+ZLaHyhQE=";
};
}
diff --git a/tests/flakes.sh b/tests/flakes.sh
index 20e6d09c1..db178967f 100644
--- a/tests/flakes.sh
+++ b/tests/flakes.sh
@@ -730,6 +730,7 @@ cat > $flakeFollowsB/flake.nix <<EOF
description = "Flake B";
inputs = {
foobar.url = "path:$flakeFollowsA/flakeE";
+ goodoo.follows = "C/goodoo";
C = {
url = "path:./flakeC";
inputs.foobar.follows = "foobar";
@@ -744,6 +745,7 @@ cat > $flakeFollowsC/flake.nix <<EOF
description = "Flake C";
inputs = {
foobar.url = "path:$flakeFollowsA/flakeE";
+ goodoo.follows = "foobar";
};
outputs = { ... }: {};
}
@@ -759,7 +761,7 @@ EOF
cat > $flakeFollowsE/flake.nix <<EOF
{
- description = "Flake D";
+ description = "Flake E";
inputs = {};
outputs = { ... }: {};
}
@@ -768,6 +770,8 @@ EOF
git -C $flakeFollowsA add flake.nix flakeB/flake.nix \
flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix
+nix flake metadata $flakeFollowsA
+
nix flake update $flakeFollowsA
oldLock="$(cat "$flakeFollowsA/flake.lock")"
diff --git a/tests/lang/eval-okay-xml.exp.xml b/tests/lang/eval-okay-xml.exp.xml
index 92b75e0b8..20099326c 100644
--- a/tests/lang/eval-okay-xml.exp.xml
+++ b/tests/lang/eval-okay-xml.exp.xml
@@ -31,9 +31,9 @@
<attr name="f">
<function>
<attrspat>
- <attr name="z" />
<attr name="x" />
<attr name="y" />
+ <attr name="z" />
</attrspat>
</function>
</attr>