aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/attr-path.cc14
-rw-r--r--src/libexpr/attr-path.hh4
-rw-r--r--src/libexpr/attr-set.cc6
-rw-r--r--src/libexpr/attr-set.hh50
-rw-r--r--src/libexpr/common-eval-args.cc101
-rw-r--r--src/libexpr/common-eval-args.hh27
-rw-r--r--src/libexpr/eval-cache.cc77
-rw-r--r--src/libexpr/eval-cache.hh4
-rw-r--r--src/libexpr/eval-inline.hh40
-rw-r--r--src/libexpr/eval.cc301
-rw-r--r--src/libexpr/eval.hh103
-rw-r--r--src/libexpr/flake/flake.cc88
-rw-r--r--src/libexpr/function-trace.hh2
-rw-r--r--src/libexpr/get-drvs.cc30
-rw-r--r--src/libexpr/lexer.l10
-rw-r--r--src/libexpr/nixexpr.cc305
-rw-r--r--src/libexpr/nixexpr.hh246
-rw-r--r--src/libexpr/parser.y108
-rw-r--r--src/libexpr/primops.cc355
-rw-r--r--src/libexpr/primops.hh4
-rw-r--r--src/libexpr/primops/context.cc47
-rw-r--r--src/libexpr/primops/fetchClosure.cc36
-rw-r--r--src/libexpr/primops/fetchMercurial.cc16
-rw-r--r--src/libexpr/primops/fetchTree.cc51
-rw-r--r--src/libexpr/primops/fromTOML.cc4
-rw-r--r--src/libexpr/symbol-table.hh97
-rw-r--r--src/libexpr/value-to-json.cc14
-rw-r--r--src/libexpr/value-to-json.hh4
-rw-r--r--src/libexpr/value-to-xml.cc30
-rw-r--r--src/libexpr/value-to-xml.hh2
-rw-r--r--src/libexpr/value.hh14
31 files changed, 1126 insertions, 1064 deletions
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 32deecfae..94ab60f9a 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -41,13 +41,13 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
}
-std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string & attrPath,
+std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
Bindings & autoArgs, Value & vIn)
{
Strings tokens = parseAttrPath(attrPath);
Value * v = &vIn;
- Pos pos = noPos;
+ PosIdx pos = noPos;
for (auto & attr : tokens) {
@@ -77,13 +77,13 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
if (a == v->attrs->end()) {
std::set<std::string> attrNames;
for (auto & attr : *v->attrs)
- attrNames.insert(attr.name);
+ attrNames.insert(state.symbols[attr.name]);
auto suggestions = Suggestions::bestMatches(attrNames, attr);
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
}
v = &*a->value;
- pos = *a->pos;
+ pos = a->pos;
}
else {
@@ -106,7 +106,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
}
-Pos findPackageFilename(EvalState & state, Value & v, std::string what)
+std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
{
Value * v2;
try {
@@ -132,9 +132,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
throw ParseError("cannot parse line number '%s'", pos);
}
- Symbol file = state.symbols.create(filename);
-
- return { foFile, file, lineno, 0 };
+ return { std::move(filename), lineno };
}
diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh
index ff1135a06..117e0051b 100644
--- a/src/libexpr/attr-path.hh
+++ b/src/libexpr/attr-path.hh
@@ -10,14 +10,14 @@ namespace nix {
MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error);
-std::pair<Value *, Pos> findAlongAttrPath(
+std::pair<Value *, PosIdx> findAlongAttrPath(
EvalState & state,
const std::string & attrPath,
Bindings & autoArgs,
Value & vIn);
/* Heuristic to find the filename and lineno or a nix value. */
-Pos findPackageFilename(EvalState & state, Value & v, std::string what);
+std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what);
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc
index 52ac47e9b..877116f1f 100644
--- a/src/libexpr/attr-set.cc
+++ b/src/libexpr/attr-set.cc
@@ -26,7 +26,7 @@ Bindings * EvalState::allocBindings(size_t capacity)
/* Create a new attribute named 'name' on an existing attribute set stored
in 'vAttrs' and return the newly allocated Value which is associated with
this attribute. */
-Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
+Value * EvalState::allocAttr(Value & vAttrs, Symbol name)
{
Value * v = allocValue();
vAttrs.attrs->push_back(Attr(name, v));
@@ -40,7 +40,7 @@ Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
}
-Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
+Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
{
auto value = state.allocValue();
bindings->push_back(Attr(name, value, pos));
@@ -48,7 +48,7 @@ Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
}
-Value & BindingsBuilder::alloc(std::string_view name, ptr<Pos> pos)
+Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
{
return alloc(state.symbols.create(name), pos);
}
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index cad9743ea..dcc73b506 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -15,18 +15,27 @@ struct Value;
/* Map one attribute name to its value. */
struct Attr
{
+ /* the placement of `name` and `pos` in this struct is important.
+ both of them are uint32 wrappers, they are next to each other
+ to make sure that Attr has no padding on 64 bit machines. that
+ way we keep Attr size at two words with no wasted space. */
Symbol name;
+ PosIdx pos;
Value * value;
- ptr<Pos> pos;
- Attr(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
- : name(name), value(value), pos(pos) { };
- Attr() : pos(&noPos) { };
+ Attr(Symbol name, Value * value, PosIdx pos = noPos)
+ : name(name), pos(pos), value(value) { };
+ Attr() { };
bool operator < (const Attr & a) const
{
return name < a.name;
}
};
+static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
+ "performance of the evaluator is highly sensitive to the size of Attr. "
+ "avoid introducing any padding into Attr if at all possible, and do not "
+ "introduce new fields that need not be present for almost every instance.");
+
/* Bindings contains all the attributes of an attribute set. It is defined
by its size and its capacity, the capacity being the number of Attr
elements allocated after this structure, while the size corresponds to
@@ -35,13 +44,13 @@ class Bindings
{
public:
typedef uint32_t size_t;
- ptr<Pos> pos;
+ PosIdx pos;
private:
size_t size_, capacity_;
Attr attrs[0];
- Bindings(size_t capacity) : pos(&noPos), size_(0), capacity_(capacity) { }
+ Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
Bindings(const Bindings & bindings) = delete;
public:
@@ -57,7 +66,7 @@ public:
attrs[size_++] = attr;
}
- iterator find(const Symbol & name)
+ iterator find(Symbol name)
{
Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key);
@@ -65,7 +74,7 @@ public:
return end();
}
- Attr * get(const Symbol & name)
+ Attr * get(Symbol name)
{
Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key);
@@ -73,18 +82,6 @@ public:
return nullptr;
}
- Attr & need(const Symbol & name, const Pos & pos = noPos)
- {
- auto a = get(name);
- if (!a)
- throw Error({
- .msg = hintfmt("attribute '%s' missing", name),
- .errPos = pos
- });
-
- return *a;
- }
-
iterator begin() { return &attrs[0]; }
iterator end() { return &attrs[size_]; }
@@ -98,14 +95,15 @@ public:
size_t capacity() { return capacity_; }
/* Returns the attributes in lexicographically sorted order. */
- std::vector<const Attr *> lexicographicOrder() const
+ std::vector<const Attr *> lexicographicOrder(const SymbolTable & symbols) const
{
std::vector<const Attr *> res;
res.reserve(size_);
for (size_t n = 0; n < size_; n++)
res.emplace_back(&attrs[n]);
- std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
- return (const std::string &) a->name < (const std::string &) b->name;
+ std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
+ std::string_view sa = symbols[a->name], sb = symbols[b->name];
+ return sa < sb;
});
return res;
}
@@ -130,7 +128,7 @@ public:
: bindings(bindings), state(state)
{ }
- void insert(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
+ void insert(Symbol name, Value * value, PosIdx pos = noPos)
{
insert(Attr(name, value, pos));
}
@@ -145,9 +143,9 @@ public:
bindings->push_back(attr);
}
- Value & alloc(const Symbol & name, ptr<Pos> pos = ptr(&noPos));
+ Value & alloc(Symbol name, PosIdx pos = noPos);
- Value & alloc(std::string_view name, ptr<Pos> pos = ptr(&noPos));
+ Value & alloc(std::string_view name, PosIdx pos = noPos);
Bindings * finish()
{
diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc
deleted file mode 100644
index e50ff244c..000000000
--- a/src/libexpr/common-eval-args.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-#include "common-eval-args.hh"
-#include "shared.hh"
-#include "filetransfer.hh"
-#include "util.hh"
-#include "eval.hh"
-#include "fetchers.hh"
-#include "registry.hh"
-#include "flake/flakeref.hh"
-#include "store-api.hh"
-
-namespace nix {
-
-MixEvalArgs::MixEvalArgs()
-{
- auto category = "Common evaluation options";
-
- addFlag({
- .longName = "arg",
- .description = "Pass the value *expr* as the argument *name* to Nix functions.",
- .category = category,
- .labels = {"name", "expr"},
- .handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }}
- });
-
- addFlag({
- .longName = "argstr",
- .description = "Pass the string *string* as the argument *name* to Nix functions.",
- .category = category,
- .labels = {"name", "string"},
- .handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }},
- });
-
- addFlag({
- .longName = "include",
- .shortName = 'I',
- .description = "Add *path* to the list of locations used to look up `<...>` file names.",
- .category = category,
- .labels = {"path"},
- .handler = {[&](std::string s) { searchPath.push_back(s); }}
- });
-
- addFlag({
- .longName = "impure",
- .description = "Allow access to mutable paths and repositories.",
- .category = category,
- .handler = {[&]() {
- evalSettings.pureEval = false;
- }},
- });
-
- addFlag({
- .longName = "override-flake",
- .description = "Override the flake registries, redirecting *original-ref* to *resolved-ref*.",
- .category = category,
- .labels = {"original-ref", "resolved-ref"},
- .handler = {[&](std::string _from, std::string _to) {
- auto from = parseFlakeRef(_from, absPath("."));
- auto to = parseFlakeRef(_to, absPath("."));
- fetchers::Attrs extraAttrs;
- if (to.subdir != "") extraAttrs["dir"] = to.subdir;
- fetchers::overrideRegistry(from.input, to.input, extraAttrs);
- }}
- });
-
- addFlag({
- .longName = "eval-store",
- .description = "The Nix store to use for evaluations.",
- .category = category,
- .labels = {"store-url"},
- .handler = {&evalStoreUrl},
- });
-}
-
-Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
-{
- auto res = state.buildBindings(autoArgs.size());
- for (auto & i : autoArgs) {
- auto v = state.allocValue();
- if (i.second[0] == 'E')
- state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath(".")));
- else
- v->mkString(((std::string_view) i.second).substr(1));
- res.insert(state.symbols.create(i.first), v);
- }
- return res.finish();
-}
-
-Path lookupFileArg(EvalState & state, std::string_view s)
-{
- if (isUri(s)) {
- return state.store->toRealPath(
- fetchers::downloadTarball(
- state.store, resolveUri(s), "source", false).first.storePath);
- } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
- Path p(s.substr(1, s.size() - 2));
- return state.findFile(p);
- } else
- return absPath(std::string(s));
-}
-
-}
diff --git a/src/libexpr/common-eval-args.hh b/src/libexpr/common-eval-args.hh
deleted file mode 100644
index 03fa226aa..000000000
--- a/src/libexpr/common-eval-args.hh
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include "args.hh"
-
-namespace nix {
-
-class Store;
-class EvalState;
-class Bindings;
-
-struct MixEvalArgs : virtual Args
-{
- MixEvalArgs();
-
- Bindings * getAutoArgs(EvalState & state);
-
- Strings searchPath;
-
- std::optional<std::string> evalStoreUrl;
-
-private:
- std::map<std::string, std::string> autoArgs;
-};
-
-Path lookupFileArg(EvalState & state, std::string_view s);
-
-}
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 0608d8378..236e07d14 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -35,9 +35,15 @@ struct AttrDb
std::unique_ptr<Sync<State>> _state;
- AttrDb(const Store & cfg, const Hash & fingerprint)
+ SymbolTable & symbols;
+
+ AttrDb(
+ const Store & cfg,
+ const Hash & fingerprint,
+ SymbolTable & symbols)
: cfg(cfg)
, _state(std::make_unique<Sync<State>>())
+ , symbols(symbols)
{
auto state(_state->lock());
@@ -100,7 +106,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::FullAttrs)
(0, false).exec();
@@ -110,7 +116,7 @@ struct AttrDb
for (auto & attr : attrs)
state->insertAttribute.use()
(rowId)
- (attr)
+ (symbols[attr])
(AttrType::Placeholder)
(0, false).exec();
@@ -135,14 +141,14 @@ struct AttrDb
}
state->insertAttributeWithContext.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::String)
(s)
(ctx).exec();
} else {
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::String)
(s).exec();
}
@@ -161,7 +167,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Bool)
(b ? 1 : 0).exec();
@@ -177,7 +183,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Placeholder)
(0, false).exec();
@@ -193,7 +199,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Missing)
(0, false).exec();
@@ -209,7 +215,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Misc)
(0, false).exec();
@@ -225,7 +231,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Failed)
(0, false).exec();
@@ -233,13 +239,11 @@ struct AttrDb
});
}
- std::optional<std::pair<AttrId, AttrValue>> getAttr(
- AttrKey key,
- SymbolTable & symbols)
+ std::optional<std::pair<AttrId, AttrValue>> getAttr(AttrKey key)
{
auto state(_state->lock());
- auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
+ auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
if (!queryAttribute.next()) return {};
auto rowId = (AttrType) queryAttribute.getInt(0);
@@ -253,7 +257,7 @@ struct AttrDb
std::vector<Symbol> attrs;
auto queryAttributes(state->queryAttributes.use()(rowId));
while (queryAttributes.next())
- attrs.push_back(symbols.create(queryAttributes.getStr(0)));
+ attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
return {{rowId, attrs}};
}
case AttrType::String: {
@@ -277,10 +281,13 @@ struct AttrDb
}
};
-static std::shared_ptr<AttrDb> makeAttrDb(const Store & cfg, const Hash & fingerprint)
+static std::shared_ptr<AttrDb> makeAttrDb(
+ const Store & cfg,
+ const Hash & fingerprint,
+ SymbolTable & symbols)
{
try {
- return std::make_shared<AttrDb>(cfg, fingerprint);
+ return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
} catch (SQLiteError &) {
ignoreException();
return nullptr;
@@ -291,7 +298,7 @@ EvalCache::EvalCache(
std::optional<std::reference_wrapper<const Hash>> useCache,
EvalState & state,
RootLoader rootLoader)
- : db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
+ : db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
, state(state)
, rootLoader(rootLoader)
{
@@ -306,9 +313,9 @@ Value * EvalCache::getRootValue()
return *value;
}
-std::shared_ptr<AttrCursor> EvalCache::getRoot()
+ref<AttrCursor> EvalCache::getRoot()
{
- return std::make_shared<AttrCursor>(ref(shared_from_this()), std::nullopt);
+ return make_ref<AttrCursor>(ref(shared_from_this()), std::nullopt);
}
AttrCursor::AttrCursor(
@@ -327,8 +334,7 @@ AttrKey AttrCursor::getKey()
if (!parent)
return {0, root->state.sEpsilon};
if (!parent->first->cachedValue) {
- parent->first->cachedValue = root->db->getAttr(
- parent->first->getKey(), root->state.symbols);
+ parent->first->cachedValue = root->db->getAttr(parent->first->getKey());
assert(parent->first->cachedValue);
}
return {parent->first->cachedValue->first, parent->second};
@@ -369,12 +375,12 @@ std::vector<Symbol> AttrCursor::getAttrPath(Symbol name) const
std::string AttrCursor::getAttrPathStr() const
{
- return concatStringsSep(".", getAttrPath());
+ return concatStringsSep(".", root->state.symbols.resolve(getAttrPath()));
}
std::string AttrCursor::getAttrPathStr(Symbol name) const
{
- return concatStringsSep(".", getAttrPath(name));
+ return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name)));
}
Value & AttrCursor::forceValue()
@@ -414,25 +420,25 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
auto attrNames = getAttrs();
std::set<std::string> strAttrNames;
for (auto & name : attrNames)
- strAttrNames.insert(std::string(name));
+ strAttrNames.insert(root->state.symbols[name]);
- return Suggestions::bestMatches(strAttrNames, name);
+ return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]);
}
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue) {
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
for (auto & attr : *attrs)
if (attr == name)
- return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), name));
+ return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), attr));
return nullptr;
} else if (std::get_if<placeholder_t>(&cachedValue->second)) {
- auto attr = root->db->getAttr({cachedValue->first, name}, root->state.symbols);
+ auto attr = root->db->getAttr({cachedValue->first, name});
if (attr) {
if (std::get_if<missing_t>(&attr->second))
return nullptr;
@@ -522,7 +528,7 @@ std::string AttrCursor::getString()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
debug("using cached string attribute '%s'", getAttrPathStr());
@@ -552,7 +558,7 @@ string_t AttrCursor::getStringWithContext()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
bool valid = true;
@@ -594,7 +600,7 @@ bool AttrCursor::getBool()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto b = std::get_if<bool>(&cachedValue->second)) {
debug("using cached Boolean attribute '%s'", getAttrPathStr());
@@ -624,7 +630,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
debug("using cached attrset attribute '%s'", getAttrPathStr());
@@ -650,8 +656,9 @@ std::vector<Symbol> AttrCursor::getAttrs()
std::vector<Symbol> attrs;
for (auto & attr : *getValue().attrs)
attrs.push_back(attr.name);
- std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
- return (const std::string &) a < (const std::string &) b;
+ std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) {
+ std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b];
+ return sa < sb;
});
if (root->db)
diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh
index c9a9bf471..b0709ebc2 100644
--- a/src/libexpr/eval-cache.hh
+++ b/src/libexpr/eval-cache.hh
@@ -33,7 +33,7 @@ public:
EvalState & state,
RootLoader rootLoader);
- std::shared_ptr<AttrCursor> getRoot();
+ ref<AttrCursor> getRoot();
};
enum AttrType {
@@ -104,6 +104,8 @@ public:
ref<AttrCursor> getAttr(std::string_view name);
+ /* Get an attribute along a chain of attrsets. Note that this does
+ not auto-call functors or functions. */
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
std::string getString();
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index 9b0073822..4e0826101 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -2,42 +2,8 @@
#include "eval.hh"
-#define LocalNoInline(f) static f __attribute__((noinline)); f
-#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
-
namespace nix {
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, EvalState &evalState))
-{
- auto error = EvalError({
- .msg = hintfmt(s),
- .errPos = pos
- });
-
- if (debuggerHook && !evalState.debugTraces.empty()) {
- DebugTrace &last = evalState.debugTraces.front();
- debuggerHook(&error, last.env, last.expr);
- }
-
- throw error;
-}
-
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, EvalState &evalState))
-{
- auto error = TypeError({
- .msg = hintfmt(s, showType(v)),
- .errPos = pos
- });
-
- if (debuggerHook && !evalState.debugTraces.empty()) {
- DebugTrace &last = evalState.debugTraces.front();
- debuggerHook(&error, last.env, last.expr);
- }
-
- throw error;
-}
-
-
/* Note: Various places expect the allocated memory to be zeroed. */
[[gnu::always_inline]]
inline void * allocBytes(size_t n)
@@ -113,7 +79,7 @@ Env & EvalState::allocEnv(size_t size)
[[gnu::always_inline]]
-void EvalState::forceValue(Value & v, const Pos & pos)
+void EvalState::forceValue(Value & v, const PosIdx pos)
{
forceValue(v, [&]() { return pos; });
}
@@ -142,7 +108,7 @@ void EvalState::forceValue(Value & v, Callable getPos)
[[gnu::always_inline]]
-inline void EvalState::forceAttrs(Value & v, const Pos & pos)
+inline void EvalState::forceAttrs(Value & v, const PosIdx pos)
{
forceAttrs(v, [&]() { return pos; });
}
@@ -159,7 +125,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
[[gnu::always_inline]]
-inline void EvalState::forceList(Value & v, const Pos & pos)
+inline void EvalState::forceList(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (!v.isList())
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index d0e147733..896242e0c 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -96,7 +96,8 @@ RootValue allocRootValue(Value * v)
}
-void Value::print(std::ostream & str, std::set<const void *> * seen) const
+void Value::print(const SymbolTable & symbols, std::ostream & str,
+ std::set<const void *> * seen) const
{
checkInterrupt();
@@ -129,9 +130,9 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
str << "«repeated»";
else {
str << "{ ";
- for (auto & i : attrs->lexicographicOrder()) {
- str << i->name << " = ";
- i->value->print(str, seen);
+ for (auto & i : attrs->lexicographicOrder(symbols)) {
+ str << symbols[i->name] << " = ";
+ i->value->print(symbols, str, seen);
str << "; ";
}
str << "}";
@@ -146,7 +147,7 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
else {
str << "[ ";
for (auto v2 : listItems()) {
- v2->print(str, seen);
+ v2->print(symbols, str, seen);
str << " ";
}
str << "]";
@@ -177,17 +178,18 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
}
-void Value::print(std::ostream & str, bool showRepeated) const
+void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const
{
std::set<const void *> seen;
- print(str, showRepeated ? nullptr : &seen);
+ print(symbols, str, showRepeated ? nullptr : &seen);
}
-std::ostream & operator << (std::ostream & str, const Value & v)
+std::string printValue(const EvalState & state, const Value & v)
{
- v.print(str, false);
- return str;
+ std::ostringstream out;
+ v.print(state.symbols, out);
+ return out.str();
}
@@ -236,10 +238,10 @@ std::string showType(const Value & v)
}
}
-Pos Value::determinePos(const Pos & pos) const
+PosIdx Value::determinePos(const PosIdx pos) const
{
switch (internalType) {
- case tAttrs: return *attrs->pos;
+ case tAttrs: return attrs->pos;
case tLambda: return lambda.fun->pos;
case tApp: return app.left->determinePos(pos);
default: return pos;
@@ -308,7 +310,7 @@ static BoehmGCStackAllocator boehmGCStackAllocator;
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
{
- if (name.symbol.set()) {
+ if (name.symbol) {
return name.symbol;
} else {
Value nameValue;
@@ -641,20 +643,20 @@ Value * EvalState::addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp)
{
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
- Symbol sym = symbols.create(name2);
+ auto sym = symbols.create(name2);
/* Hack to make constants lazy: turn them into a application of
the primop to a dummy value. */
if (arity == 0) {
auto vPrimOp = allocValue();
- vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
+ vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 });
Value v;
v.mkApp(vPrimOp, vPrimOp);
return addConstant(name, v);
}
Value * v = allocValue();
- v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym });
+ v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
@@ -669,21 +671,21 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
if (primOp.arity == 0) {
primOp.arity = 1;
auto vPrimOp = allocValue();
- vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
+ vPrimOp->mkPrimOp(new PrimOp(primOp));
Value v;
v.mkApp(vPrimOp, vPrimOp);
return addConstant(primOp.name, v);
}
- Symbol envName = primOp.name;
+ auto envName = symbols.create(primOp.name);
if (hasPrefix(primOp.name, "__"))
- primOp.name = symbols.create(std::string(primOp.name, 2));
+ primOp.name = primOp.name.substr(2);
Value * v = allocValue();
- v->mkPrimOp(new PrimOp(std::move(primOp)));
+ v->mkPrimOp(new PrimOp(primOp));
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
- baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
+ baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
return v;
}
@@ -700,7 +702,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
auto v2 = &v;
if (v2->primOp->doc)
return Doc {
- .pos = noPos,
+ .pos = {},
.name = v2->primOp->name,
.arity = v2->primOp->arity,
.args = v2->primOp->args,
@@ -819,12 +821,42 @@ valmap * mapStaticEnvBindings(const StaticEnv &se, const Env &env)
evaluator. So here are some helper functions for throwing
exceptions. */
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, EvalState &evalState))
+void EvalState::throwEvalError(const PosIdx pos, const char * s) const
+{
+ auto error = EvalError({
+ .msg = hintfmt(s),
+ .errPos = positions[pos]
+ });
+
+ if (debuggerHook && !debugTraces.empty()) {
+ DebugTrace &last = debugTraces.front();
+ debuggerHook(&error, last.env, last.expr);
+ }
+
+ throw error;
+}
+
+void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
+{
+ auto error = TypeError({
+ .msg = hintfmt(s, showType(v)),
+ .errPos = positions[pos]
+ });
+
+ if (debuggerHook && !debugTraces.empty()) {
+ DebugTrace &last = debugTraces.front();
+ debuggerHook(&error, last.env, last.expr);
+ }
+
+ throw error;
+}
+
+void EvalState::throwEvalError(const char * s, const std::string & s2) const
{
auto error = EvalError(s, s2);
- if (debuggerHook && !evalState.debugTraces.empty()) {
- DebugTrace &last = evalState.debugTraces.front();
+ if (debuggerHook && !debugTraces.empty()) {
+ DebugTrace &last = debugTraces.front();
debuggerHook(&error, last.env, last.expr);
}
@@ -840,12 +872,13 @@ void EvalState::debugLastTrace(Error & e) {
}
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2, Env & env, Expr &expr))
+void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const std::string & s2, Env & env, Expr &expr) const
{
auto error = EvalError({
.msg = hintfmt(s, s2),
- .errPos = pos,
- .suggestions = suggestions
+ .errPos = positions[pos],
+ .suggestions = suggestions,
});
if (debuggerHook)
@@ -854,19 +887,19 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & s
throw error;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, EvalState &evalState))
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
{
auto error = EvalError({
.msg = hintfmt(s, s2),
- .errPos = pos
+ .errPos = positions[pos]
});
- evalState.debugLastTrace(error);
+ debugLastTrace(error);
throw error;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, Env & env, Expr &expr))
+void EvalState::throwEvalError(const char * s, const std::string & s2, const std::string & s3, Env & env, Expr &expr) const
{
auto error = EvalError({
.msg = hintfmt(s),
@@ -879,7 +912,8 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, Env &
throw error;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, Env & env, Expr &expr))
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
+ const std::string & s3, Env & env, Expr &expr) const
{
auto error = EvalError({
.msg = hintfmt(s, s2),
@@ -892,11 +926,12 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
throw error;
}
+/*
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3, EvalState &evalState))
{
auto error = EvalError({
.msg = hintfmt(s, s2, s3),
- .errPos = pos
+ .errPos = positions[pos]
});
evalState.debugLastTrace(error);
@@ -915,13 +950,14 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2
throw error;
}
+*/
-LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2, Env & env, Expr &expr))
+void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr &expr) const
{
// p1 is where the error occurred; p2 is a position mentioned in the message.
auto error = EvalError({
- .msg = hintfmt(s, sym, p2),
- .errPos = p1
+ .msg = hintfmt(s, symbols[sym], positions[p2]),
+ .errPos = positions[p1]
});
if (debuggerHook)
@@ -930,11 +966,11 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const
throw error;
}
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, EvalState &evalState))
+void EvalState::throwTypeError(const PosIdx pos, const char * s) const
{
auto error = TypeError({
.msg = hintfmt(s),
- .errPos = pos
+ .errPos = positions[pos]
});
evalState.debugLastTrace(error);
@@ -942,12 +978,12 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, EvalS
throw error;
}
-
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, Env & env, Expr &expr))
+void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
+ const Symbol s2, Env & env, Expr &expr) const
{
auto error = TypeError({
- .msg = hintfmt(s, v),
- .errPos = pos
+ .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
+ .errPos = positions[pos]
});
if (debuggerHook)
@@ -956,11 +992,13 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
throw error;
}
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr &expr))
+void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr) const
{
- auto error = TypeError({
- .msg = hintfmt(s, fun.showNamePos(), s2),
- .errPos = pos,
+ auto error = TypeError(ErrorInfo {
+ .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
+ .errPos = positions[pos],
+ .suggestions = suggestions,
});
if (debuggerHook)
@@ -969,7 +1007,7 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
throw error;
}
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr &expr))
+void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr) const
{
auto error = TypeError({
.msg = hintfmt(s, fun.showNamePos(), s2),
@@ -983,11 +1021,11 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & s
throw error;
}
-LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1, Env & env, Expr &expr))
+void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) const
{
auto error = AssertionError({
.msg = hintfmt(s, s1),
- .errPos = pos
+ .errPos = positions[pos]
});
if (debuggerHook)
@@ -996,11 +1034,11 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
throw error;
}
-LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1, Env & env, const Expr &expr))
+void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) const
{
auto error = UndefinedVarError({
.msg = hintfmt(s, s1),
- .errPos = pos
+ .errPos = positions[pos]
});
if (debuggerHook)
@@ -1009,11 +1047,11 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
throw error;
}
-LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1, Env & env, Expr &expr))
+void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) const
{
auto error = MissingArgumentError({
.msg = hintfmt(s, s1),
- .errPos = pos
+ .errPos = positions[pos]
});
if (debuggerHook)
@@ -1022,14 +1060,14 @@ LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char
throw error;
}
-LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2))
+void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
{
e.addTrace(std::nullopt, s, s2);
}
-LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2))
+void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
{
- e.addTrace(pos, s, s2);
+ e.addTrace(positions[pos], s, s2);
}
LocalNoInline(std::unique_ptr<DebugTraceStacker>
@@ -1108,12 +1146,11 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
}
Bindings::iterator j = env->values[0]->attrs->find(var.name);
if (j != env->values[0]->attrs->end()) {
- if (countCalls) attrSelects[*j->pos]++;
+ if (countCalls) attrSelects[j->pos]++;
return j->value;
}
- if (!env->prevWith) {
- throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name, *env, var);
- }
+ if (!env->prevWith)
+ throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, var);
for (size_t l = env->prevWith; l; --l, env = env->up) ;
}
}
@@ -1142,13 +1179,14 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
}
-void EvalState::mkPos(Value & v, ptr<Pos> pos)
+void EvalState::mkPos(Value & v, PosIdx p)
{
- if (pos->file.set()) {
+ auto pos = positions[p];
+ if (!pos.file.empty()) {
auto attrs = buildBindings(3);
- attrs.alloc(sFile).mkString(pos->file);
- attrs.alloc(sLine).mkInt(pos->line);
- attrs.alloc(sColumn).mkInt(pos->column);
+ attrs.alloc(sFile).mkString(pos.file);
+ attrs.alloc(sLine).mkInt(pos.line);
+ attrs.alloc(sColumn).mkInt(pos.column);
v.mkAttrs(attrs);
} else
v.mkNull();
@@ -1291,7 +1329,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
}
-inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
+inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
{
Value v;
e->eval(*this, env, v);
@@ -1365,7 +1403,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
} else
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
env2.values[displ++] = vAttr;
- v.attrs->push_back(Attr(i.first, vAttr, ptr(&i.second.pos)));
+ v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
}
/* If the rec contains an attribute called `__overrides', then
@@ -1397,7 +1435,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
else
for (auto & i : attrs)
- v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), ptr(&i.second.pos)));
+ v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
/* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : dynamicAttrs) {
@@ -1407,18 +1445,18 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
if (nameVal.type() == nNull)
continue;
state.forceStringNoCtx(nameVal);
- Symbol nameSym = state.symbols.create(nameVal.string.s);
+ auto nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
- throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos, env, *this);
+ state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this);
i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
- v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), ptr(&i.pos)));
+ v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
v.attrs->sort(); // FIXME: inefficient
}
- v.attrs->pos = ptr(&pos);
+ v.attrs->pos = pos;
}
@@ -1463,10 +1501,12 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
for (auto & i : attrPath) {
if (!first) out << '.'; else first = false;
try {
- out << getName(i, state, env);
+ out << state.symbols[getName(i, state, env)];
} catch (Error & e) {
- assert(!i.symbol.set());
- out << "\"${" << *i.expr << "}\"";
+ assert(!i.symbol);
+ out << "\"${";
+ i.expr->show(state.symbols, out);
+ out << "}\"";
}
}
return out.str();
@@ -1476,7 +1516,7 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{
Value vTmp;
- ptr<Pos> pos2(&noPos);
+ PosIdx pos2;
Value * vAttrs = &vTmp;
e->eval(state, env, vTmp);
@@ -1496,7 +1536,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) {
state.nrLookups++;
Bindings::iterator j;
- Symbol name = getName(i, state, env);
+ auto name = getName(i, state, env);
if (def) {
state.forceValue(*vAttrs, pos);
if (vAttrs->type() != nAttrs ||
@@ -1510,23 +1550,24 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
std::set<std::string> allAttrNames;
for (auto & attr : *vAttrs->attrs)
- allAttrNames.insert(attr.name);
- throwEvalError(
+ allAttrNames.insert(state.symbols[attr.name]);
+ state.throwEvalError(
pos,
- Suggestions::bestMatches(allAttrNames, name),
- "attribute '%1%' missing", name, env, *this);
+ Suggestions::bestMatches(allAttrNames, state.symbols[name]),
+ "attribute '%1%' missing", state.symbols[name], env, *this);
}
}
vAttrs = j->value;
pos2 = j->pos;
- if (state.countCalls) state.attrSelects[*pos2]++;
+ if (state.countCalls) state.attrSelects[pos2]++;
}
- state.forceValue(*vAttrs, (*pos2 != noPos ? *pos2 : this->pos ) );
+ state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
} catch (Error & e) {
- if (*pos2 != noPos && pos2->file != state.sDerivationNix)
- addErrorTrace(e, *pos2, "while evaluating the attribute '%1%'",
+ auto pos2r = state.positions[pos2];
+ if (pos2 && pos2r.file != state.derivationNixPath)
+ state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath));
throw;
}
@@ -1545,7 +1586,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) {
state.forceValue(*vAttrs, noPos);
Bindings::iterator j;
- Symbol name = getName(i, state, env);
+ auto name = getName(i, state, env);
if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
@@ -1566,9 +1607,11 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
}
-void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos)
+void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
{
- auto trace = evalSettings.traceFunctionCalls ? std::make_unique<FunctionCallTrace>(pos) : nullptr;
+ auto trace = evalSettings.traceFunctionCalls
+ ? std::make_unique<FunctionCallTrace>(positions[pos])
+ : nullptr;
forceValue(fun, pos);
@@ -1593,7 +1636,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
ExprLambda & lambda(*vCur.lambda.fun);
auto size =
- (lambda.arg.empty() ? 0 : 1) +
+ (!lambda.arg ? 0 : 1) +
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
Env & env2(allocEnv(size));
env2.up = vCur.lambda.env;
@@ -1605,7 +1648,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
else {
forceAttrs(*args[0], pos);
- if (!lambda.arg.empty())
+ if (lambda.arg)
env2.values[displ++] = args[0];
/* For each formal argument, get the actual argument. If
@@ -1633,10 +1676,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (!lambda.formals->has(i.name)) {
std::set<std::string> formalNames;
for (auto & formal : lambda.formals->formals)
- formalNames.insert(formal.name);
+ formalNames.insert(symbols[formal.name]);
throwTypeError(
pos,
- Suggestions::bestMatches(formalNames, i.name),
+ Suggestions::bestMatches(formalNames, symbols[i.name]),
"%1% called with unexpected argument '%2%'",
lambda, i.name, *fun.lambda.env, lambda);
}
@@ -1662,8 +1705,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
} catch (Error & e) {
if (loggerSettings.showTrace.get()) {
addErrorTrace(e, lambda.pos, "while evaluating %s",
- (lambda.name.set()
- ? "'" + (const std::string &) lambda.name + "'"
+ (lambda.name
+ ? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
}
@@ -1812,8 +1855,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See
-https://nixos.org/manual/nix/stable/#ss-functions.)",
- i.name,
+https://nixos.org/manual/nix/stable/#ss-functions.)", symbols[i.name],,
*fun.lambda.env, *fun.lambda.fun);
}
}
@@ -1845,8 +1887,8 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{
if (!state.evalBool(env, cond, pos)) {
std::ostringstream out;
- cond->show(out);
- throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
+ cond->show(state.symbols, out);
+ state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
}
body->eval(state, env, v);
}
@@ -1939,7 +1981,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
}
-void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos)
+void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
{
nrListConcats++;
@@ -2022,16 +2064,15 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
firstType = nFloat;
nf = n;
nf += vTmp.fpoint;
- } else {
- throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
- }
+ } else
+ state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
} else if (firstType == nFloat) {
if (vTmp.type() == nInt) {
nf += vTmp.integer;
} else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint;
} else
- throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
+ state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
} else {
if (s.empty()) s.reserve(es->size());
/* skip canonization of first path, which would only be not
@@ -2051,7 +2092,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
v.mkFloat(nf);
else if (firstType == nPath) {
if (!context.empty())
- throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this);
+ state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this););
v.mkPath(canonPath(str()));
} else
v.mkStringMove(c_str(), context);
@@ -2060,7 +2101,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
void ExprPos::eval(EvalState & state, Env & env, Value & v)
{
- state.mkPos(v, ptr(&pos));
+ state.mkPos(v, pos);
}
@@ -2090,7 +2131,7 @@ void EvalState::forceValueDeep(Value & v)
recurse(*i.value);
} catch (Error & e) {
- addErrorTrace(e, *i.pos, "while evaluating the attribute '%1%'", i.name);
+ addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", symbols[i.name]);
throw;
}
}
@@ -2105,7 +2146,7 @@ void EvalState::forceValueDeep(Value & v)
}
-NixInt EvalState::forceInt(Value & v, const Pos & pos)
+NixInt EvalState::forceInt(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nInt)
@@ -2115,7 +2156,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
}
-NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
+NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() == nInt)
@@ -2126,7 +2167,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
}
-bool EvalState::forceBool(Value & v, const Pos & pos)
+bool EvalState::forceBool(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nBool)
@@ -2141,7 +2182,7 @@ bool EvalState::isFunctor(Value & fun)
}
-void EvalState::forceFunction(Value & v, const Pos & pos)
+void EvalState::forceFunction(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v))
@@ -2149,7 +2190,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
}
-std::string_view EvalState::forceString(Value & v, const Pos & pos)
+std::string_view EvalState::forceString(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nString) {
@@ -2199,7 +2240,7 @@ NixStringContext Value::getContext(const Store & store)
}
-std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
+std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
{
auto s = forceString(v, pos);
copyContext(v, context);
@@ -2207,7 +2248,7 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos
}
-std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
+std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
{
auto s = forceString(v, pos);
if (v.string.context) {
@@ -2227,13 +2268,13 @@ bool EvalState::isDerivation(Value & v)
if (v.type() != nAttrs) return false;
Bindings::iterator i = v.attrs->find(sType);
if (i == v.attrs->end()) return false;
- forceValue(*i->value, *i->pos);
+ forceValue(*i->value, i->pos);
if (i->value->type() != nString) return false;
return strcmp(i->value->string.s, "derivation") == 0;
}
-std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
+std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore)
{
auto i = v.attrs->find(sToString);
@@ -2246,7 +2287,7 @@ std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value &
return {};
}
-BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
+BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath)
{
forceValue(v, pos);
@@ -2276,7 +2317,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
}
if (v.type() == nExternal)
- return v.external->coerceToString(pos, context, coerceMore, copyToStore);
+ return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
if (coerceMore) {
/* Note that `false' is represented as an empty string for
@@ -2328,7 +2369,7 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
}
-Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
+Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
@@ -2337,14 +2378,14 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
}
-StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context)
+StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, false, false).toOwned();
if (auto storePath = store->maybeParseStorePath(path))
return *storePath;
throw EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
- .errPos = pos
+ .errPos = positions[pos]
});
}
@@ -2514,14 +2555,14 @@ void EvalState::printStats()
auto list = topObj.list("functions");
for (auto & i : functionCalls) {
auto obj = list.object();
- if (i.first->name.set())
+ if (i.first->name)
obj.attr("name", (const std::string &) i.first->name);
else
obj.attr("name", nullptr);
- if (i.first->pos) {
- obj.attr("file", (const std::string &) i.first->pos.file);
- obj.attr("line", i.first->pos.line);
- obj.attr("column", i.first->pos.column);
+ if (auto pos = positions[i.first->pos]) {
+ obj.attr("file", (const std::string &) pos.file);
+ obj.attr("line", pos.line);
+ obj.attr("column", pos.column);
}
obj.attr("count", i.second);
}
@@ -2530,10 +2571,10 @@ void EvalState::printStats()
auto list = topObj.list("attributes");
for (auto & i : attrSelects) {
auto obj = list.object();
- if (i.first) {
- obj.attr("file", (const std::string &) i.first.file);
- obj.attr("line", i.first.line);
- obj.attr("column", i.first.column);
+ if (auto pos = positions[i.first]) {
+ obj.attr("file", (const std::string &) pos.file);
+ obj.attr("line", pos.line);
+ obj.attr("column", pos.column);
}
obj.attr("count", i.second);
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 11f5707a4..76bd63ca6 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -23,7 +23,7 @@ class StorePath;
enum RepairFlag : bool;
-typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
+typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
void printEnvBindings(const Expr &expr, const Env &env);
void printEnvBindings(const StaticEnv &se, const Env &env, int lvl = 0);
@@ -32,7 +32,7 @@ struct PrimOp
{
PrimOpFun fun;
size_t arity;
- Symbol name;
+ std::string name;
std::vector<std::string> args;
const char * doc = nullptr;
};
@@ -57,7 +57,8 @@ void copyContext(const Value & v, PathSet & context);
typedef std::map<Path, StorePath> SrcToStore;
-std::ostream & operator << (std::ostream & str, const Value & v);
+std::ostream & printValue(const EvalState & state, std::ostream & str, const Value & v);
+std::string printValue(const EvalState & state, const Value & v);
typedef std::pair<std::string, std::string> SearchPathElem;
@@ -84,6 +85,9 @@ class EvalState
{
public:
SymbolTable symbols;
+ PosTable positions;
+
+ static inline std::string derivationNixPath = "//builtin/derivation.nix";
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
@@ -222,7 +226,7 @@ public:
/* Look up a file in the search path. */
Path findFile(const std::string_view path);
- Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
+ Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
@@ -234,14 +238,14 @@ public:
/* Evaluation the expression, then verify that it has the expected
type. */
inline bool evalBool(Env & env, Expr * e);
- inline bool evalBool(Env & env, Expr * e, const Pos & pos);
+ inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
inline void evalAttrs(Env & env, Expr * e, Value & v);
/* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
- inline void forceValue(Value & v, const Pos & pos);
+ inline void forceValue(Value & v, const PosIdx pos);
template <typename Callable>
inline void forceValue(Value & v, Callable getPos);
@@ -251,33 +255,72 @@ public:
void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */
- NixInt forceInt(Value & v, const Pos & pos);
- NixFloat forceFloat(Value & v, const Pos & pos);
- bool forceBool(Value & v, const Pos & pos);
+ NixInt forceInt(Value & v, const PosIdx pos);
+ NixFloat forceFloat(Value & v, const PosIdx pos);
+ bool forceBool(Value & v, const PosIdx pos);
- void forceAttrs(Value & v, const Pos & pos);
+ void forceAttrs(Value & v, const PosIdx pos);
template <typename Callable>
inline void forceAttrs(Value & v, Callable getPos);
- inline void forceList(Value & v, const Pos & pos);
- void forceFunction(Value & v, const Pos & pos); // either lambda or primop
- 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);
+ inline void forceList(Value & v, const PosIdx pos);
+ void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
+ std::string_view forceString(Value & v, const PosIdx pos = noPos);
+ std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
+ std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s, const Value & v) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const std::string & s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const ExprLambda & fun, const Symbol s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const char * s, const Value & v) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const;
+
+ [[gnu::noinline]]
+ void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
+ [[gnu::noinline]]
+ void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
+public:
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
- std::optional<std::string> tryAttrsToString(const Pos & pos, Value & v,
+ std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a
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. */
- BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
+ BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
@@ -286,10 +329,10 @@ public:
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
- Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
+ Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
/* Like coerceToPath, but the result must be a store path. */
- StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context);
+ StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
public:
@@ -322,7 +365,7 @@ public:
struct Doc
{
Pos pos;
- std::optional<Symbol> name;
+ std::optional<std::string> name;
size_t arity;
std::vector<std::string> args;
const char * doc;
@@ -350,9 +393,9 @@ public:
bool isFunctor(Value & fun);
// FIXME: use std::span
- void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos);
+ void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
- void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos)
+ void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
{
Value * args[] = {&arg};
callFunction(fun, 1, args, vRes, pos);
@@ -366,7 +409,7 @@ public:
inline Value * allocValue();
inline Env & allocEnv(size_t size);
- Value * allocAttr(Value & vAttrs, const Symbol & name);
+ Value * allocAttr(Value & vAttrs, Symbol name);
Value * allocAttr(Value & vAttrs, std::string_view name);
Bindings * allocBindings(size_t capacity);
@@ -378,9 +421,9 @@ public:
void mkList(Value & v, size_t length);
void mkThunk_(Value & v, Expr * expr);
- void mkPos(Value & v, ptr<Pos> pos);
+ void mkPos(Value & v, PosIdx pos);
- void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos);
+ void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
/* Print statistics. */
void printStats();
@@ -408,7 +451,7 @@ private:
bool countCalls;
- typedef std::map<Symbol, size_t> PrimOpCalls;
+ typedef std::map<std::string, size_t> PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map<ExprLambda *, size_t> FunctionCalls;
@@ -416,7 +459,7 @@ private:
void incrFunctionCall(ExprLambda * fun);
- typedef std::map<Pos, size_t> AttrSelects;
+ typedef std::map<PosIdx, size_t> AttrSelects;
AttrSelects attrSelects;
friend struct ExprOpUpdate;
@@ -427,9 +470,9 @@ private:
friend struct ExprFloat;
friend struct ExprPath;
friend struct ExprSelect;
- friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
- friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
- friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
+ friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
+ friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
+ friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
friend struct Value;
};
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 22257c6b3..cbf4f0a6f 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -72,7 +72,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
return {std::move(tree), resolvedRef, lockedRef};
}
-static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
+static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
{
if (value.isThunk() && value.isTrivial())
state.forceValue(value, pos);
@@ -80,20 +80,20 @@ static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
static void expectType(EvalState & state, ValueType type,
- Value & value, const Pos & pos)
+ Value & value, const PosIdx pos)
{
forceTrivialValue(state, value, pos);
if (value.type() != type)
throw Error("expected %s but got %s at %s",
- showType(type), showType(value.type()), pos);
+ showType(type), showType(value.type()), state.positions[pos]);
}
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
- EvalState & state, Value * value, const Pos & pos,
+ EvalState & state, Value * value, const PosIdx pos,
const std::optional<Path> & baseDir, InputPath lockRootPath);
static FlakeInput parseFlakeInput(EvalState & state,
- const std::string & inputName, Value * value, const Pos & pos,
+ const std::string & inputName, Value * value, const PosIdx pos,
const std::optional<Path> & baseDir, InputPath lockRootPath)
{
expectType(state, nAttrs, *value, pos);
@@ -111,37 +111,39 @@ static FlakeInput parseFlakeInput(EvalState & state,
for (nix::Attr attr : *(value->attrs)) {
try {
if (attr.name == sUrl) {
- expectType(state, nString, *attr.value, *attr.pos);
+ expectType(state, nString, *attr.value, attr.pos);
url = attr.value->string.s;
attrs.emplace("url", *url);
} else if (attr.name == sFlake) {
- expectType(state, nBool, *attr.value, *attr.pos);
+ 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, lockRootPath);
+ input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
} else if (attr.name == sFollows) {
- expectType(state, nString, *attr.value, *attr.pos);
+ expectType(state, nString, *attr.value, attr.pos);
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:
- attrs.emplace(attr.name, attr.value->string.s);
+ attrs.emplace(state.symbols[attr.name], attr.value->string.s);
break;
case nBool:
- attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean });
+ attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
break;
case nInt:
- attrs.emplace(attr.name, (long unsigned int)attr.value->integer);
+ attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
break;
default:
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
- attr.name, showType(*attr.value));
+ state.symbols[attr.name], showType(*attr.value));
}
}
} catch (Error & e) {
- e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name));
+ e.addTrace(
+ state.positions[attr.pos],
+ hintfmt("in flake attribute '%s'", state.symbols[attr.name]));
throw;
}
}
@@ -150,13 +152,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
try {
input.ref = FlakeRef::fromAttrs(attrs);
} catch (Error & e) {
- e.addTrace(pos, hintfmt("in flake input"));
+ e.addTrace(state.positions[pos], hintfmt("in flake input"));
throw;
}
else {
attrs.erase("url");
if (!attrs.empty())
- throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
+ throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
if (url)
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
}
@@ -168,7 +170,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
}
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
- EvalState & state, Value * value, const Pos & pos,
+ EvalState & state, Value * value, const PosIdx pos,
const std::optional<Path> & baseDir, InputPath lockRootPath)
{
std::map<FlakeId, FlakeInput> inputs;
@@ -176,11 +178,11 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
expectType(state, nAttrs, *value, pos);
for (nix::Attr & inputAttr : *(*value).attrs) {
- inputs.emplace(inputAttr.name,
+ inputs.emplace(state.symbols[inputAttr.name],
parseFlakeInput(state,
- inputAttr.name,
+ state.symbols[inputAttr.name],
inputAttr.value,
- *inputAttr.pos,
+ inputAttr.pos,
baseDir,
lockRootPath));
}
@@ -218,28 +220,28 @@ static Flake getFlake(
Value vInfo;
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
- expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
+ expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0));
if (auto description = vInfo.attrs->get(state.sDescription)) {
- expectType(state, nString, *description->value, *description->pos);
+ expectType(state, nString, *description->value, description->pos);
flake.description = description->value->string.s;
}
auto sInputs = state.symbols.create("inputs");
if (auto inputs = vInfo.attrs->get(sInputs))
- flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath);
+ flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath);
auto sOutputs = state.symbols.create("outputs");
if (auto outputs = vInfo.attrs->get(sOutputs)) {
- expectType(state, nFunction, *outputs->value, *outputs->pos);
+ expectType(state, nFunction, *outputs->value, outputs->pos);
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
if (formal.name != state.sSelf)
- flake.inputs.emplace(formal.name, FlakeInput {
- .ref = parseFlakeRef(formal.name)
+ flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
+ .ref = parseFlakeRef(state.symbols[formal.name])
});
}
}
@@ -250,35 +252,41 @@ static Flake getFlake(
auto sNixConfig = state.symbols.create("nixConfig");
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
- expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos);
+ expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
for (auto & setting : *nixConfig->value->attrs) {
- forceTrivialValue(state, *setting.value, *setting.pos);
+ forceTrivialValue(state, *setting.value, setting.pos);
if (setting.value->type() == nString)
- flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))});
+ flake.config.settings.emplace(
+ state.symbols[setting.name],
+ std::string(state.forceStringNoCtx(*setting.value, setting.pos)));
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
flake.config.settings.emplace(
- setting.name,
- state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
+ state.symbols[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)});
+ flake.config.settings.emplace(
+ state.symbols[setting.name],
+ state.forceInt(*setting.value, setting.pos));
else if (setting.value->type() == nBool)
- flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }});
+ flake.config.settings.emplace(
+ state.symbols[setting.name],
+ Explicit<bool> { state.forceBool(*setting.value, setting.pos) });
else if (setting.value->type() == nList) {
std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) {
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.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
+ state.symbols[setting.name], showType(*setting.value));
+ ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos));
}
- flake.config.settings.insert({setting.name, ss});
+ flake.config.settings.emplace(state.symbols[setting.name], ss);
}
else
throw TypeError("flake configuration setting '%s' is %s",
- setting.name, showType(*setting.value));
+ state.symbols[setting.name], showType(*setting.value));
}
}
@@ -288,7 +296,7 @@ static Flake getFlake(
attr.name != sOutputs &&
attr.name != sNixConfig)
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
- lockedRef, attr.name, *attr.pos);
+ lockedRef, state.symbols[attr.name], state.positions[attr.pos]);
}
return flake;
@@ -704,12 +712,12 @@ void callFlake(EvalState & state,
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
}
-static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isLocked())
- throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
+ throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
callFlake(state,
lockFlake(state, flakeRef,
diff --git a/src/libexpr/function-trace.hh b/src/libexpr/function-trace.hh
index 472f2045e..e9a2526bd 100644
--- a/src/libexpr/function-trace.hh
+++ b/src/libexpr/function-trace.hh
@@ -8,7 +8,7 @@ namespace nix {
struct FunctionCallTrace
{
- const Pos & pos;
+ const Pos pos;
FunctionCallTrace(const Pos & pos);
~FunctionCallTrace();
};
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index bb7e77b61..11f2b279d 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -61,7 +61,7 @@ std::string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
- system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos);
+ system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos);
}
return system;
}
@@ -75,7 +75,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
if (i == attrs->end())
drvPath = {std::nullopt};
else
- drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)};
+ drvPath = {state->coerceToStorePath(i->pos, *i->value, context)};
}
return drvPath.value_or(std::nullopt);
}
@@ -95,7 +95,7 @@ StorePath DrvInfo::queryOutPath() const
Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context;
if (i != attrs->end())
- outPath = state->coerceToStorePath(*i->pos, *i->value, context);
+ outPath = state->coerceToStorePath(i->pos, *i->value, context);
}
if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
@@ -109,23 +109,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
/* Get the ‘outputs’ list. */
Bindings::iterator i;
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
- state->forceList(*i->value, *i->pos);
+ state->forceList(*i->value, i->pos);
/* For each output... */
for (auto elem : i->value->listItems()) {
- std::string output(state->forceStringNoCtx(*elem, *i->pos));
+ std::string output(state->forceStringNoCtx(*elem, i->pos));
if (withPaths) {
/* Evaluate the corresponding set. */
Bindings::iterator out = attrs->find(state->symbols.create(output));
if (out == attrs->end()) continue; // FIXME: throw error?
- state->forceAttrs(*out->value, *i->pos);
+ state->forceAttrs(*out->value, i->pos);
/* 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;
- outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
+ outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context));
} else
outputs.emplace(output, std::nullopt);
}
@@ -168,7 +168,7 @@ Bindings * DrvInfo::getMeta()
if (!attrs) return 0;
Bindings::iterator a = attrs->find(state->sMeta);
if (a == attrs->end()) return 0;
- state->forceAttrs(*a->value, *a->pos);
+ state->forceAttrs(*a->value, a->pos);
meta = a->value->attrs;
return meta;
}
@@ -179,7 +179,7 @@ StringSet DrvInfo::queryMetaNames()
StringSet res;
if (!getMeta()) return res;
for (auto & i : *meta)
- res.insert(i.name);
+ res.emplace(state->symbols[i.name]);
return res;
}
@@ -269,7 +269,7 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
{
getMeta();
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
- Symbol sym = state->symbols.create(name);
+ auto sym = state->symbols.create(name);
if (meta)
for (auto i : *meta)
if (i.name != sym)
@@ -356,11 +356,11 @@ static void getDerivations(EvalState & state, Value & vIn,
there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take
precedence). */
- for (auto & i : v.attrs->lexicographicOrder()) {
- debug("evaluating attribute '%1%'", i->name);
- if (!std::regex_match(std::string(i->name), attrRegex))
+ for (auto & i : v.attrs->lexicographicOrder(state.symbols)) {
+ debug("evaluating attribute '%1%'", state.symbols[i->name]);
+ if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
continue;
- std::string pathPrefix2 = addToPath(pathPrefix, i->name);
+ std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
@@ -369,7 +369,7 @@ static void getDerivations(EvalState & state, Value & vIn,
`recurseForDerivations = true' attribute. */
if (i->value->type() == nAttrs) {
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
- if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
+ if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index d574121b0..4c28b976e 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -28,9 +28,9 @@ using namespace nix;
namespace nix {
-static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
+static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
{
- return Pos(data->origin, data->file, loc.first_line, loc.first_column);
+ return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
}
#define CUR_POS makeCurPos(*yylloc, data)
@@ -155,7 +155,7 @@ or { return OR_KW; }
} catch (const boost::bad_lexical_cast &) {
throw ParseError({
.msg = hintfmt("invalid integer '%1%'", yytext),
- .errPos = CUR_POS,
+ .errPos = data->state.positions[CUR_POS],
});
}
return INT;
@@ -165,7 +165,7 @@ or { return OR_KW; }
if (errno != 0)
throw ParseError({
.msg = hintfmt("invalid float '%1%'", yytext),
- .errPos = CUR_POS,
+ .errPos = data->state.positions[CUR_POS],
});
return FLOAT;
}
@@ -294,7 +294,7 @@ or { return OR_KW; }
<INPATH_SLASH><<EOF>> {
throw ParseError({
.msg = hintfmt("path has a trailing slash"),
- .errPos = CUR_POS,
+ .errPos = data->state.positions[CUR_POS],
});
}
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 51b05de60..efaeba7c6 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -1,5 +1,7 @@
#include "nixexpr.hh"
#include "derivations.hh"
+#include "eval.hh"
+#include "symbol-table.hh"
#include "util.hh"
#include <cstdlib>
@@ -12,12 +14,6 @@ std::function<void(const Error * error, const Env & env, const Expr & expr)> deb
/* Displaying abstract syntax trees. */
-std::ostream & operator << (std::ostream & str, const Expr & e)
-{
- e.show(str);
- return str;
-}
-
static void showString(std::ostream & str, std::string_view s)
{
str << '"';
@@ -30,8 +26,10 @@ static void showString(std::ostream & str, std::string_view s)
str << '"';
}
-static void showId(std::ostream & str, std::string_view s)
+std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
{
+ std::string_view s = symbol;
+
if (s.empty())
str << "\"\"";
else if (s == "if") // FIXME: handle other keywords
@@ -40,7 +38,7 @@ static void showId(std::ostream & str, std::string_view s)
char c = s[0];
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
showString(str, s);
- return;
+ return str;
}
for (auto c : s)
if (!((c >= 'a' && c <= 'z') ||
@@ -48,89 +46,104 @@ static void showId(std::ostream & str, std::string_view s)
(c >= '0' && c <= '9') ||
c == '_' || c == '\'' || c == '-')) {
showString(str, s);
- return;
+ return str;
}
str << s;
}
-}
-
-std::ostream & operator << (std::ostream & str, const Symbol & sym)
-{
- showId(str, *sym.s);
return str;
}
-void Expr::show(std::ostream & str) const
+void Expr::show(const SymbolTable & symbols, std::ostream & str) const
{
abort();
}
-void ExprInt::show(std::ostream & str) const
+void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
{
str << n;
}
-void ExprFloat::show(std::ostream & str) const
+void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
{
str << nf;
}
-void ExprString::show(std::ostream & str) const
+void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
{
showString(str, s);
}
-void ExprPath::show(std::ostream & str) const
+void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
{
str << s;
}
-void ExprVar::show(std::ostream & str) const
+void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << name;
+ str << symbols[name];
}
-void ExprSelect::show(std::ostream & str) const
+void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(" << *e << ")." << showAttrPath(attrPath);
- if (def) str << " or (" << *def << ")";
+ str << "(";
+ e->show(symbols, str);
+ str << ")." << showAttrPath(symbols, attrPath);
+ if (def) {
+ str << " or (";
+ def->show(symbols, str);
+ str << ")";
+ }
}
-void ExprOpHasAttr::show(std::ostream & str) const
+void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "((" << *e << ") ? " << showAttrPath(attrPath) << ")";
+ str << "((";
+ e->show(symbols, str);
+ str << ") ? " << showAttrPath(symbols, attrPath) << ")";
}
-void ExprAttrs::show(std::ostream & str) const
+void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
{
if (recursive) str << "rec ";
str << "{ ";
typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted;
for (auto & i : attrs) sorted.push_back(&i);
- std::sort(sorted.begin(), sorted.end(), [](Attr a, Attr b) {
- return (const std::string &) a->first < (const std::string &) b->first;
- });
+ std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
+ std::string_view sa = symbols[a->first], sb = symbols[b->first];
+ return sa < sb;
+ });
for (auto & i : sorted) {
if (i->second.inherited)
- str << "inherit " << i->first << " " << "; ";
- else
- str << i->first << " = " << *i->second.e << "; ";
+ str << "inherit " << symbols[i->first] << " " << "; ";
+ else {
+ str << symbols[i->first] << " = ";
+ i->second.e->show(symbols, str);
+ str << "; ";
+ }
+ }
+ for (auto & i : dynamicAttrs) {
+ str << "\"${";
+ i.nameExpr->show(symbols, str);
+ str << "}\" = ";
+ i.valueExpr->show(symbols, str);
+ str << "; ";
}
- for (auto & i : dynamicAttrs)
- str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
str << "}";
}
-void ExprList::show(std::ostream & str) const
+void ExprList::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "[ ";
- for (auto & i : elems)
- str << "(" << *i << ") ";
+ for (auto & i : elems) {
+ str << "(";
+ i->show(symbols, str);
+ str << ") ";
+ }
str << "]";
}
-void ExprLambda::show(std::ostream & str) const
+void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(";
if (hasFormals()) {
@@ -138,74 +151,100 @@ void ExprLambda::show(std::ostream & str) const
bool first = true;
for (auto & i : formals->formals) {
if (first) first = false; else str << ", ";
- str << i.name;
- if (i.def) str << " ? " << *i.def;
+ str << symbols[i.name];
+ if (i.def) {
+ str << " ? ";
+ i.def->show(symbols, str);
+ }
}
if (formals->ellipsis) {
if (!first) str << ", ";
str << "...";
}
str << " }";
- if (!arg.empty()) str << " @ ";
+ if (arg) str << " @ ";
}
- if (!arg.empty()) str << arg;
- str << ": " << *body << ")";
+ if (arg) str << symbols[arg];
+ str << ": ";
+ body->show(symbols, str);
+ str << ")";
}
-void ExprCall::show(std::ostream & str) const
+void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << '(' << *fun;
+ str << '(';
+ fun->show(symbols, str);
for (auto e : args) {
str << ' ';
- str << *e;
+ e->show(symbols, str);
}
str << ')';
}
-void ExprLet::show(std::ostream & str) const
+void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(let ";
for (auto & i : attrs->attrs)
if (i.second.inherited) {
- str << "inherit " << i.first << "; ";
+ str << "inherit " << symbols[i.first] << "; ";
}
- else
- str << i.first << " = " << *i.second.e << "; ";
- str << "in " << *body << ")";
+ else {
+ str << symbols[i.first] << " = ";
+ i.second.e->show(symbols, str);
+ str << "; ";
+ }
+ str << "in ";
+ body->show(symbols, str);
+ str << ")";
}
-void ExprWith::show(std::ostream & str) const
+void ExprWith::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(with " << *attrs << "; " << *body << ")";
+ str << "(with ";
+ attrs->show(symbols, str);
+ str << "; ";
+ body->show(symbols, str);
+ str << ")";
}
-void ExprIf::show(std::ostream & str) const
+void ExprIf::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(if " << *cond << " then " << *then << " else " << *else_ << ")";
+ str << "(if ";
+ cond->show(symbols, str);
+ str << " then ";
+ then->show(symbols, str);
+ str << " else ";
+ else_->show(symbols, str);
+ str << ")";
}
-void ExprAssert::show(std::ostream & str) const
+void ExprAssert::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "assert " << *cond << "; " << *body;
+ str << "assert ";
+ cond->show(symbols, str);
+ str << "; ";
+ body->show(symbols, str);
}
-void ExprOpNot::show(std::ostream & str) const
+void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(! " << *e << ")";
+ str << "(! ";
+ e->show(symbols, str);
+ str << ")";
}
-void ExprConcatStrings::show(std::ostream & str) const
+void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const
{
bool first = true;
str << "(";
for (auto & i : *es) {
if (first) first = false; else str << " + ";
- str << *i.second;
+ i.second->show(symbols, str);
}
str << ")";
}
-void ExprPos::show(std::ostream & str) const
+void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "__curPos";
}
@@ -236,56 +275,57 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
}
-std::string showAttrPath(const AttrPath & attrPath)
+std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
for (auto & i : attrPath) {
if (!first) out << '.'; else first = false;
- if (i.symbol.set())
- out << i.symbol;
- else
- out << "\"${" << *i.expr << "}\"";
+ if (i.symbol)
+ out << symbols[i.symbol];
+ else {
+ out << "\"${";
+ i.expr->show(symbols, out);
+ out << "}\"";
+ }
}
return out.str();
}
-Pos noPos;
-
/* Computing levels/displacements for variables. */
-void Expr::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void Expr::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
abort();
}
-void ExprInt::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprInt::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
}
-void ExprFloat::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprFloat::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
}
-void ExprString::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprString::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
}
-void ExprPath::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprPath::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
}
-void ExprVar::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprVar::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
@@ -315,38 +355,38 @@ void ExprVar::bindVars(const std::shared_ptr<const StaticEnv> &env)
if (withLevel == -1)
{
throw UndefinedVarError({
- .msg = hintfmt("undefined variable '%1%'", name),
- .errPos = pos
+ .msg = hintfmt("undefined variable '%1%'", es.symbols[name]),
+ .errPos = es.positions[pos]
});
}
fromWith = true;
this->level = withLevel;
}
-void ExprSelect::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprSelect::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
- e->bindVars(env);
- if (def) def->bindVars(env);
+ e->bindVars(es, env);
+ if (def) def->bindVars(es, env);
for (auto & i : attrPath)
- if (!i.symbol.set())
- i.expr->bindVars(env);
+ if (!i.symbol)
+ i.expr->bindVars(es, env);
}
-void ExprOpHasAttr::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprOpHasAttr::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
- e->bindVars(env);
+ e->bindVars(es, env);
for (auto & i : attrPath)
- if (!i.symbol.set())
- i.expr->bindVars(env);
+ if (!i.symbol)
+ i.expr->bindVars(es, env);
}
-void ExprAttrs::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprAttrs::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
@@ -361,34 +401,34 @@ void ExprAttrs::bindVars(const std::shared_ptr<const StaticEnv> &env)
// No need to sort newEnv since attrs is in sorted order.
for (auto & i : attrs)
- i.second.e->bindVars(i.second.inherited ? env : newEnv);
+ i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
for (auto & i : dynamicAttrs) {
- i.nameExpr->bindVars(newEnv);
- i.valueExpr->bindVars(newEnv);
+ i.nameExpr->bindVars(es, newEnv);
+ i.valueExpr->bindVars(es, newEnv);
}
}
else {
for (auto & i : attrs)
- i.second.e->bindVars(env);
+ i.second.e->bindVars(es, env);
for (auto & i : dynamicAttrs) {
- i.nameExpr->bindVars(env);
- i.valueExpr->bindVars(env);
+ i.nameExpr->bindVars(es, env);
+ i.valueExpr->bindVars(es, env);
}
}
}
-void ExprList::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprList::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
for (auto & i : elems)
- i->bindVars(env);
+ i->bindVars(es, env);
}
-void ExprLambda::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprLambda::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
@@ -397,11 +437,11 @@ void ExprLambda::bindVars(const std::shared_ptr<const StaticEnv> &env)
new StaticEnv(
false, env.get(),
(hasFormals() ? formals->formals.size() : 0) +
- (arg.empty() ? 0 : 1)));
+ (!arg ? 0 : 1));
Displacement displ = 0;
- if (!arg.empty()) newEnv->vars.emplace_back(arg, displ++);
+ if (arg) newEnv.vars.emplace_back(arg, displ++);
if (hasFormals()) {
for (auto & i : formals->formals)
@@ -410,23 +450,23 @@ void ExprLambda::bindVars(const std::shared_ptr<const StaticEnv> &env)
newEnv->sort();
for (auto & i : formals->formals)
- if (i.def) i.def->bindVars(newEnv);
+ if (i.def) i.def->bindVars(es, newEnv);
}
- body->bindVars(newEnv);
+ body->bindVars(es, newEnv);
}
-void ExprCall::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprCall::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
- fun->bindVars(env);
+ fun->bindVars(es, env);
for (auto e : args)
- e->bindVars(env);
+ e->bindVars(es, env);
}
-void ExprLet::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprLet::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
@@ -440,12 +480,12 @@ void ExprLet::bindVars(const std::shared_ptr<const StaticEnv> &env)
// No need to sort newEnv since attrs->attrs is in sorted order.
for (auto & i : attrs->attrs)
- i.second.e->bindVars(i.second.inherited ? env : newEnv);
+ i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
- body->bindVars(newEnv);
+ body->bindVars(es, newEnv);
}
-void ExprWith::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprWith::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
@@ -462,48 +502,51 @@ void ExprWith::bindVars(const std::shared_ptr<const StaticEnv> &env)
break;
}
- attrs->bindVars(env);
- auto newEnv = std::shared_ptr<StaticEnv>(new StaticEnv(true, env.get()));
- body->bindVars(newEnv);
+ if (debuggerHook)
+ staticenv = env;
+
+ attrs->bindVars(es, env);
+ StaticEnv newEnv(true, &env);
+ body->bindVars(es, newEnv);
}
-void ExprIf::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprIf::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
- cond->bindVars(env);
- then->bindVars(env);
- else_->bindVars(env);
+ cond->bindVars(es, env);
+ then->bindVars(es, env);
+ else_->bindVars(es, env);
}
-void ExprAssert::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprAssert::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
- cond->bindVars(env);
- body->bindVars(env);
+ cond->bindVars(es, env);
+ body->bindVars(es, env);
}
-void ExprOpNot::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprOpNot::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
- e->bindVars(env);
+ e->bindVars(es, env);
}
-void ExprConcatStrings::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprConcatStrings::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
- for (auto & i : *es)
- i.second->bindVars(env);
+ for (auto & i : *this->es)
+ i.second->bindVars(es, env);
}
-void ExprPos::bindVars(const std::shared_ptr<const StaticEnv> &env)
+void ExprPos::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
{
if (debuggerHook)
staticenv = env;
@@ -513,21 +556,24 @@ void ExprPos::bindVars(const std::shared_ptr<const StaticEnv> &env)
/* Storing function names. */
-void Expr::setName(Symbol & name)
+void Expr::setName(Symbol name)
{
}
-void ExprLambda::setName(Symbol & name)
+void ExprLambda::setName(Symbol name)
{
this->name = name;
body->setName(name);
}
-std::string ExprLambda::showNamePos() const
+std::string ExprLambda::showNamePos(const EvalState & state) const
{
- return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos);
+ std::string id(name
+ ? concatStrings("'", state.symbols[name], "'")
+ : "anonymous function");
+ return fmt("%1% at %2%", id, state.positions[pos]);
}
@@ -537,8 +583,7 @@ std::string ExprLambda::showNamePos() const
size_t SymbolTable::totalSize() const
{
size_t n = 0;
- for (auto & i : store)
- n += i.size();
+ dump([&] (const std::string & s) { n += s.size(); });
return n;
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index db210e07b..d275a51e9 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -1,8 +1,12 @@
#pragma once
+#include <map>
+#include <vector>
+
#include "value.hh"
#include "symbol-table.hh"
#include "error.hh"
+#include "chunked-vector.hh"
namespace nix {
@@ -24,32 +28,92 @@ extern std::function<void(const Error * error, const Env & env, const Expr & exp
struct Pos
{
- Symbol file;
+ std::string file;
+ FileOrigin origin;
uint32_t line;
- FileOrigin origin:2;
- uint32_t column:30;
- Pos() : line(0), origin(foString), column(0) { };
- Pos(FileOrigin origin, const Symbol & file, uint32_t line, uint32_t column)
- : file(file), line(line), origin(origin), column(column) { };
- operator bool() const
+ uint32_t column;
+
+ explicit operator bool() const { return line > 0; }
+};
+
+class PosIdx {
+ friend class PosTable;
+
+private:
+ uint32_t id;
+
+ explicit PosIdx(uint32_t id): id(id) {}
+
+public:
+ PosIdx() : id(0) {}
+
+ explicit operator bool() const { return id > 0; }
+
+ bool operator<(const PosIdx other) const { return id < other.id; }
+};
+
+class PosTable
+{
+public:
+ class Origin {
+ friend PosTable;
+ private:
+ // must always be invalid by default, add() replaces this with the actual value.
+ // subsequent add() calls use this index as a token to quickly check whether the
+ // current origins.back() can be reused or not.
+ mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
+
+ explicit Origin(uint32_t idx): idx(idx), file{}, origin{} {}
+
+ public:
+ const std::string file;
+ const FileOrigin origin;
+
+ Origin(std::string file, FileOrigin origin): file(std::move(file)), origin(origin) {}
+ };
+
+ struct Offset {
+ uint32_t line, column;
+ };
+
+private:
+ std::vector<Origin> origins;
+ ChunkedVector<Offset, 8192> offsets;
+
+public:
+ PosTable(): offsets(1024)
+ {
+ origins.reserve(1024);
+ }
+
+ PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
{
- return line != 0;
+ const auto idx = offsets.add({line, column}).second;
+ if (origins.empty() || origins.back().idx != origin.idx) {
+ origin.idx = idx;
+ origins.push_back(origin);
+ }
+ return PosIdx(idx + 1);
}
- bool operator < (const Pos & p2) const
+ Pos operator[](PosIdx p) const
{
- if (!line) return p2.line;
- if (!p2.line) return false;
- int d = ((const std::string &) file).compare((const std::string &) p2.file);
- if (d < 0) return true;
- if (d > 0) return false;
- if (line < p2.line) return true;
- if (line > p2.line) return false;
- return column < p2.column;
+ if (p.id == 0 || p.id > offsets.size())
+ return {};
+ const auto idx = p.id - 1;
+ /* we want the last key <= idx, so we'll take prev(first key > idx).
+ this is guaranteed to never rewind origin.begin because the first
+ key is always 0. */
+ const auto pastOrigin = std::upper_bound(
+ origins.begin(), origins.end(), Origin(idx),
+ [] (const auto & a, const auto & b) { return a.idx < b.idx; });
+ const auto origin = *std::prev(pastOrigin);
+ const auto offset = offsets[idx];
+ return {origin.file, origin.origin, offset.line, offset.column};
}
};
-extern Pos noPos;
+inline PosIdx noPos = {};
std::ostream & operator << (std::ostream & str, const Pos & pos);
@@ -65,13 +129,13 @@ struct AttrName
{
Symbol symbol;
Expr * expr;
- AttrName(const Symbol & s) : symbol(s) {};
+ AttrName(Symbol s) : symbol(s) {};
AttrName(Expr * e) : expr(e) {};
};
typedef std::vector<AttrName> AttrPath;
-std::string showAttrPath(const AttrPath & attrPath);
+std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath);
/* Abstract syntax of Nix expressions. */
@@ -79,22 +143,19 @@ std::string showAttrPath(const AttrPath & attrPath);
struct Expr
{
virtual ~Expr() { };
- virtual void show(std::ostream & str) const;
- virtual void bindVars(const std::shared_ptr<const StaticEnv> & env);
+ virtual void show(const SymbolTable & symbols, std::ostream & str) const;
+ virtual void bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> & env);
virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env);
- virtual void setName(Symbol & name);
-
+ virtual void setName(Symbol name);
std::shared_ptr<const StaticEnv> staticenv;
- virtual const Pos* getPos() const = 0;
+ virtual const PosIdx getPos() const = 0;
};
-std::ostream & operator << (std::ostream & str, const Expr & e);
-
#define COMMON_METHODS \
- void show(std::ostream & str) const; \
+ void show(const SymbolTable & symbols, std::ostream & str) const; \
void eval(EvalState & state, Env & env, Value & v); \
- void bindVars(const std::shared_ptr<const StaticEnv> & env);
+ void bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> & env);
struct ExprInt : Expr
{
@@ -102,7 +163,7 @@ struct ExprInt : Expr
Value v;
ExprInt(NixInt n) : n(n) { v.mkInt(n); };
Value * maybeThunk(EvalState & state, Env & env);
- const Pos* getPos() const { return 0; }
+ const PosIdx getPos() const { return noPos; }
COMMON_METHODS
};
@@ -112,7 +173,7 @@ struct ExprFloat : Expr
Value v;
ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); };
Value * maybeThunk(EvalState & state, Env & env);
- const Pos* getPos() const { return 0; }
+ const PosIdx getPos() const { return noPos; }
COMMON_METHODS
};
@@ -122,7 +183,7 @@ struct ExprString : Expr
Value v;
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
Value * maybeThunk(EvalState & state, Env & env);
- const Pos* getPos() const { return 0; }
+ const PosIdx getPos() const { return noPos; }
COMMON_METHODS
};
@@ -132,7 +193,7 @@ struct ExprPath : Expr
Value v;
ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
Value * maybeThunk(EvalState & state, Env & env);
- const Pos* getPos() const { return 0; }
+ const PosIdx getPos() const { return noPos; }
COMMON_METHODS
};
@@ -141,7 +202,7 @@ typedef uint32_t Displacement;
struct ExprVar : Expr
{
- Pos pos;
+ PosIdx pos;
Symbol name;
/* Whether the variable comes from an environment (e.g. a rec, let
@@ -157,21 +218,21 @@ struct ExprVar : Expr
Level level;
Displacement displ;
- ExprVar(const Symbol & name) : name(name) { };
- ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
+ ExprVar(Symbol name) : name(name) { };
+ ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
Value * maybeThunk(EvalState & state, Env & env);
- const Pos* getPos() const { return &pos; }
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
struct ExprSelect : Expr
{
- Pos pos;
+ PosIdx pos;
Expr * e, * def;
AttrPath attrPath;
- ExprSelect(const Pos & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
- ExprSelect(const Pos & pos, Expr * e, const Symbol & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
- const Pos* getPos() const { return &pos; }
+ ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
+ ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
@@ -180,20 +241,20 @@ struct ExprOpHasAttr : Expr
Expr * e;
AttrPath attrPath;
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
- const Pos* getPos() const { return e->getPos(); }
+ const PosIdx getPos() const { return e->getPos(); }
COMMON_METHODS
};
struct ExprAttrs : Expr
{
bool recursive;
- Pos pos;
+ PosIdx pos;
struct AttrDef {
bool inherited;
Expr * e;
- Pos pos;
+ PosIdx pos;
Displacement displ; // displacement
- AttrDef(Expr * e, const Pos & pos, bool inherited=false)
+ AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
: inherited(inherited), e(e), pos(pos) { };
AttrDef() { };
};
@@ -201,15 +262,15 @@ struct ExprAttrs : Expr
AttrDefs attrs;
struct DynamicAttrDef {
Expr * nameExpr, * valueExpr;
- Pos pos;
- DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const Pos & pos)
+ PosIdx pos;
+ DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos)
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
};
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs;
- ExprAttrs(const Pos &pos) : recursive(false), pos(pos) { };
- ExprAttrs() : recursive(false), pos(noPos) { };
- const Pos* getPos() const { return &pos; }
+ ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
+ ExprAttrs() : recursive(false) { };
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
@@ -217,16 +278,15 @@ struct ExprList : Expr
{
std::vector<Expr *> elems;
ExprList() { };
- const Pos* getPos() const { return 0; }
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
struct Formal
{
- Pos pos;
+ PosIdx pos;
Symbol name;
Expr * def;
- Formal(const Pos & pos, const Symbol & name, Expr * def) : pos(pos), name(name), def(def) { };
};
struct Formals
@@ -235,18 +295,20 @@ struct Formals
Formals_ formals;
bool ellipsis;
- bool has(Symbol arg) const {
+ 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> lexicographicOrder(const SymbolTable & symbols) 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);
+ [&] (const Formal & a, const Formal & b) {
+ std::string_view sa = symbols[a.name], sb = symbols[b.name];
+ return sa < sb;
});
return result;
}
@@ -254,19 +316,23 @@ struct Formals
struct ExprLambda : Expr
{
- Pos pos;
+ PosIdx pos;
Symbol name;
Symbol arg;
Formals * formals;
Expr * body;
- ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
+ ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body)
{
};
- void setName(Symbol & name);
- std::string showNamePos() const;
+ ExprLambda(PosIdx pos, Formals * formals, Expr * body)
+ : pos(pos), formals(formals), body(body)
+ {
+ }
+ void setName(Symbol name);
+ std::string showNamePos(const EvalState & state) const;
inline bool hasFormals() const { return formals != nullptr; }
- const Pos* getPos() const { return &pos; }
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
@@ -274,11 +340,11 @@ struct ExprCall : Expr
{
Expr * fun;
std::vector<Expr *> args;
- Pos pos;
- ExprCall(const Pos & pos, Expr * fun, std::vector<Expr *> && args)
+ PosIdx pos;
+ ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
: fun(fun), args(args), pos(pos)
{ }
- const Pos* getPos() const { return &pos; }
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
@@ -287,35 +353,35 @@ struct ExprLet : Expr
ExprAttrs * attrs;
Expr * body;
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
- const Pos* getPos() const { return 0; }
+ const PosIdx getPos() const { return noPos; }
COMMON_METHODS
};
struct ExprWith : Expr
{
- Pos pos;
+ PosIdx pos;
Expr * attrs, * body;
size_t prevWith;
- ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
- const Pos* getPos() const { return &pos; }
+ ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
struct ExprIf : Expr
{
- Pos pos;
+ PosIdx pos;
Expr * cond, * then, * else_;
- ExprIf(const Pos & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
- const Pos* getPos() const { return &pos; }
+ ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
struct ExprAssert : Expr
{
- Pos pos;
+ PosIdx pos;
Expr * cond, * body;
- ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
- const Pos* getPos() const { return &pos; }
+ ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
@@ -330,20 +396,20 @@ struct ExprOpNot : Expr
#define MakeBinOp(name, s) \
struct name : Expr \
{ \
- Pos pos; \
+ PosIdx pos; \
Expr * e1, * e2; \
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
- name(const Pos & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
- void show(std::ostream & str) const \
+ name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
+ void show(const SymbolTable & symbols, std::ostream & str) const \
{ \
- str << "(" << *e1 << " " s " " << *e2 << ")"; \
+ str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
} \
- void bindVars(const std::shared_ptr<const StaticEnv> & env) \
+ void bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> & env) \
{ \
- e1->bindVars(env); e2->bindVars(env); \
+ e1->bindVars(es, env); e2->bindVars(es, env); \
} \
void eval(EvalState & state, Env & env, Value & v); \
- const Pos* getPos() const { return &pos; } \
+ const PosIdx getPos() const { return pos; } \
};
MakeBinOp(ExprOpEq, "==")
@@ -356,20 +422,20 @@ MakeBinOp(ExprOpConcatLists, "++")
struct ExprConcatStrings : Expr
{
- Pos pos;
+ PosIdx pos;
bool forceString;
- std::vector<std::pair<Pos, Expr *> > * es;
- ExprConcatStrings(const Pos & pos, bool forceString, std::vector<std::pair<Pos, Expr *> > * es)
+ std::vector<std::pair<PosIdx, Expr *> > * es;
+ ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *> > * es)
: pos(pos), forceString(forceString), es(es) { };
- const Pos* getPos() const { return &pos; }
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
struct ExprPos : Expr
{
- Pos pos;
- ExprPos(const Pos & pos) : pos(pos) { };
- const Pos* getPos() const { return &pos; }
+ PosIdx pos;
+ ExprPos(const PosIdx & pos) : pos(pos) { };
+ const PosIdx getPos() const { return pos; }
COMMON_METHODS
};
@@ -407,7 +473,7 @@ struct StaticEnv
vars.erase(it, end);
}
- Vars::const_iterator find(const Symbol & name) const
+ Vars::const_iterator find(Symbol name) const
{
Vars::value_type key(name, 0);
auto i = std::lower_bound(vars.begin(), vars.end(), key);
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 1a5832e6b..8edafdd57 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -32,12 +32,12 @@ namespace nix {
SymbolTable & symbols;
Expr * result;
Path basePath;
- Symbol file;
- FileOrigin origin;
+ PosTable::Origin origin;
std::optional<ErrorInfo> error;
- ParseData(EvalState & state)
+ ParseData(EvalState & state, PosTable::Origin origin)
: state(state)
, symbols(state.symbols)
+ , origin(std::move(origin))
{ };
};
@@ -77,26 +77,26 @@ using namespace nix;
namespace nix {
-static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
+static void dupAttr(const EvalState & state, const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
{
throw ParseError({
.msg = hintfmt("attribute '%1%' already defined at %2%",
- showAttrPath(attrPath), prevPos),
- .errPos = pos
+ showAttrPath(state.symbols, attrPath), state.positions[prevPos]),
+ .errPos = state.positions[pos]
});
}
-static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
+static void dupAttr(const EvalState & state, Symbol attr, const PosIdx pos, const PosIdx prevPos)
{
throw ParseError({
- .msg = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
- .errPos = pos
+ .msg = hintfmt("attribute '%1%' already defined at %2%", state.symbols[attr], state.positions[prevPos]),
+ .errPos = state.positions[pos]
});
}
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
- Expr * e, const Pos & pos)
+ Expr * e, const PosIdx pos, const nix::EvalState & state)
{
AttrPath::iterator i;
// All attrpaths have at least one attr
@@ -104,15 +104,15 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
// Checking attrPath validity.
// ===========================
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
- if (i->symbol.set()) {
+ if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
if (!j->second.inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
- if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
+ if (!attrs2) dupAttr(state, attrPath, pos, j->second.pos);
attrs = attrs2;
} else
- dupAttr(attrPath, pos, j->second.pos);
+ dupAttr(state, attrPath, pos, j->second.pos);
} else {
ExprAttrs * nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
@@ -126,7 +126,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
}
// Expr insertion.
// ==========================
- if (i->symbol.set()) {
+ if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
// This attr path is already defined. However, if both
@@ -139,11 +139,11 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
- dupAttr(ad.first, j2->second.pos, ad.second.pos);
+ dupAttr(state, ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second);
}
} else {
- dupAttr(attrPath, pos, j->second.pos);
+ dupAttr(state, attrPath, pos, j->second.pos);
}
} else {
// This attr path is not defined. Let's create it.
@@ -157,14 +157,14 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
static Formals * toFormals(ParseData & data, ParserFormals * formals,
- Pos pos = noPos, Symbol arg = {})
+ PosIdx pos = noPos, Symbol arg = {})
{
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;
+ std::optional<std::pair<Symbol, PosIdx>> duplicate;
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
if (formals->formals[i].name != formals->formals[i + 1].name)
continue;
@@ -173,18 +173,18 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
}
if (duplicate)
throw ParseError({
- .msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
- .errPos = duplicate->second
+ .msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[duplicate->first]),
+ .errPos = data.state.positions[duplicate->second]
});
Formals result;
result.ellipsis = formals->ellipsis;
result.formals = std::move(formals->formals);
- if (arg.set() && result.has(arg))
+ if (arg && result.has(arg))
throw ParseError({
- .msg = hintfmt("duplicate formal function argument '%1%'", arg),
- .errPos = pos
+ .msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[arg]),
+ .errPos = data.state.positions[pos]
});
delete formals;
@@ -192,8 +192,8 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
}
-static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
- std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
+static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
+ std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > > & es)
{
if (es.empty()) return new ExprString("");
@@ -233,7 +233,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
}
/* Strip spaces from each line. */
- std::vector<std::pair<Pos, Expr *> > * es2 = new std::vector<std::pair<Pos, Expr *> >;
+ auto * es2 = new std::vector<std::pair<PosIdx, Expr *> >;
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
@@ -284,9 +284,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
}
-static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
+static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
{
- return Pos(data->origin, data->file, loc.first_line, loc.first_column);
+ return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
}
#define CUR_POS makeCurPos(*yylocp, data)
@@ -299,7 +299,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
{
data->error = {
.msg = hintfmt(error),
- .errPos = makeCurPos(*loc, data)
+ .errPos = data->state.positions[makeCurPos(*loc, data)]
};
}
@@ -320,8 +320,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
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;
+ std::vector<std::pair<nix::PosIdx, nix::Expr *> > * string_parts;
+ std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
}
%type <e> start expr expr_function expr_if expr_op
@@ -369,15 +369,15 @@ 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(""), toFormals(*data, $2), $5); }
+ { $$ = new ExprLambda(CUR_POS, toFormals(*data, $2), $5); }
| '{' formals '}' '@' ID ':' expr_function
{
- Symbol arg = data->symbols.create($5);
+ auto arg = data->symbols.create($5);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
}
| ID '@' '{' formals '}' ':' expr_function
{
- Symbol arg = data->symbols.create($1);
+ auto arg = data->symbols.create($1);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
}
| ASSERT expr ';' expr_function
@@ -388,7 +388,7 @@ expr_function
{ if (!$2->dynamicAttrs.empty())
throw ParseError({
.msg = hintfmt("dynamic attributes not allowed in let"),
- .errPos = CUR_POS
+ .errPos = data->state.positions[CUR_POS]
});
$$ = new ExprLet($2, $4);
}
@@ -415,7 +415,7 @@ expr_op
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op
- { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
+ { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
@@ -477,7 +477,7 @@ expr_simple
if (noURLLiterals)
throw ParseError({
.msg = hintfmt("URL literals are disabled"),
- .errPos = CUR_POS
+ .errPos = data->state.positions[CUR_POS]
});
$$ = new ExprString(std::string($1));
}
@@ -503,9 +503,9 @@ string_parts_interpolated
: string_parts_interpolated STR
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
- | DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
+ | DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
- $$ = new std::vector<std::pair<Pos, Expr *> >;
+ $$ = new std::vector<std::pair<PosIdx, Expr *> >;
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
$$->emplace_back(makeCurPos(@2, data), $3);
}
@@ -528,17 +528,17 @@ 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 std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
+ | { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > >; }
;
binds
- : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
+ : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
| binds INHERIT attrs ';'
{ $$ = $1;
for (auto & i : *$3) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
- dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
- Pos pos = makeCurPos(@3, data);
+ dupAttr(data->state, i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
+ auto pos = makeCurPos(@3, data);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
}
}
@@ -547,7 +547,7 @@ binds
/* !!! Should ensure sharing of the expression in $4. */
for (auto & i : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
- dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
+ dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
}
}
@@ -565,7 +565,7 @@ attrs
} else
throw ParseError({
.msg = hintfmt("dynamic attributes not allowed in inherit"),
- .errPos = makeCurPos(@2, data)
+ .errPos = data->state.positions[makeCurPos(@2, data)]
});
}
| { $$ = new AttrPath; }
@@ -621,8 +621,8 @@ formals
;
formal
- : ID { $$ = new Formal(CUR_POS, data->symbols.create($1), 0); }
- | ID '?' expr { $$ = new Formal(CUR_POS, data->symbols.create($1), $3); }
+ : ID { $$ = new Formal{CUR_POS, data->symbols.create($1), 0}; }
+ | ID '?' expr { $$ = new Formal{CUR_POS, data->symbols.create($1), $3}; }
;
%%
@@ -646,19 +646,19 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
const PathView path, const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv)
{
yyscan_t scanner;
- ParseData data(*this);
- data.origin = origin;
+ std::string file;
switch (origin) {
case foFile:
- data.file = data.symbols.create(path);
+ file = path;
break;
case foStdin:
case foString:
- data.file = data.symbols.create(text);
+ file = text;
break;
default:
assert(false);
}
+ ParseData data(*this, {file, origin});
data.basePath = basePath;
yylex_init(&scanner);
@@ -668,7 +668,7 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
if (res) throw ParseError(data.error.value());
- data.result->bindVars(staticEnv);
+ data.result->bindVars(*this, staticEnv);
return data.result;
}
@@ -760,7 +760,7 @@ Path EvalState::findFile(const std::string_view path)
}
-Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
+Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos)
{
for (auto & i : searchPath) {
std::string suffix;
@@ -787,7 +787,7 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
path),
- .errPos = pos
+ .errPos = positions[pos]
});
debugLastTrace(e);
throw e;
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index b1d2f5f40..212ce3de9 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -108,7 +108,7 @@ struct RealisePathFlags {
bool checkForPureEval = true;
};
-static Path realisePath(EvalState & state, const Pos & pos, Value & v, const RealisePathFlags flags = {})
+static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const RealisePathFlags flags = {})
{
PathSet context;
@@ -117,7 +117,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea
try {
return state.coerceToPath(pos, v, context);
} catch (Error & e) {
- e.addTrace(pos, "while realising the context of a path");
+ e.addTrace(state.positions[pos], "while realising the context of a path");
throw;
}
}();
@@ -131,7 +131,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea
? state.checkSourcePath(realPath)
: realPath;
} catch (Error & e) {
- e.addTrace(pos, "while realising the context of path '%s'", path);
+ e.addTrace(state.positions[pos], "while realising the context of path '%s'", path);
throw;
}
}
@@ -169,7 +169,7 @@ static void mkOutputString(
/* Load and evaluate an expression from path specified by the
argument. */
-static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
+static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * vScope, Value & v)
{
auto path = realisePath(state, pos, vPath);
@@ -249,7 +249,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info {
.name = "scopedImport",
.arity = 2,
- .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
+ .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
import(state, pos, *args[1], args[0], v);
}
@@ -311,7 +311,7 @@ static RegisterPrimOp primop_import({
(The function argument doesn’t have to be called `x` in `foo.nix`;
any name would work.)
)",
- .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
+ .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
import(state, pos, *args[0], nullptr, v);
}
@@ -322,7 +322,7 @@ static RegisterPrimOp primop_import({
extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */
-void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
+void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto path = realisePath(state, pos, *args[0]);
@@ -362,7 +362,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
/* Execute a program and parse its output */
-void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
+void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
auto elems = args[0]->listElems();
@@ -370,7 +370,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
if (count == 0) {
auto e = EvalError({
.msg = hintfmt("at least one argument to 'exec' required"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -387,7 +387,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
auto ee = EvalError({
.msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
program, e.path),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(ee);
throw ee;
@@ -396,22 +396,23 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
auto output = runProgram(program, true, commandArgs);
Expr * parsed;
try {
- parsed = state.parseExprFromString(std::move(output), pos.file);
+ auto base = state.positions[pos];
+ parsed = state.parseExprFromString(std::move(output), base.file);
} catch (Error & e) {
- e.addTrace(pos, "While parsing the output from '%1%'", program);
+ e.addTrace(state.positions[pos], "While parsing the output from '%1%'", program);
throw;
}
try {
state.eval(parsed, v);
} catch (Error & e) {
- e.addTrace(pos, "While evaluating the output from '%1%'", program);
+ e.addTrace(state.positions[pos], "While evaluating the output from '%1%'", program);
throw;
}
}
/* Return a string representing the type of the expression. */
-static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
std::string t;
@@ -430,7 +431,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case nFloat: t = "float"; break;
case nThunk: abort();
}
- v.mkString(state.symbols.create(t));
+ v.mkString(t);
}
static RegisterPrimOp primop_typeOf({
@@ -445,7 +446,7 @@ static RegisterPrimOp primop_typeOf({
});
/* Determine whether the argument is the null value. */
-static void prim_isNull(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isNull(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nNull);
@@ -465,7 +466,7 @@ static RegisterPrimOp primop_isNull({
});
/* Determine whether the argument is a function. */
-static void prim_isFunction(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isFunction(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nFunction);
@@ -481,7 +482,7 @@ static RegisterPrimOp primop_isFunction({
});
/* Determine whether the argument is an integer. */
-static void prim_isInt(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isInt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nInt);
@@ -497,7 +498,7 @@ static RegisterPrimOp primop_isInt({
});
/* Determine whether the argument is a float. */
-static void prim_isFloat(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isFloat(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nFloat);
@@ -513,7 +514,7 @@ static RegisterPrimOp primop_isFloat({
});
/* Determine whether the argument is a string. */
-static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nString);
@@ -529,7 +530,7 @@ static RegisterPrimOp primop_isString({
});
/* Determine whether the argument is a Boolean. */
-static void prim_isBool(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isBool(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nBool);
@@ -545,7 +546,7 @@ static RegisterPrimOp primop_isBool({
});
/* Determine whether the argument is a path. */
-static void prim_isPath(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nPath);
@@ -621,33 +622,33 @@ static Bindings::iterator getAttr(
std::string_view funcName,
Symbol attrSym,
Bindings * attrSet,
- const Pos & pos)
+ const PosIdx pos)
{
Bindings::iterator value = attrSet->find(attrSym);
if (value == attrSet->end()) {
hintformat errorMsg = hintfmt(
"attribute '%s' missing for call to '%s'",
- attrSym,
+ state.symbols[attrSym],
funcName
);
- Pos aPos = *attrSet->pos;
- if (aPos == noPos) {
+ auto aPos = attrSet->pos;
+ if (!aPos) {
auto e = TypeError({
.msg = errorMsg,
- .errPos = pos,
+ .errPos = state.positions[pos],
});
state.debugLastTrace(e);
throw e;
} else {
auto e = TypeError({
.msg = errorMsg,
- .errPos = aPos,
+ .errPos = state.positions[aPos],
});
// Adding another trace for the function name to make it clear
// which call received wrong arguments.
- e.addTrace(pos, hintfmt("while invoking '%s'", funcName));
+ e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", funcName));
state.debugLastTrace(e);
throw e;
}
@@ -656,7 +657,7 @@ static Bindings::iterator getAttr(
return value;
}
-static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
@@ -706,7 +707,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
{
auto e = EvalError({
.msg = hintfmt("attribute 'key' required"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -806,7 +807,7 @@ static RegisterPrimOp primop_abort({
.doc = R"(
Abort Nix expression evaluation and print the error message *s*.
)",
- .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
+ .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned();
@@ -828,7 +829,7 @@ static RegisterPrimOp primop_throw({
derivations, a derivation that throws an error is silently skipped
(which is not the case for `abort`).
)",
- .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
+ .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned();
@@ -838,7 +839,7 @@ static RegisterPrimOp primop_throw({
}
});
-static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
try {
state.forceValue(*args[1], pos);
@@ -856,7 +857,7 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info {
.fun = prim_addErrorContext,
});
-static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
v.mkInt(ceil(value));
@@ -875,7 +876,7 @@ static RegisterPrimOp primop_ceil({
.fun = prim_ceil,
});
-static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
v.mkInt(floor(value));
@@ -896,7 +897,7 @@ static RegisterPrimOp primop_floor({
/* Try evaluating the argument. Success => {success=true; value=something;},
* else => {success=false; value=false;} */
-static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto attrs = state.buildBindings(2);
auto saveDebuggerHook = debuggerHook;
@@ -935,7 +936,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)
+static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::string name(state.forceStringNoCtx(*args[0], pos));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
@@ -959,7 +960,7 @@ static RegisterPrimOp primop_getEnv({
});
/* Evaluate the first argument, then return the second argument. */
-static void prim_seq(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_seq(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
@@ -978,7 +979,7 @@ static RegisterPrimOp primop_seq({
/* Evaluate the first argument deeply (i.e. recursing into lists and
attrsets), then return the second argument. */
-static void prim_deepSeq(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_deepSeq(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValueDeep(*args[0]);
state.forceValue(*args[1], pos);
@@ -998,13 +999,13 @@ static RegisterPrimOp primop_deepSeq({
/* Evaluate the first expression and print it on standard error. Then
return the second expression. Useful for debugging. */
-static void prim_trace(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
if (args[0]->type() == nString)
printError("trace: %1%", args[0]->string.s);
else
- printError("trace: %1%", *args[0]);
+ printError("trace: %1%", printValue(state, *args[0]));
state.forceValue(*args[1], pos);
v = *args[1];
}
@@ -1033,7 +1034,7 @@ static RegisterPrimOp primop_trace({
derivation; `drvPath' containing the path of the Nix expression;
and `type' set to `derivation' to indicate that this is a
derivation. */
-static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
@@ -1047,11 +1048,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
);
std::string drvName;
- Pos & posDrvName(*attr->pos);
+ const auto posDrvName = attr->pos;
try {
drvName = state.forceStringNoCtx(*attr->value, pos);
} catch (Error & e) {
- e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'");
+ e.addTrace(state.positions[posDrvName], "while evaluating the derivation attribute 'name'");
throw;
}
@@ -1083,9 +1084,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
StringSet outputs;
outputs.insert("out");
- for (auto & i : args[0]->attrs->lexicographicOrder()) {
+ for (auto & i : args[0]->attrs->lexicographicOrder(state.symbols)) {
if (i->name == state.sIgnoreNulls) continue;
- const std::string & key = i->name;
+ const std::string & key = state.symbols[i->name];
vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string_view s) {
@@ -1095,7 +1096,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = EvalError({
.msg = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1109,7 +1110,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = EvalError({
.msg = hintfmt("duplicate derivation output '%1%'", j),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1123,7 +1124,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = EvalError({
.msg = hintfmt("invalid derivation output name 'drv'" ),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1134,7 +1135,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = EvalError({
.msg = hintfmt("derivation cannot have an empty set of outputs"),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1201,7 +1202,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} else {
- auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned();
+ 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);
@@ -1215,7 +1216,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} catch (Error & e) {
- e.addTrace(posDrvName,
+ e.addTrace(state.positions[posDrvName],
"while evaluating the attribute '%1%' of the derivation '%2%'",
key, drvName);
throw;
@@ -1266,7 +1267,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = EvalError({
.msg = hintfmt("required attribute 'builder' missing"),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1276,7 +1277,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = EvalError({
.msg = hintfmt("required attribute 'system' missing"),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1287,7 +1288,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = EvalError({
.msg = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1302,7 +1303,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
{
auto e = Error({
.msg = hintfmt("multiple outputs are not supported in fixed-output derivations"),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
state.debugLastTrace(e);
throw e;
@@ -1326,7 +1327,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (contentAddressed && isImpure)
throw EvalError({
.msg = hintfmt("derivation cannot be both content-addressed and impure"),
- .errPos = posDrvName
+ .errPos = state.positions[posDrvName]
});
auto ht = parseHashTypeOpt(outputHashAlgo).value_or(htSHA256);
@@ -1418,7 +1419,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
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)
+static void prim_placeholder(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos)));
}
@@ -1441,7 +1442,7 @@ static RegisterPrimOp primop_placeholder({
/* Convert the argument to a path. !!! obsolete? */
-static void prim_toPath(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, *args[0], context);
@@ -1466,13 +1467,13 @@ static RegisterPrimOp primop_toPath({
/nix/store/newhash-oldhash-oldname. In the past, `toPath' had
special case behaviour for store paths, but that created weird
corner cases. */
-static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
if (evalSettings.pureEval)
{
auto e = EvalError({
.msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -1488,7 +1489,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
{
auto e = EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -1518,7 +1519,7 @@ static RegisterPrimOp primop_storePath({
.fun = prim_storePath,
});
-static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
/* We don’t check the path right now, because we don’t want to
throw if the path isn’t allowed, but just return false (and we
@@ -1550,7 +1551,7 @@ static RegisterPrimOp primop_pathExists({
/* Return the base name of the given string, i.e., everything
following the last slash. */
-static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context);
@@ -1570,7 +1571,7 @@ static RegisterPrimOp primop_baseNameOf({
/* Return the directory of the given path, i.e., everything before the
last slash. Return either a path or a string depending on the type
of the argument. */
-static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto path = state.coerceToString(pos, *args[0], context, false, false);
@@ -1590,7 +1591,7 @@ static RegisterPrimOp primop_dirOf({
});
/* Return the contents of a file as a string. */
-static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto path = realisePath(state, pos, *args[0]);
auto s = readFile(path);
@@ -1622,7 +1623,7 @@ static RegisterPrimOp primop_readFile({
/* Find a file in the Nix search path. Used to implement <x> paths,
which are desugared to 'findFile __nixPath "x"'. */
-static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
@@ -1653,7 +1654,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
} catch (InvalidPathError & e) {
auto ee = EvalError({
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(ee);
throw ee;
@@ -1675,7 +1676,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)
+static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
@@ -1683,7 +1684,7 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
{
auto e = Error({
.msg = hintfmt("unknown hash type '%1%'", type),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -1706,7 +1707,7 @@ static RegisterPrimOp primop_hashFile({
});
/* Read a directory (without . or ..) */
-static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto path = realisePath(state, pos, *args[0]);
@@ -1755,7 +1756,7 @@ static RegisterPrimOp primop_readDir({
/* Convert the argument (which can be any Nix expression) to an XML
representation returned in a string. Not all Nix expressions can
be sensibly or completely represented (e.g., functions). */
-static void prim_toXML(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::ostringstream out;
PathSet context;
@@ -1863,7 +1864,7 @@ static RegisterPrimOp primop_toXML({
/* Convert the argument (which can be any Nix expression) to a JSON
string. Not all Nix expressions can be sensibly or completely
represented (e.g., functions). */
-static void prim_toJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::ostringstream out;
PathSet context;
@@ -1886,13 +1887,13 @@ static RegisterPrimOp primop_toJSON({
});
/* Parse a JSON string to a value. */
-static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fromJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto s = state.forceStringNoCtx(*args[0], pos);
try {
parseJSON(state, s, v);
} catch (JSONParseError &e) {
- e.addTrace(pos, "while decoding a JSON string");
+ e.addTrace(state.positions[pos], "while decoding a JSON string");
throw;
}
}
@@ -1914,7 +1915,7 @@ static RegisterPrimOp primop_fromJSON({
/* Store a string in the Nix store as a source file that can be used
as an input by derivations. */
-static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
std::string name(state.forceStringNoCtx(*args[0], pos));
@@ -1930,7 +1931,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
"in 'toFile': the file named '%1%' must not contain a reference "
"to a derivation but contains (%2%)",
name, path),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -1938,15 +1939,16 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
refs.insert(state.store->parseStorePath(path));
}
- auto storePath = state.store->printStorePath(settings.readOnlyMode
+ auto storePath = settings.readOnlyMode
? state.store->computeStorePathForText(name, contents, refs)
- : state.store->addTextToStore(name, contents, refs, state.repair));
+ : state.store->addTextToStore(name, contents, refs, state.repair);
/* Note: we don't need to add `context' to the context of the
result, since `storePath' itself has references to the paths
used in args[1]. */
- v.mkString(storePath, {storePath});
+ /* Add the output of this to the allowed paths. */
+ state.allowAndSetStorePathString(storePath, v);
}
static RegisterPrimOp primop_toFile({
@@ -2029,7 +2031,7 @@ static RegisterPrimOp primop_toFile({
static void addPath(
EvalState & state,
- const Pos & pos,
+ const PosIdx pos,
const std::string & name,
Path path,
Value * filterFun,
@@ -2100,13 +2102,13 @@ static void addPath(
} else
state.allowAndSetStorePathString(*expectedStorePath, v);
} catch (Error & e) {
- e.addTrace(pos, "while adding path '%s'", path);
+ e.addTrace(state.positions[pos], "while adding path '%s'", path);
throw;
}
}
-static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, *args[1], context);
@@ -2118,7 +2120,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
.msg = hintfmt(
"first argument in call to 'filterSource' is not a function but %1%",
showType(*args[0])),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -2182,7 +2184,7 @@ static RegisterPrimOp primop_filterSource({
.fun = prim_filterSource,
});
-static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
Path path;
@@ -2193,23 +2195,23 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
PathSet context;
for (auto & attr : *args[0]->attrs) {
- auto & n(attr.name);
+ auto n = state.symbols[attr.name];
if (n == "path")
- path = state.coerceToPath(*attr.pos, *attr.value, context);
+ path = state.coerceToPath(attr.pos, *attr.value, context);
else if (attr.name == state.sName)
- name = state.forceStringNoCtx(*attr.value, *attr.pos);
+ name = state.forceStringNoCtx(*attr.value, attr.pos);
else if (n == "filter") {
state.forceValue(*attr.value, pos);
filterFun = attr.value;
} else if (n == "recursive")
- method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) };
+ method = FileIngestionMethod { state.forceBool(*attr.value, attr.pos) };
else if (n == "sha256")
- expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
+ expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
else
{
auto e = EvalError({
- .msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name),
- .errPos = *attr.pos
+ .msg = hintfmt("unsupported argument '%1%' to 'addPath'", state.symbols[attr.name]),
+ .errPos = state.positions[attr.pos]
});
state.debugLastTrace(e);
throw e;
@@ -2219,7 +2221,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
{
auto e = EvalError({
.msg = hintfmt("'path' required"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -2273,7 +2275,7 @@ static RegisterPrimOp primop_path({
/* Return the names of the attributes in a set as a sorted list of
strings. */
-static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
@@ -2281,7 +2283,7 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V
size_t n = 0;
for (auto & i : *args[0]->attrs)
- (v.listElems()[n++] = state.allocValue())->mkString(i.name);
+ (v.listElems()[n++] = state.allocValue())->mkString(state.symbols[i.name]);
std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
@@ -2300,7 +2302,7 @@ static RegisterPrimOp primop_attrNames({
/* Return the values of the attributes in a set as a list, in the same
order as attrNames. */
-static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
@@ -2311,8 +2313,9 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args,
v.listElems()[n++] = (Value *) &i;
std::sort(v.listElems(), v.listElems() + n,
- [](Value * v1, Value * v2) {
- std::string_view s1 = ((Attr *) v1)->name, s2 = ((Attr *) v2)->name;
+ [&](Value * v1, Value * v2) {
+ std::string_view s1 = state.symbols[((Attr *) v1)->name],
+ s2 = state.symbols[((Attr *) v2)->name];
return s1 < s2;
});
@@ -2331,7 +2334,7 @@ static RegisterPrimOp primop_attrValues({
});
/* Dynamic version of the `.' operator. */
-void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
+void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
@@ -2343,7 +2346,7 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
pos
);
// !!! add to stack trace?
- if (state.countCalls && *i->pos != noPos) state.attrSelects[*i->pos]++;
+ if (state.countCalls && i->pos) state.attrSelects[i->pos]++;
state.forceValue(*i->value, pos);
v = *i->value;
}
@@ -2361,7 +2364,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)
+static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
@@ -2379,7 +2382,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)
+static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
@@ -2398,7 +2401,7 @@ static RegisterPrimOp primop_hasAttr({
});
/* Determine whether the argument is a set. */
-static void prim_isAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nAttrs);
@@ -2413,7 +2416,7 @@ static RegisterPrimOp primop_isAttrs({
.fun = prim_isAttrs,
});
-static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
state.forceList(*args[1], pos);
@@ -2461,7 +2464,7 @@ static RegisterPrimOp primop_removeAttrs({
"nameN"; value = valueN;}] is transformed to {name1 = value1;
... 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)
+static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
@@ -2480,9 +2483,9 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
pos
);
- auto name = state.forceStringNoCtx(*j->value, *j->pos);
+ auto name = state.forceStringNoCtx(*j->value, j->pos);
- Symbol sym = state.symbols.create(name);
+ auto sym = state.symbols.create(name);
if (seen.insert(sym).second) {
Bindings::iterator j2 = getAttr(
state,
@@ -2523,7 +2526,7 @@ static RegisterPrimOp primop_listToAttrs({
.fun = prim_listToAttrs,
});
-static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
state.forceAttrs(*args[1], pos);
@@ -2549,9 +2552,9 @@ static RegisterPrimOp primop_intersectAttrs({
.fun = prim_intersectAttrs,
});
-static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
- Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
+ auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
state.forceList(*args[1], pos);
Value * res[args[1]->listSize()];
@@ -2586,7 +2589,7 @@ static RegisterPrimOp primop_catAttrs({
.fun = prim_catAttrs,
});
-static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
@@ -2597,7 +2600,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
{
auto e = TypeError({
.msg = hintfmt("'functionArgs' requires a function"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -2611,7 +2614,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size());
for (auto & i : args[0]->lambda.fun->formals->formals)
// !!! should optimise booleans (allocate only once)
- attrs.alloc(i.name, ptr(&i.pos)).mkBool(i.def);
+ attrs.alloc(i.name, i.pos).mkBool(i.def);
v.mkAttrs(attrs);
}
@@ -2633,7 +2636,7 @@ static RegisterPrimOp primop_functionArgs({
});
/* */
-static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[1], pos);
@@ -2642,7 +2645,7 @@ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Va
for (auto & i : *args[1]->attrs) {
Value * vName = state.allocValue();
Value * vFun2 = state.allocValue();
- vName->mkString(i.name);
+ vName->mkString(state.symbols[i.name]);
vFun2->mkApp(args[0], vName);
attrs.alloc(i.name).mkApp(vFun2, i.value);
}
@@ -2665,7 +2668,7 @@ static RegisterPrimOp primop_mapAttrs({
.fun = prim_mapAttrs,
});
-static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
// we will first count how many values are present for each given key.
// we then allocate a single attrset and pre-populate it with lists of
@@ -2688,7 +2691,7 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args
for (auto & attr : *vElem->attrs)
attrsSeen[attr.name].first++;
} catch (TypeError & e) {
- e.addTrace(pos, hintfmt("while invoking '%s'", "zipAttrsWith"));
+ e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", "zipAttrsWith"));
state.debugLastTrace(e);
throw e;
}
@@ -2710,7 +2713,7 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args
for (auto & attr : *v.attrs) {
auto name = state.allocValue();
- name->mkString(attr.name);
+ name->mkString(state.symbols[attr.name]);
auto call1 = state.allocValue();
call1->mkApp(args[0], name);
auto call2 = state.allocValue();
@@ -2758,7 +2761,7 @@ static RegisterPrimOp primop_zipAttrsWith({
/* Determine whether the argument is a list. */
-static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_isList(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nList);
@@ -2773,14 +2776,14 @@ static RegisterPrimOp primop_isList({
.fun = prim_isList,
});
-static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v)
+static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v)
{
state.forceList(list, pos);
if (n < 0 || (unsigned int) n >= list.listSize())
{
auto e = Error({
- .msg = hintfmt("list index %1% is out of boundz", n),
- .errPos = pos
+ .msg = hintfmt("list index %1% is out of bounds", n),
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -2790,7 +2793,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu
}
/* Return the n-1'th element of a list. */
-static void prim_elemAt(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
elemAt(state, pos, *args[0], state.forceInt(*args[1], pos), v);
}
@@ -2806,7 +2809,7 @@ static RegisterPrimOp primop_elemAt({
});
/* Return the first element of a list. */
-static void prim_head(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
elemAt(state, pos, *args[0], 0, v);
}
@@ -2825,14 +2828,14 @@ static RegisterPrimOp primop_head({
/* Return a list consisting of everything but the first element of
a list. Warning: this function takes O(n) time, so you probably
don't want to use it! */
-static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
if (args[0]->listSize() == 0)
{
auto e = Error({
.msg = hintfmt("'tail' called on an empty list"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -2860,7 +2863,7 @@ static RegisterPrimOp primop_tail({
});
/* Apply a function to every element of a list. */
-static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[1], pos);
@@ -2890,7 +2893,7 @@ static RegisterPrimOp primop_map({
/* Filter a list using a predicate; that is, return a list containing
every element from the list for which the predicate function
returns true. */
-static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
@@ -2928,7 +2931,7 @@ static RegisterPrimOp primop_filter({
});
/* Return true if a list contains a given element. */
-static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_elem(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
bool res = false;
state.forceList(*args[1], pos);
@@ -2951,7 +2954,7 @@ static RegisterPrimOp primop_elem({
});
/* Concatenate a list of lists. */
-static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_concatLists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos);
@@ -2967,7 +2970,7 @@ static RegisterPrimOp primop_concatLists({
});
/* Return the length of a list. This is an O(1) time operation. */
-static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_length(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
v.mkInt(args[0]->listSize());
@@ -2984,7 +2987,7 @@ static RegisterPrimOp primop_length({
/* Reduce a list by applying a binary operator, from left to
right. The operator is applied strictly. */
-static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[2], pos);
@@ -3017,7 +3020,7 @@ static RegisterPrimOp primop_foldlStrict({
.fun = prim_foldlStrict,
});
-static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
@@ -3036,7 +3039,7 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg
}
-static void prim_any(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_any(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
anyOrAll(true, state, pos, args, v);
}
@@ -3051,7 +3054,7 @@ static RegisterPrimOp primop_any({
.fun = prim_any,
});
-static void prim_all(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_all(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
anyOrAll(false, state, pos, args, v);
}
@@ -3066,7 +3069,7 @@ static RegisterPrimOp primop_all({
.fun = prim_all,
});
-static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto len = state.forceInt(*args[1], pos);
@@ -3074,7 +3077,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
{
auto e = EvalError({
.msg = hintfmt("cannot create list of size %1%", len),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3105,10 +3108,10 @@ static RegisterPrimOp primop_genList({
.fun = prim_genList,
});
-static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Value & v);
+static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v);
-static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
@@ -3159,7 +3162,7 @@ static RegisterPrimOp primop_sort({
.fun = prim_sort,
});
-static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
@@ -3219,7 +3222,7 @@ static RegisterPrimOp primop_partition({
.fun = prim_partition,
});
-static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
@@ -3230,7 +3233,7 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val
Value res;
state.callFunction(*args[0], *vElem, res, pos);
auto name = state.forceStringNoCtx(res, pos);
- Symbol sym = state.symbols.create(name);
+ auto sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first;
vector->second.push_back(vElem);
}
@@ -3271,7 +3274,7 @@ static RegisterPrimOp primop_groupBy({
.fun = prim_groupBy,
});
-static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
@@ -3286,7 +3289,7 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V
try {
state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)));
} catch (TypeError &e) {
- e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap"));
+ e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", "concatMap"));
state.debugLastTrace(e);
throw e;
}
@@ -3319,7 +3322,7 @@ static RegisterPrimOp primop_concatMap({
*************************************************************/
-static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
@@ -3338,7 +3341,7 @@ static RegisterPrimOp primop_add({
.fun = prim_add,
});
-static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
@@ -3357,7 +3360,7 @@ static RegisterPrimOp primop_sub({
.fun = prim_sub,
});
-static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
@@ -3376,7 +3379,7 @@ static RegisterPrimOp primop_mul({
.fun = prim_mul,
});
-static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
@@ -3386,7 +3389,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
{
auto e = EvalError({
.msg = hintfmt("division by zero"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3402,7 +3405,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
{
auto e = EvalError({
.msg = hintfmt("overflow in integer division"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3421,7 +3424,7 @@ static RegisterPrimOp primop_div({
.fun = prim_div,
});
-static void prim_bitAnd(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
v.mkInt(state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos));
}
@@ -3435,7 +3438,7 @@ static RegisterPrimOp primop_bitAnd({
.fun = prim_bitAnd,
});
-static void prim_bitOr(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
v.mkInt(state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos));
}
@@ -3449,7 +3452,7 @@ static RegisterPrimOp primop_bitOr({
.fun = prim_bitOr,
});
-static void prim_bitXor(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
v.mkInt(state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos));
}
@@ -3463,7 +3466,7 @@ static RegisterPrimOp primop_bitXor({
.fun = prim_bitXor,
});
-static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
@@ -3491,7 +3494,7 @@ static RegisterPrimOp primop_lessThan({
/* Convert the argument to a string. Paths are *not* copied to the
store, so `toString /foo/bar' yields `"/foo/bar"', not
`"/nix/store/whatever..."'. */
-static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context, true, false);
@@ -3526,7 +3529,7 @@ static RegisterPrimOp primop_toString({
at character position `min(start, stringLength str)' inclusive and
ending at `min(start + len, stringLength str)'. `start' must be
non-negative. */
-static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
int start = state.forceInt(*args[0], pos);
int len = state.forceInt(*args[1], pos);
@@ -3537,7 +3540,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
{
auto e = EvalError({
.msg = hintfmt("negative start position in 'substring'"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3566,7 +3569,7 @@ static RegisterPrimOp primop_substring({
.fun = prim_substring,
});
-static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context);
@@ -3584,7 +3587,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)
+static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
@@ -3592,7 +3595,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
{
auto e = Error({
.msg = hintfmt("unknown hash type '%1%'", type),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3636,7 +3639,7 @@ std::shared_ptr<RegexCache> makeRegexCache()
return std::make_shared<RegexCache>();
}
-void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
+void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto re = state.forceStringNoCtx(*args[0], pos);
@@ -3668,14 +3671,14 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
auto e = EvalError({
.msg = hintfmt("memory limit exceeded by regular expression '%s'", re),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
} else {
auto e = EvalError({
.msg = hintfmt("invalid regular expression '%s'", re),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3721,7 +3724,7 @@ static RegisterPrimOp primop_match({
/* Split a string with a regular expression, and return a list of the
non-matching parts interleaved by the lists of the matching groups. */
-void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
+void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto re = state.forceStringNoCtx(*args[0], pos);
@@ -3777,14 +3780,14 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
auto e = EvalError({
.msg = hintfmt("memory limit exceeded by regular expression '%s'", re),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
} else {
auto e = EvalError({
.msg = hintfmt("invalid regular expression '%s'", re),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3829,7 +3832,7 @@ static RegisterPrimOp primop_split({
.fun = prim_split,
});
-static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
@@ -3859,7 +3862,7 @@ static RegisterPrimOp primop_concatStringsSep({
.fun = prim_concatStringsSep,
});
-static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
state.forceList(*args[1], pos);
@@ -3867,7 +3870,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
{
auto e = EvalError({
.msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -3943,7 +3946,7 @@ static RegisterPrimOp primop_replaceStrings({
*************************************************************/
-static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto name = state.forceStringNoCtx(*args[0], pos);
DrvName parsed(name);
@@ -3967,7 +3970,7 @@ static RegisterPrimOp primop_parseDrvName({
.fun = prim_parseDrvName,
});
-static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto version1 = state.forceStringNoCtx(*args[0], pos);
auto version2 = state.forceStringNoCtx(*args[1], pos);
@@ -3987,7 +3990,7 @@ static RegisterPrimOp primop_compareVersions({
.fun = prim_compareVersions,
});
-static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto version = state.forceStringNoCtx(*args[0], pos);
auto iter = version.cbegin();
@@ -4108,7 +4111,7 @@ void EvalState::createBaseEnv()
addPrimOp({
.fun = primOp.fun,
.arity = std::max(primOp.args.size(), primOp.arity),
- .name = symbols.create(primOp.name),
+ .name = primOp.name,
.args = primOp.args,
.doc = primOp.doc,
});
@@ -4116,7 +4119,7 @@ void EvalState::createBaseEnv()
/* Add a wrapper around the derivation primop that computes the
`drvPath' and `outPath' attributes lazily. */
- sDerivationNix = symbols.create("//builtin/derivation.nix");
+ sDerivationNix = symbols.create(derivationNixPath);
auto vDerivation = allocValue();
addConstant("derivation", vDerivation);
@@ -4133,7 +4136,7 @@ void EvalState::createBaseEnv()
// the parser needs two NUL bytes as terminators; one of them
// is implied by being a C string.
"\0";
- eval(parse(code, sizeof(code), foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation);
+ eval(parse(code, sizeof(code), foFile, derivationNixPath, "/", staticBaseEnv), *vDerivation);
}
diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh
index 905bd0366..1cfb4356b 100644
--- a/src/libexpr/primops.hh
+++ b/src/libexpr/primops.hh
@@ -38,9 +38,9 @@ struct RegisterPrimOp
them. */
/* Load a ValueInitializer from a DSO and return whatever it initializes */
-void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v);
+void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v);
/* Execute a program and parse its output */
-void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v);
+void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
}
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index cc74c7f58..979136984 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -5,7 +5,7 @@
namespace nix {
-static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context);
@@ -15,7 +15,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos,
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
-static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
state.forceString(*args[0], context, pos);
@@ -31,7 +31,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
source-only deployment). This primop marks the string context so
that builtins.derivation adds the path to drv.inputSrcs rather than
drv.inputDrvs. */
-static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context);
@@ -65,7 +65,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutpu
Note that for a given path any combination of the above attributes
may be present.
*/
-static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
struct ContextInfo {
bool path = false;
@@ -134,7 +134,7 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
See the commentary above unsafeGetContext for details of the
context representation.
*/
-static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto orig = state.forceString(*args[0], context, pos);
@@ -144,45 +144,46 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs");
for (auto & i : *args[1]->attrs) {
- if (!state.store->isStorePath(i.name))
+ const auto & name = state.symbols[i.name];
+ if (!state.store->isStorePath(name))
throw EvalError({
- .msg = hintfmt("Context key '%s' is not a store path", i.name),
- .errPos = *i.pos
+ .msg = hintfmt("Context key '%s' is not a store path", name),
+ .errPos = state.positions[i.pos]
});
if (!settings.readOnlyMode)
- state.store->ensurePath(state.store->parseStorePath(i.name));
- state.forceAttrs(*i.value, *i.pos);
+ state.store->ensurePath(state.store->parseStorePath(name));
+ state.forceAttrs(*i.value, i.pos);
auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) {
- if (state.forceBool(*iter->value, *iter->pos))
- context.insert(i.name);
+ if (state.forceBool(*iter->value, iter->pos))
+ context.emplace(name);
}
iter = i.value->attrs->find(sAllOutputs);
if (iter != i.value->attrs->end()) {
- if (state.forceBool(*iter->value, *iter->pos)) {
- if (!isDerivation(i.name)) {
+ if (state.forceBool(*iter->value, iter->pos)) {
+ if (!isDerivation(name)) {
throw EvalError({
- .msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
- .errPos = *i.pos
+ .msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", name),
+ .errPos = state.positions[i.pos]
});
}
- context.insert("=" + std::string(i.name));
+ context.insert(concatStrings("=", name));
}
}
iter = i.value->attrs->find(state.sOutputs);
if (iter != i.value->attrs->end()) {
- state.forceList(*iter->value, *iter->pos);
- if (iter->value->listSize() && !isDerivation(i.name)) {
+ state.forceList(*iter->value, iter->pos);
+ if (iter->value->listSize() && !isDerivation(name)) {
throw EvalError({
- .msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
- .errPos = *i.pos
+ .msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", name),
+ .errPos = state.positions[i.pos]
});
}
for (auto elem : iter->value->listItems()) {
- auto name = state.forceStringNoCtx(*elem, *iter->pos);
- context.insert(concatStrings("!", name, "!", i.name));
+ auto outputName = state.forceStringNoCtx(*elem, iter->pos);
+ context.insert(concatStrings("!", outputName, "!", name));
}
}
}
diff --git a/src/libexpr/primops/fetchClosure.cc b/src/libexpr/primops/fetchClosure.cc
index 821eba698..662c9652e 100644
--- a/src/libexpr/primops/fetchClosure.cc
+++ b/src/libexpr/primops/fetchClosure.cc
@@ -5,7 +5,7 @@
namespace nix {
-static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
@@ -15,40 +15,42 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
std::optional<StorePath> toPath;
for (auto & attr : *args[0]->attrs) {
- if (attr.name == "fromPath") {
+ const auto & attrName = state.symbols[attr.name];
+
+ if (attrName == "fromPath") {
PathSet context;
- fromPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
+ fromPath = state.coerceToStorePath(attr.pos, *attr.value, context);
}
- else if (attr.name == "toPath") {
- state.forceValue(*attr.value, *attr.pos);
+ else if (attrName == "toPath") {
+ state.forceValue(*attr.value, attr.pos);
toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
PathSet context;
- toPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
+ toPath = state.coerceToStorePath(attr.pos, *attr.value, context);
}
}
- else if (attr.name == "fromStore")
- fromStoreUrl = state.forceStringNoCtx(*attr.value, *attr.pos);
+ else if (attrName == "fromStore")
+ fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos);
else
throw Error({
- .msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name),
- .errPos = pos
+ .msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
+ .errPos = state.positions[pos]
});
}
if (!fromPath)
throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
if (!fromStoreUrl)
throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
auto parsedURL = parseURL(*fromStoreUrl);
@@ -58,13 +60,13 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
throw Error({
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
if (!parsedURL.query.empty())
throw Error({
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
- .errPos = pos
+ .errPos = state.positions[pos]
});
auto fromStore = openStore(parsedURL.to_string());
@@ -80,7 +82,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second),
state.store->printStorePath(*toPath)),
- .errPos = pos
+ .errPos = state.positions[pos]
});
if (!toPath)
throw Error({
@@ -89,7 +91,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second)),
- .errPos = pos
+ .errPos = state.positions[pos]
});
}
} else {
@@ -105,7 +107,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
throw Error({
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
state.store->printStorePath(*toPath)),
- .errPos = pos
+ .errPos = state.positions[pos]
});
}
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index b7f715859..249c0934e 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -7,7 +7,7 @@
namespace nix {
-static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::string url;
std::optional<Hash> rev;
@@ -22,31 +22,31 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
- std::string_view n(attr.name);
+ std::string_view n(state.symbols[attr.name]);
if (n == "url")
- url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
+ 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);
+ auto value = state.forceStringNoCtx(*attr.value, attr.pos);
if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, htSHA1);
else
ref = value;
}
else if (n == "name")
- name = state.forceStringNoCtx(*attr.value, *attr.pos);
+ name = state.forceStringNoCtx(*attr.value, attr.pos);
else
throw EvalError({
- .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
- .errPos = *attr.pos
+ .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]),
+ .errPos = state.positions[attr.pos]
});
}
if (url.empty())
throw EvalError({
.msg = hintfmt("'url' argument required"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
} else
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index c7b73b83d..55fe7da24 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -90,7 +90,7 @@ struct FetchTreeParams {
static void fetchTree(
EvalState & state,
- const Pos & pos,
+ const PosIdx pos,
Value * * args,
Value & v,
std::optional<std::string> type,
@@ -111,18 +111,17 @@ static void fetchTree(
{
auto e = EvalError({
.msg = hintfmt("unexpected attribute 'type'"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
-
}
- type = state.forceStringNoCtx(*aType->value, *aType->pos);
+ type = state.forceStringNoCtx(*aType->value, aType->pos);
} else if (!type)
{
auto e = EvalError({
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -132,24 +131,24 @@ static void fetchTree(
for (auto & attr : *args[0]->attrs) {
if (attr.name == state.sType) continue;
- state.forceValue(*attr.value, *attr.pos);
+ 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).toOwned();
- attrs.emplace(attr.name,
- attr.name == "url"
+ auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
+ attrs.emplace(state.symbols[attr.name],
+ state.symbols[attr.name] == "url"
? type == "git"
? fixURIForGit(s, state)
: fixURI(s, state)
: s);
}
else if (attr.value->type() == nBool)
- attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
+ attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean});
else if (attr.value->type() == nInt)
- attrs.emplace(attr.name, uint64_t(attr.value->integer));
+ attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer));
else
{
auto e = TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
- attr.name, showType(*attr.value));
+ state.symbols[attr.name], showType(*attr.value));
state.debugLastTrace(e);
throw e;
}
@@ -160,7 +159,7 @@ static void fetchTree(
{
auto e = EvalError({
.msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -185,7 +184,7 @@ static void fetchTree(
if (evalSettings.pureEval && !input.isLocked())
{
- auto e = EvalError("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos);
+ auto e = EvalError("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos]);
state.debugLastTrace(e);
throw e;
}
@@ -197,7 +196,7 @@ static void fetchTree(
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
}
-static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
settings.requireExperimentalFeature(Xp::Flakes);
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
@@ -206,7 +205,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
// FIXME: document
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
-static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
+static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
const std::string & who, bool unpack, std::string name)
{
std::optional<std::string> url;
@@ -219,18 +218,18 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
- std::string n(attr.name);
+ std::string_view n(state.symbols[attr.name]);
if (n == "url")
- url = state.forceStringNoCtx(*attr.value, *attr.pos);
+ url = state.forceStringNoCtx(*attr.value, attr.pos);
else if (n == "sha256")
- expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
+ expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
else if (n == "name")
- name = state.forceStringNoCtx(*attr.value, *attr.pos);
+ name = state.forceStringNoCtx(*attr.value, attr.pos);
else
{
auto e = EvalError({
- .msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
- .errPos = *attr.pos
+ .msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
+ .errPos = state.positions[attr.pos]
});
state.debugLastTrace(e);
throw e;
@@ -241,7 +240,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
{
auto e = EvalError({
.msg = hintfmt("'url' argument required"),
- .errPos = pos
+ .errPos = state.positions[pos]
});
state.debugLastTrace(e);
throw e;
@@ -299,7 +298,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.allowAndSetStorePathString(storePath, v);
}
-static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
fetch(state, pos, args, v, "fetchurl", false, "");
}
@@ -315,7 +314,7 @@ static RegisterPrimOp primop_fetchurl({
.fun = prim_fetchurl,
});
-static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
fetch(state, pos, args, v, "fetchTarball", true, "source");
}
@@ -366,7 +365,7 @@ static RegisterPrimOp primop_fetchTarball({
.fun = prim_fetchTarball,
});
-static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
}
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index dd4280030..9753e2ac9 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -5,7 +5,7 @@
namespace nix {
-static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
+static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
{
auto toml = state.forceStringNoCtx(*args[0], pos);
@@ -73,7 +73,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
} catch (std::exception & e) { // TODO: toml::syntax_error
throw EvalError({
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
- .errPos = pos
+ .errPos = state.positions[pos]
});
}
}
diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh
index 48d20c29d..288c15602 100644
--- a/src/libexpr/symbol-table.hh
+++ b/src/libexpr/symbol-table.hh
@@ -5,44 +5,32 @@
#include <unordered_map>
#include "types.hh"
+#include "chunked-vector.hh"
namespace nix {
/* Symbol table used by the parser and evaluator to represent and look
up identifiers and attributes efficiently. SymbolTable::create()
converts a string into a symbol. Symbols have the property that
- they can be compared efficiently (using a pointer equality test),
+ they can be compared efficiently (using an equality test),
because the symbol table stores only one copy of each string. */
-class Symbol
+/* This class mainly exists to give us an operator<< for ostreams. We could also
+ return plain strings from SymbolTable, but then we'd have to wrap every
+ instance of a symbol that is fmt()ed, which is inconvenient and error-prone. */
+class SymbolStr
{
-private:
- const std::string * s; // pointer into SymbolTable
- Symbol(const std::string * s) : s(s) { };
friend class SymbolTable;
-public:
- Symbol() : s(0) { };
+private:
+ const std::string * s;
- bool operator == (const Symbol & s2) const
- {
- return s == s2.s;
- }
+ explicit SymbolStr(const std::string & symbol): s(&symbol) {}
- // FIXME: remove
+public:
bool operator == (std::string_view s2) const
{
- return s->compare(s2) == 0;
- }
-
- bool operator != (const Symbol & s2) const
- {
- return s != s2.s;
- }
-
- bool operator < (const Symbol & s2) const
- {
- return s < s2.s;
+ return *s == s2;
}
operator const std::string & () const
@@ -55,51 +43,78 @@ public:
return *s;
}
- bool set() const
- {
- return s;
- }
+ friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol);
+};
- bool empty() const
- {
- return s->empty();
- }
+class Symbol
+{
+ friend class SymbolTable;
+
+private:
+ uint32_t id;
+
+ explicit Symbol(uint32_t id): id(id) {}
+
+public:
+ Symbol() : id(0) {}
- friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
+ explicit operator bool() const { return id > 0; }
+
+ bool operator<(const Symbol other) const { return id < other.id; }
+ bool operator==(const Symbol other) const { return id == other.id; }
+ bool operator!=(const Symbol other) const { return id != other.id; }
};
class SymbolTable
{
private:
- std::unordered_map<std::string_view, Symbol> symbols;
- std::list<std::string> store;
+ std::unordered_map<std::string_view, std::pair<const std::string *, uint32_t>> symbols;
+ ChunkedVector<std::string, 8192> store{16};
public:
+
Symbol create(std::string_view s)
{
// Most symbols are looked up more than once, so we trade off insertion performance
// for lookup performance.
// TODO: could probably be done more efficiently with transparent Hash and Equals
// on the original implementation using unordered_set
+ // FIXME: make this thread-safe.
auto it = symbols.find(s);
- if (it != symbols.end()) return it->second;
+ if (it != symbols.end()) return Symbol(it->second.second + 1);
+
+ const auto & [rawSym, idx] = store.add(std::string(s));
+ symbols.emplace(rawSym, std::make_pair(&rawSym, idx));
+ return Symbol(idx + 1);
+ }
- auto & rawSym = store.emplace_back(s);
- return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
+ std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
+ {
+ std::vector<SymbolStr> result;
+ result.reserve(symbols.size());
+ for (auto sym : symbols)
+ result.push_back((*this)[sym]);
+ return result;
+ }
+
+ SymbolStr operator[](Symbol s) const
+ {
+ if (s.id == 0 || s.id > store.size())
+ abort();
+ return SymbolStr(store[s.id - 1]);
}
size_t size() const
{
- return symbols.size();
+ return store.size();
}
size_t totalSize() const;
template<typename T>
- void dump(T callback)
+ void dump(T callback) const
{
- for (auto & s : store)
- callback(s);
+ store.forEach(callback);
}
};
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index 2f34feb1f..34f3a34c8 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -10,7 +10,7 @@
namespace nix {
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context)
+ Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context)
{
checkInterrupt();
@@ -50,14 +50,14 @@ void printValueAsJSON(EvalState & state, bool strict,
auto obj(out.object());
StringSet names;
for (auto & j : *v.attrs)
- names.insert(j.name);
+ names.emplace(state.symbols[j.name]);
for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j)));
auto placeholder(obj.placeholder(j));
- printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context);
+ printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context);
}
} else
- printValueAsJSON(state, strict, *i->value, *i->pos, out, context);
+ printValueAsJSON(state, strict, *i->value, i->pos, out, context);
break;
}
@@ -82,16 +82,16 @@ void printValueAsJSON(EvalState & state, bool strict,
case nFunction:
auto e = TypeError({
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
- .errPos = v.determinePos(pos)
+ .errPos = state.positions[v.determinePos(pos)]
});
- e.addTrace(pos, hintfmt("message for the trace"));
+ e.addTrace(state.positions[pos], hintfmt("message for the trace"));
state.debugLastTrace(e);
throw e;
}
}
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const Pos & pos, std::ostream & str, PathSet & context)
+ Value & v, const PosIdx pos, std::ostream & str, PathSet & context)
{
JSONPlaceholder out(str);
printValueAsJSON(state, strict, v, pos, out, context);
diff --git a/src/libexpr/value-to-json.hh b/src/libexpr/value-to-json.hh
index c2f797b29..c020a817a 100644
--- a/src/libexpr/value-to-json.hh
+++ b/src/libexpr/value-to-json.hh
@@ -11,9 +11,9 @@ namespace nix {
class JSONPlaceholder;
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context);
+ Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context);
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const Pos & pos, std::ostream & str, PathSet & context);
+ Value & v, const PosIdx pos, std::ostream & str, PathSet & context);
}
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
index afeaf5694..7c3bf9492 100644
--- a/src/libexpr/value-to-xml.cc
+++ b/src/libexpr/value-to-xml.cc
@@ -19,10 +19,10 @@ 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,
- const Pos & pos);
+ const PosIdx pos);
-static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
+static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
{
xmlAttrs["path"] = pos.file;
xmlAttrs["line"] = (format("%1%") % pos.line).str();
@@ -36,25 +36,25 @@ static void showAttrs(EvalState & state, bool strict, bool location,
StringSet names;
for (auto & i : attrs)
- names.insert(i.name);
+ names.emplace(state.symbols[i.name]);
for (auto & i : names) {
Attr & a(*attrs.find(state.symbols.create(i)));
XMLAttrs xmlAttrs;
xmlAttrs["name"] = i;
- if (location && a.pos != ptr(&noPos)) posToXML(xmlAttrs, *a.pos);
+ if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]);
XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location,
- *a.value, doc, context, drvsSeen, *a.pos);
+ *a.value, doc, context, drvsSeen, a.pos);
}
}
static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
- const Pos & pos)
+ const PosIdx pos)
{
checkInterrupt();
@@ -93,14 +93,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
Path drvPath;
a = v.attrs->find(state.sDrvPath);
if (a != v.attrs->end()) {
- if (strict) state.forceValue(*a->value, *a->pos);
+ if (strict) state.forceValue(*a->value, a->pos);
if (a->value->type() == nString)
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
}
a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) {
- if (strict) state.forceValue(*a->value, *a->pos);
+ if (strict) state.forceValue(*a->value, a->pos);
if (a->value->type() == nString)
xmlAttrs["outPath"] = a->value->string.s;
}
@@ -134,18 +134,18 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break;
}
XMLAttrs xmlAttrs;
- if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
+ if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]);
XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->hasFormals()) {
XMLAttrs attrs;
- if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
+ if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg];
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
- for (auto & i : v.lambda.fun->formals->lexicographicOrder())
- doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
+ for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
+ doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
} else
- doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
+ doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
break;
}
@@ -166,14 +166,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
- const Pos & pos) const
+ const PosIdx pos) const
{
doc.writeEmptyElement("unevaluated");
}
void printValueAsXML(EvalState & state, bool strict, bool location,
- Value & v, std::ostream & out, PathSet & context, const Pos & pos)
+ Value & v, std::ostream & out, PathSet & context, const PosIdx pos)
{
XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr");
diff --git a/src/libexpr/value-to-xml.hh b/src/libexpr/value-to-xml.hh
index cc778a2cb..506f32b6b 100644
--- a/src/libexpr/value-to-xml.hh
+++ b/src/libexpr/value-to-xml.hh
@@ -9,6 +9,6 @@
namespace nix {
void printValueAsXML(EvalState & state, bool strict, bool location,
- Value & v, std::ostream & out, PathSet & context, const Pos & pos);
+ Value & v, std::ostream & out, PathSet & context, const PosIdx pos);
}
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 3d07c3198..58a8a56a0 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -56,6 +56,7 @@ struct Expr;
struct ExprLambda;
struct PrimOp;
class Symbol;
+class PosIdx;
struct Pos;
class StorePath;
class Store;
@@ -103,7 +104,7 @@ class ExternalValueBase
/* Print the value as XML. Defaults to unevaluated */
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
- const Pos & pos) const;
+ const PosIdx pos) const;
virtual ~ExternalValueBase()
{
@@ -120,11 +121,11 @@ private:
friend std::string showType(const Value & v);
- void print(std::ostream & str, std::set<const void *> * seen) const;
+ void print(const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen) const;
public:
- void print(std::ostream & str, bool showRepeated = false) const;
+ void print(const SymbolTable & symbols, std::ostream & str, bool showRepeated = false) const;
// Functions needed to distinguish the type
// These should be removed eventually, by putting the functionality that's
@@ -250,11 +251,6 @@ public:
void mkStringMove(const char * s, const PathSet & context);
- inline void mkString(const Symbol & s)
- {
- mkString(((const std::string &) s).c_str());
- }
-
inline void mkPath(const char * s)
{
clearValue();
@@ -368,7 +364,7 @@ public:
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
}
- Pos determinePos(const Pos & pos) const;
+ PosIdx determinePos(const PosIdx pos) const;
/* Check whether forcing this value requires a trivial amount of
computation. In particular, function applications are