mirror of
https://github.com/NixOS/nix.git
synced 2024-11-25 08:12:29 +00:00
Merge remote-tracking branch 'origin/master' into nixbuildaddprintstorepaths
This commit is contained in:
commit
be28603dca
@ -3,5 +3,9 @@
|
||||
* `nix repl` has a new build-'n-link (`:bl`) command that builds a derivation
|
||||
while creating GC root symlinks.
|
||||
|
||||
* The path produced by `builtins.toFile` is now allowed to be imported or read
|
||||
even with restricted evaluation. Note that this will not work with a
|
||||
read-only store.
|
||||
|
||||
* `nix build` has a new `--print-out-paths` flag to print the resulting output paths.
|
||||
This matches the default behaviour of `nix-build`.
|
||||
|
@ -22,6 +22,7 @@ let
|
||||
findutils
|
||||
iana-etc
|
||||
git
|
||||
openssh
|
||||
];
|
||||
|
||||
users = {
|
||||
|
@ -197,17 +197,17 @@ void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePath
|
||||
run(store, *storePaths.begin());
|
||||
}
|
||||
|
||||
Strings editorFor(const Pos & pos)
|
||||
Strings editorFor(const Path & file, uint32_t line)
|
||||
{
|
||||
auto editor = getEnv("EDITOR").value_or("cat");
|
||||
auto args = tokenizeString<Strings>(editor);
|
||||
if (pos.line > 0 && (
|
||||
if (line > 0 && (
|
||||
editor.find("emacs") != std::string::npos ||
|
||||
editor.find("nano") != std::string::npos ||
|
||||
editor.find("vim") != std::string::npos ||
|
||||
editor.find("kak") != std::string::npos))
|
||||
args.push_back(fmt("+%d", pos.line));
|
||||
args.push_back(pos.file);
|
||||
args.push_back(fmt("+%d", line));
|
||||
args.push_back(file);
|
||||
return args;
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
||||
|
||||
/* Helper function to generate args that invoke $EDITOR on
|
||||
filename:lineno. */
|
||||
Strings editorFor(const Pos & pos);
|
||||
Strings editorFor(const Path & file, uint32_t line);
|
||||
|
||||
struct MixProfile : virtual StoreCommand
|
||||
{
|
||||
|
@ -235,7 +235,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs) {
|
||||
std::string name = i.name;
|
||||
std::string name = state->symbols[i.name];
|
||||
if (name.find(searchWord) == 0) {
|
||||
if (prefix_ == "")
|
||||
completions->add(name);
|
||||
@ -473,7 +473,7 @@ struct InstallableAttrPath : InstallableValue
|
||||
|
||||
std::string what() const override { return attrPath; }
|
||||
|
||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override
|
||||
{
|
||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
||||
state.forceValue(*vRes, pos);
|
||||
@ -600,7 +600,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
||||
|
||||
auto drvInfo = DerivationInfo {
|
||||
std::move(drvPath),
|
||||
attr->getAttr(state->sOutputName)->getString()
|
||||
attr->getAttr("outputName")->getString()
|
||||
};
|
||||
|
||||
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
|
||||
@ -613,7 +613,7 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||
return res;
|
||||
}
|
||||
|
||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
||||
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||
{
|
||||
return {&getCursor(state)->forceValue(), noPos};
|
||||
}
|
||||
@ -831,8 +831,8 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
|
||||
auto realisation = store->queryRealisation(outputId);
|
||||
if (!realisation)
|
||||
throw Error(
|
||||
"cannot operate on an output of unbuilt "
|
||||
"content-addressed derivation '%s'",
|
||||
"cannot operate on an output of the "
|
||||
"unbuilt derivation '%s'",
|
||||
outputId.to_string());
|
||||
outputs.insert_or_assign(output, realisation->outPath);
|
||||
} else {
|
||||
|
@ -68,7 +68,7 @@ struct Installable
|
||||
|
||||
UnresolvedApp toApp(EvalState & state);
|
||||
|
||||
virtual std::pair<Value *, Pos> toValue(EvalState & state)
|
||||
virtual std::pair<Value *, PosIdx> toValue(EvalState & state)
|
||||
{
|
||||
throw Error("argument '%s' cannot be evaluated", what());
|
||||
}
|
||||
@ -178,7 +178,7 @@ struct InstallableFlake : InstallableValue
|
||||
|
||||
std::vector<DerivationInfo> toDerivations() override;
|
||||
|
||||
std::pair<Value *, Pos> toValue(EvalState & state) override;
|
||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||
|
||||
/* Get a cursor to every attrpath in getActualAttrPaths() that
|
||||
exists. */
|
||||
|
@ -36,18 +36,18 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
||||
{
|
||||
std::vector<Symbol> res;
|
||||
for (auto & a : parseAttrPath(s))
|
||||
res.push_back(state.symbols.create(a));
|
||||
res.emplace_back(a);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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, const SymbolIdx & 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(const SymbolIdx & 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);
|
||||
}
|
||||
|
@ -15,18 +15,27 @@ struct Value;
|
||||
/* Map one attribute name to its value. */
|
||||
struct Attr
|
||||
{
|
||||
Symbol name;
|
||||
/* 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. */
|
||||
SymbolIdx 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(SymbolIdx 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(const SymbolIdx & 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(const SymbolIdx & 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(SymbolIdx 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(const SymbolIdx & 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()
|
||||
{
|
||||
|
@ -253,7 +253,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(queryAttributes.getStr(0));
|
||||
return {{rowId, attrs}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
@ -325,7 +325,7 @@ AttrCursor::AttrCursor(
|
||||
AttrKey AttrCursor::getKey()
|
||||
{
|
||||
if (!parent)
|
||||
return {0, root->state.sEpsilon};
|
||||
return {0, {""}};
|
||||
if (!parent->first->cachedValue) {
|
||||
parent->first->cachedValue = root->db->getAttr(
|
||||
parent->first->getKey(), root->state.symbols);
|
||||
@ -340,7 +340,7 @@ Value & AttrCursor::getValue()
|
||||
if (parent) {
|
||||
auto & vParent = parent->first->getValue();
|
||||
root->state.forceAttrs(vParent, noPos);
|
||||
auto attr = vParent.attrs->get(parent->second);
|
||||
auto attr = vParent.attrs->get(root->state.symbols.create(parent->second));
|
||||
if (!attr)
|
||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
||||
_value = allocRootValue(attr->value);
|
||||
@ -419,7 +419,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
|
||||
return Suggestions::bestMatches(strAttrNames, name);
|
||||
}
|
||||
|
||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
|
||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name, bool forceErrors)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
@ -461,10 +461,10 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
||||
|
||||
for (auto & attr : *v.attrs) {
|
||||
if (root->db)
|
||||
root->db->setPlaceholder({cachedValue->first, attr.name});
|
||||
root->db->setPlaceholder({cachedValue->first, root->state.symbols[attr.name]});
|
||||
}
|
||||
|
||||
auto attr = v.attrs->get(name);
|
||||
auto attr = v.attrs->get(root->state.symbols.create(name));
|
||||
|
||||
if (!attr) {
|
||||
if (root->db) {
|
||||
@ -486,12 +486,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
||||
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
|
||||
}
|
||||
|
||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name)
|
||||
{
|
||||
return maybeGetAttr(root->state.symbols.create(name));
|
||||
}
|
||||
|
||||
ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
|
||||
ref<AttrCursor> AttrCursor::getAttr(std::string_view name, bool forceErrors)
|
||||
{
|
||||
auto p = maybeGetAttr(name, forceErrors);
|
||||
if (!p)
|
||||
@ -499,11 +494,6 @@ ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
|
||||
return ref(p);
|
||||
}
|
||||
|
||||
ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
|
||||
{
|
||||
return getAttr(root->state.symbols.create(name));
|
||||
}
|
||||
|
||||
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
|
||||
{
|
||||
auto res = shared_from_this();
|
||||
@ -616,7 +606,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
||||
|
||||
std::vector<Symbol> attrs;
|
||||
for (auto & attr : *getValue().attrs)
|
||||
attrs.push_back(attr.name);
|
||||
attrs.push_back(root->state.symbols[attr.name]);
|
||||
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
|
||||
return (const std::string &) a < (const std::string &) b;
|
||||
});
|
||||
@ -635,7 +625,7 @@ bool AttrCursor::isDerivation()
|
||||
|
||||
StorePath AttrCursor::forceDerivation()
|
||||
{
|
||||
auto aDrvPath = getAttr(root->state.sDrvPath, true);
|
||||
auto aDrvPath = getAttr("drvPath", true);
|
||||
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
|
||||
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
|
||||
/* The eval cache contains 'drvPath', but the actual path has
|
||||
|
@ -96,13 +96,9 @@ public:
|
||||
|
||||
Suggestions getSuggestionsForAttr(Symbol name);
|
||||
|
||||
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name, bool forceErrors = false);
|
||||
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name, bool forceErrors = false);
|
||||
|
||||
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name);
|
||||
|
||||
ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
|
||||
|
||||
ref<AttrCursor> getAttr(std::string_view name);
|
||||
ref<AttrCursor> getAttr(std::string_view name, bool forceErrors = false);
|
||||
|
||||
/* Get an attribute along a chain of attrsets. Note that this does
|
||||
not auto-call functors or functions. */
|
||||
|
@ -2,27 +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))
|
||||
{
|
||||
throw EvalError({
|
||||
.msg = hintfmt(s),
|
||||
.errPos = pos
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
||||
{
|
||||
throw TypeError({
|
||||
.msg = hintfmt(s, showType(v)),
|
||||
.errPos = pos
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||
[[gnu::always_inline]]
|
||||
@ -99,7 +80,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; });
|
||||
}
|
||||
@ -128,7 +109,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; });
|
||||
}
|
||||
@ -145,7 +126,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())
|
||||
|
@ -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;
|
||||
@ -306,9 +308,9 @@ static BoehmGCStackAllocator boehmGCStackAllocator;
|
||||
#endif
|
||||
|
||||
|
||||
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
||||
static SymbolIdx getName(const AttrName & name, EvalState & state, Env & env)
|
||||
{
|
||||
if (name.symbol.set()) {
|
||||
if (name.symbol) {
|
||||
return name.symbol;
|
||||
} else {
|
||||
Value nameValue;
|
||||
@ -639,20 +641,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));
|
||||
@ -667,21 +669,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;
|
||||
}
|
||||
|
||||
@ -698,7 +700,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,
|
||||
@ -714,113 +716,133 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||
evaluator. So here are some helper functions for throwing
|
||||
exceptions. */
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2))
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s) const
|
||||
{
|
||||
throw EvalError({
|
||||
.msg = hintfmt(s),
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
|
||||
{
|
||||
throw TypeError({
|
||||
.msg = hintfmt(s, showType(v)),
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
void EvalState::throwEvalError(const char * s, const std::string & s2) const
|
||||
{
|
||||
throw EvalError(s, s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2))
|
||||
void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||
const std::string & s2) const
|
||||
{
|
||||
throw EvalError(ErrorInfo {
|
||||
.msg = hintfmt(s, s2),
|
||||
.errPos = pos,
|
||||
.errPos = positions[pos],
|
||||
.suggestions = suggestions,
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
|
||||
{
|
||||
throw EvalError(ErrorInfo {
|
||||
.msg = hintfmt(s, s2),
|
||||
.errPos = pos
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3))
|
||||
void EvalState::throwEvalError(const char * s, const std::string & s2, const std::string & s3) const
|
||||
{
|
||||
throw EvalError(s, s2, s3);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3))
|
||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
||||
const std::string & s3) const
|
||||
{
|
||||
throw EvalError({
|
||||
.msg = hintfmt(s, s2, s3),
|
||||
.errPos = pos
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
|
||||
void EvalState::throwEvalError(const PosIdx p1, const char * s, const SymbolIdx sym, const PosIdx p2) const
|
||||
{
|
||||
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
||||
throw EvalError({
|
||||
.msg = hintfmt(s, sym, p2),
|
||||
.errPos = p1
|
||||
.msg = hintfmt(s, symbols[sym], positions[p2]),
|
||||
.errPos = positions[p1]
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
|
||||
void EvalState::throwTypeError(const PosIdx pos, const char * s) const
|
||||
{
|
||||
throw TypeError({
|
||||
.msg = hintfmt(s),
|
||||
.errPos = pos
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
|
||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
|
||||
const SymbolIdx s2) const
|
||||
{
|
||||
throw TypeError({
|
||||
.msg = hintfmt(s, fun.showNamePos(), s2),
|
||||
.errPos = pos
|
||||
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2))
|
||||
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||
const ExprLambda & fun, const SymbolIdx s2) const
|
||||
{
|
||||
throw TypeError(ErrorInfo {
|
||||
.msg = hintfmt(s, fun.showNamePos(), s2),
|
||||
.errPos = pos,
|
||||
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||
.errPos = positions[pos],
|
||||
.suggestions = suggestions,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||
void EvalState::throwTypeError(const char * s, const Value & v) const
|
||||
{
|
||||
throw TypeError(s, showType(v));
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1))
|
||||
void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const
|
||||
{
|
||||
throw AssertionError({
|
||||
.msg = hintfmt(s, s1),
|
||||
.errPos = pos
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1))
|
||||
void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const
|
||||
{
|
||||
throw UndefinedVarError({
|
||||
.msg = hintfmt(s, s1),
|
||||
.errPos = pos
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1))
|
||||
void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const
|
||||
{
|
||||
throw MissingArgumentError({
|
||||
.msg = hintfmt(s, s1),
|
||||
.errPos = pos
|
||||
.errPos = positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -877,11 +899,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);
|
||||
throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name]);
|
||||
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
}
|
||||
@ -911,13 +933,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();
|
||||
@ -1050,7 +1073,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);
|
||||
@ -1124,7 +1147,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
|
||||
@ -1156,7 +1179,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) {
|
||||
@ -1166,18 +1189,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);
|
||||
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -1222,10 +1245,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();
|
||||
@ -1235,7 +1260,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);
|
||||
@ -1245,7 +1270,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 ||
|
||||
@ -1259,23 +1284,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);
|
||||
Suggestions::bestMatches(allAttrNames, state.symbols[name]),
|
||||
"attribute '%1%' missing", state.symbols[name]);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -1294,7 +1320,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())
|
||||
{
|
||||
@ -1315,9 +1341,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);
|
||||
|
||||
@ -1342,7 +1370,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;
|
||||
@ -1355,7 +1383,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
|
||||
@ -1383,10 +1411,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);
|
||||
@ -1404,8 +1432,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", "");
|
||||
}
|
||||
@ -1554,7 +1582,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]);
|
||||
|
||||
}
|
||||
}
|
||||
@ -1586,8 +1614,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());
|
||||
cond->show(state.symbols, out);
|
||||
state.throwAssertionError(pos, "assertion '%1%' failed", out.str());
|
||||
}
|
||||
body->eval(state, env, v);
|
||||
}
|
||||
@ -1680,7 +1708,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++;
|
||||
|
||||
@ -1764,14 +1792,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
nf = n;
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
|
||||
state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
|
||||
} 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));
|
||||
state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
||||
} else {
|
||||
if (s.empty()) s.reserve(es->size());
|
||||
/* skip canonization of first path, which would only be not
|
||||
@ -1791,7 +1819,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");
|
||||
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||
v.mkPath(canonPath(str()));
|
||||
} else
|
||||
v.mkStringMove(c_str(), context);
|
||||
@ -1800,7 +1828,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);
|
||||
}
|
||||
|
||||
|
||||
@ -1820,7 +1848,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||
try {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1835,7 +1863,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)
|
||||
@ -1844,7 +1872,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)
|
||||
@ -1855,7 +1883,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)
|
||||
@ -1870,7 +1898,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))
|
||||
@ -1878,7 +1906,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) {
|
||||
@ -1931,7 +1959,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);
|
||||
@ -1939,7 +1967,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) {
|
||||
@ -1959,13 +1987,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);
|
||||
@ -1978,7 +2006,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);
|
||||
@ -2007,7 +2035,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) {
|
||||
|
||||
@ -2060,7 +2088,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] != '/')
|
||||
@ -2069,14 +2097,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]
|
||||
});
|
||||
}
|
||||
|
||||
@ -2243,14 +2271,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);
|
||||
}
|
||||
@ -2259,10 +2287,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);
|
||||
}
|
||||
|
@ -23,14 +23,14 @@ 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);
|
||||
|
||||
|
||||
struct PrimOp
|
||||
{
|
||||
PrimOpFun fun;
|
||||
size_t arity;
|
||||
Symbol name;
|
||||
std::string name;
|
||||
std::vector<std::string> args;
|
||||
const char * doc = nullptr;
|
||||
};
|
||||
@ -53,7 +53,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;
|
||||
@ -73,8 +74,11 @@ class EvalState
|
||||
{
|
||||
public:
|
||||
SymbolTable symbols;
|
||||
PosTable positions;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
static inline std::string derivationNixPath = "//builtin/derivation.nix";
|
||||
|
||||
const SymbolIdx sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||
sFile, sLine, sColumn, sFunctor, sToString,
|
||||
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
||||
@ -83,7 +87,7 @@ public:
|
||||
sRecurseForDerivations,
|
||||
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
|
||||
sPrefix;
|
||||
Symbol sDerivationNix;
|
||||
SymbolIdx sDerivationNix;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
@ -205,7 +209,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);
|
||||
@ -217,14 +221,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);
|
||||
@ -234,33 +238,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 SymbolIdx 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 SymbolIdx s2) const;
|
||||
[[gnu::noinline, gnu::noreturn]]
|
||||
void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||
const ExprLambda & fun, const SymbolIdx 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);
|
||||
|
||||
@ -269,10 +312,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:
|
||||
|
||||
@ -305,7 +348,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;
|
||||
@ -333,9 +376,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);
|
||||
@ -349,7 +392,7 @@ public:
|
||||
inline Value * allocValue();
|
||||
inline Env & allocEnv(size_t size);
|
||||
|
||||
Value * allocAttr(Value & vAttrs, const Symbol & name);
|
||||
Value * allocAttr(Value & vAttrs, const SymbolIdx & name);
|
||||
Value * allocAttr(Value & vAttrs, std::string_view name);
|
||||
|
||||
Bindings * allocBindings(size_t capacity);
|
||||
@ -361,9 +404,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();
|
||||
@ -391,7 +434,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;
|
||||
@ -399,7 +442,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;
|
||||
@ -410,9 +453,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;
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -8,7 +8,7 @@ namespace nix {
|
||||
|
||||
struct FunctionCallTrace
|
||||
{
|
||||
const Pos & pos;
|
||||
const Pos pos;
|
||||
FunctionCallTrace(const Pos & pos);
|
||||
~FunctionCallTrace();
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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],
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "nixexpr.hh"
|
||||
#include "derivations.hh"
|
||||
#include "eval.hh"
|
||||
#include "symbol-table.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
@ -10,12 +12,6 @@ namespace nix {
|
||||
|
||||
/* 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 << '"';
|
||||
@ -54,81 +50,101 @@ static void showId(std::ostream & str, std::string_view s)
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Symbol & sym)
|
||||
{
|
||||
showId(str, *sym.s);
|
||||
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()) {
|
||||
@ -136,74 +152,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";
|
||||
}
|
||||
@ -234,48 +276,49 @@ 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 StaticEnv & env)
|
||||
void Expr::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void ExprInt::bindVars(const StaticEnv & env)
|
||||
void ExprInt::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void ExprFloat::bindVars(const StaticEnv & env)
|
||||
void ExprFloat::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void ExprString::bindVars(const StaticEnv & env)
|
||||
void ExprString::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void ExprPath::bindVars(const StaticEnv & env)
|
||||
void ExprPath::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void ExprVar::bindVars(const StaticEnv & env)
|
||||
void ExprVar::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
/* Check whether the variable appears in the environment. If so,
|
||||
set its level and displacement. */
|
||||
@ -301,31 +344,31 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||
"undefined variable" error now. */
|
||||
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 StaticEnv & env)
|
||||
void ExprSelect::bindVars(const EvalState & es, const 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 StaticEnv & env)
|
||||
void ExprOpHasAttr::bindVars(const EvalState & es, const 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 StaticEnv & env)
|
||||
void ExprAttrs::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
const StaticEnv * dynamicEnv = &env;
|
||||
StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
|
||||
@ -340,35 +383,35 @@ void ExprAttrs::bindVars(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);
|
||||
}
|
||||
|
||||
else
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(env);
|
||||
i.second.e->bindVars(es, env);
|
||||
|
||||
for (auto & i : dynamicAttrs) {
|
||||
i.nameExpr->bindVars(*dynamicEnv);
|
||||
i.valueExpr->bindVars(*dynamicEnv);
|
||||
i.nameExpr->bindVars(es, *dynamicEnv);
|
||||
i.valueExpr->bindVars(es, *dynamicEnv);
|
||||
}
|
||||
}
|
||||
|
||||
void ExprList::bindVars(const StaticEnv & env)
|
||||
void ExprList::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
for (auto & i : elems)
|
||||
i->bindVars(env);
|
||||
i->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprLambda::bindVars(const StaticEnv & env)
|
||||
void ExprLambda::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(
|
||||
false, &env,
|
||||
(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)
|
||||
@ -377,20 +420,20 @@ void ExprLambda::bindVars(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 StaticEnv & env)
|
||||
void ExprCall::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
fun->bindVars(env);
|
||||
fun->bindVars(es, env);
|
||||
for (auto e : args)
|
||||
e->bindVars(env);
|
||||
e->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprLet::bindVars(const StaticEnv & env)
|
||||
void ExprLet::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env, attrs->attrs.size());
|
||||
|
||||
@ -401,12 +444,12 @@ void ExprLet::bindVars(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 StaticEnv & env)
|
||||
void ExprWith::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
/* Does this `with' have an enclosing `with'? If so, record its
|
||||
level so that `lookupVar' can look up variables in the previous
|
||||
@ -420,57 +463,60 @@ void ExprWith::bindVars(const StaticEnv & env)
|
||||
break;
|
||||
}
|
||||
|
||||
attrs->bindVars(env);
|
||||
attrs->bindVars(es, env);
|
||||
StaticEnv newEnv(true, &env);
|
||||
body->bindVars(newEnv);
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
|
||||
void ExprIf::bindVars(const StaticEnv & env)
|
||||
void ExprIf::bindVars(const EvalState & es, const 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 StaticEnv & env)
|
||||
void ExprAssert::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
cond->bindVars(env);
|
||||
body->bindVars(env);
|
||||
cond->bindVars(es, env);
|
||||
body->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprOpNot::bindVars(const StaticEnv & env)
|
||||
void ExprOpNot::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
e->bindVars(env);
|
||||
e->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||
void ExprConcatStrings::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
for (auto & i : *es)
|
||||
i.second->bindVars(env);
|
||||
for (auto & i : *this->es)
|
||||
i.second->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprPos::bindVars(const StaticEnv & env)
|
||||
void ExprPos::bindVars(const EvalState & es, const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Storing function names. */
|
||||
|
||||
void Expr::setName(Symbol & name)
|
||||
void Expr::setName(SymbolIdx name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ExprLambda::setName(Symbol & name)
|
||||
void ExprLambda::setName(SymbolIdx 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]);
|
||||
}
|
||||
|
||||
|
||||
@ -480,8 +526,7 @@ std::string ExprLambda::showNamePos() const
|
||||
size_t SymbolTable::totalSize() const
|
||||
{
|
||||
size_t n = 0;
|
||||
for (auto & i : store)
|
||||
n += i.size();
|
||||
dump([&] (const Symbol & s) { n += std::string_view(s).size(); });
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
@ -23,32 +27,92 @@ MakeError(RestrictedPathError, Error);
|
||||
|
||||
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)
|
||||
{
|
||||
return line != 0;
|
||||
origins.reserve(1024);
|
||||
}
|
||||
|
||||
bool operator < (const Pos & p2) const
|
||||
PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
Pos operator[](PosIdx p) const
|
||||
{
|
||||
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);
|
||||
|
||||
@ -62,15 +126,15 @@ struct StaticEnv;
|
||||
/* An attribute path is a sequence of attribute names. */
|
||||
struct AttrName
|
||||
{
|
||||
Symbol symbol;
|
||||
SymbolIdx symbol;
|
||||
Expr * expr;
|
||||
AttrName(const Symbol & s) : symbol(s) {};
|
||||
AttrName(const SymbolIdx & 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. */
|
||||
@ -78,19 +142,17 @@ std::string showAttrPath(const AttrPath & attrPath);
|
||||
struct Expr
|
||||
{
|
||||
virtual ~Expr() { };
|
||||
virtual void show(std::ostream & str) const;
|
||||
virtual void bindVars(const StaticEnv & env);
|
||||
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
||||
virtual void bindVars(const EvalState & es, 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(SymbolIdx name);
|
||||
};
|
||||
|
||||
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 StaticEnv & env);
|
||||
void bindVars(const EvalState & es, const StaticEnv & env);
|
||||
|
||||
struct ExprInt : Expr
|
||||
{
|
||||
@ -133,8 +195,8 @@ typedef uint32_t Displacement;
|
||||
|
||||
struct ExprVar : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
PosIdx pos;
|
||||
SymbolIdx name;
|
||||
|
||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||
or function argument) or from a "with". */
|
||||
@ -149,19 +211,19 @@ 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(const SymbolIdx & name) : name(name) { };
|
||||
ExprVar(const PosIdx & pos, const SymbolIdx & name) : pos(pos), name(name) { };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
|
||||
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)); };
|
||||
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, const SymbolIdx & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@ -176,28 +238,28 @@ struct ExprOpHasAttr : Expr
|
||||
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() { };
|
||||
};
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
typedef std::map<SymbolIdx, AttrDef> AttrDefs;
|
||||
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) { };
|
||||
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
|
||||
ExprAttrs() : recursive(false) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@ -210,10 +272,9 @@ struct ExprList : Expr
|
||||
|
||||
struct Formal
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
PosIdx pos;
|
||||
SymbolIdx name;
|
||||
Expr * def;
|
||||
Formal(const Pos & pos, const Symbol & name, Expr * def) : pos(pos), name(name), def(def) { };
|
||||
};
|
||||
|
||||
struct Formals
|
||||
@ -222,18 +283,19 @@ struct Formals
|
||||
Formals_ formals;
|
||||
bool ellipsis;
|
||||
|
||||
bool has(Symbol arg) const {
|
||||
bool has(SymbolIdx arg) const {
|
||||
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
|
||||
[] (const Formal & f, const Symbol & sym) { return f.name < sym; });
|
||||
[] (const Formal & f, const SymbolIdx & 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;
|
||||
}
|
||||
@ -241,17 +303,21 @@ struct Formals
|
||||
|
||||
struct ExprLambda : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
Symbol arg;
|
||||
PosIdx pos;
|
||||
SymbolIdx name;
|
||||
SymbolIdx arg;
|
||||
Formals * formals;
|
||||
Expr * body;
|
||||
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
|
||||
ExprLambda(PosIdx pos, SymbolIdx 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(SymbolIdx name);
|
||||
std::string showNamePos(const EvalState & state) const;
|
||||
inline bool hasFormals() const { return formals != nullptr; }
|
||||
COMMON_METHODS
|
||||
};
|
||||
@ -260,8 +326,8 @@ 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)
|
||||
{ }
|
||||
COMMON_METHODS
|
||||
@ -277,26 +343,26 @@ struct ExprLet : Expr
|
||||
|
||||
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) { };
|
||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
||||
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_) { };
|
||||
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
||||
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) { };
|
||||
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@ -310,17 +376,17 @@ 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 StaticEnv & env) \
|
||||
void bindVars(const EvalState & es, 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); \
|
||||
};
|
||||
@ -335,18 +401,18 @@ 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) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprPos : Expr
|
||||
{
|
||||
Pos pos;
|
||||
ExprPos(const Pos & pos) : pos(pos) { };
|
||||
PosIdx pos;
|
||||
ExprPos(const PosIdx & pos) : pos(pos) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@ -360,7 +426,7 @@ struct StaticEnv
|
||||
const StaticEnv * up;
|
||||
|
||||
// Note: these must be in sorted order.
|
||||
typedef std::vector<std::pair<Symbol, Displacement>> Vars;
|
||||
typedef std::vector<std::pair<SymbolIdx, Displacement>> Vars;
|
||||
Vars vars;
|
||||
|
||||
StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
|
||||
@ -384,7 +450,7 @@ struct StaticEnv
|
||||
vars.erase(it, end);
|
||||
}
|
||||
|
||||
Vars::const_iterator find(const Symbol & name) const
|
||||
Vars::const_iterator find(const SymbolIdx & name) const
|
||||
{
|
||||
Vars::value_type key(name, 0);
|
||||
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
||||
|
@ -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, SymbolIdx 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, SymbolIdx 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<SymbolIdx, 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, 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]
|
||||
});
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
@ -110,43 +110,43 @@ static void fetchTree(
|
||||
if (type)
|
||||
throw Error({
|
||||
.msg = hintfmt("unexpected attribute 'type'"),
|
||||
.errPos = pos
|
||||
.errPos = state.positions[pos]
|
||||
});
|
||||
type = state.forceStringNoCtx(*aType->value, *aType->pos);
|
||||
type = state.forceStringNoCtx(*aType->value, aType->pos);
|
||||
} else if (!type)
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||
.errPos = pos
|
||||
.errPos = state.positions[pos]
|
||||
});
|
||||
|
||||
attrs.emplace("type", type.value());
|
||||
|
||||
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
|
||||
throw 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));
|
||||
}
|
||||
|
||||
if (!params.allowNameArgument)
|
||||
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"),
|
||||
.errPos = pos
|
||||
.errPos = state.positions[pos]
|
||||
});
|
||||
|
||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||
@ -167,7 +167,7 @@ static void fetchTree(
|
||||
input = lookupInRegistries(state.store, input).first;
|
||||
|
||||
if (evalSettings.pureEval && !input.isLocked())
|
||||
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos);
|
||||
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos]);
|
||||
|
||||
auto [tree, input2] = input.fetch(state.store);
|
||||
|
||||
@ -176,7 +176,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 });
|
||||
@ -185,7 +185,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;
|
||||
@ -198,24 +198,24 @@ 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
|
||||
throw 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]
|
||||
});
|
||||
}
|
||||
|
||||
if (!url)
|
||||
throw EvalError({
|
||||
.msg = hintfmt("'url' argument required"),
|
||||
.errPos = pos
|
||||
.errPos = state.positions[pos]
|
||||
});
|
||||
} else
|
||||
url = state.forceStringNoCtx(*args[0], pos);
|
||||
@ -262,7 +262,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, "");
|
||||
}
|
||||
@ -278,7 +278,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");
|
||||
}
|
||||
@ -329,7 +329,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 });
|
||||
}
|
||||
|
@ -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]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types.hh"
|
||||
#include "chunked-vector.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@ -16,90 +17,90 @@ namespace nix {
|
||||
|
||||
class Symbol
|
||||
{
|
||||
private:
|
||||
const std::string * s; // pointer into SymbolTable
|
||||
Symbol(const std::string * s) : s(s) { };
|
||||
friend class SymbolTable;
|
||||
private:
|
||||
std::string s;
|
||||
|
||||
public:
|
||||
Symbol() : s(0) { };
|
||||
|
||||
bool operator == (const Symbol & s2) const
|
||||
{
|
||||
return s == s2.s;
|
||||
}
|
||||
Symbol(std::string_view s) : s(s) { }
|
||||
|
||||
// FIXME: remove
|
||||
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
|
||||
{
|
||||
return *s;
|
||||
return s;
|
||||
}
|
||||
|
||||
operator const std::string_view () const
|
||||
{
|
||||
return *s;
|
||||
}
|
||||
|
||||
bool set() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return s->empty();
|
||||
}
|
||||
|
||||
friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
|
||||
};
|
||||
|
||||
class SymbolIdx
|
||||
{
|
||||
friend class SymbolTable;
|
||||
|
||||
private:
|
||||
uint32_t id;
|
||||
|
||||
explicit SymbolIdx(uint32_t id): id(id) {}
|
||||
|
||||
public:
|
||||
SymbolIdx() : id(0) {}
|
||||
|
||||
explicit operator bool() const { return id > 0; }
|
||||
|
||||
bool operator<(const SymbolIdx other) const { return id < other.id; }
|
||||
bool operator==(const SymbolIdx other) const { return id == other.id; }
|
||||
bool operator!=(const SymbolIdx 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 Symbol *, uint32_t>> symbols;
|
||||
ChunkedVector<Symbol, 8192> store{16};
|
||||
|
||||
public:
|
||||
Symbol create(std::string_view s)
|
||||
SymbolIdx 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
|
||||
auto it = symbols.find(s);
|
||||
if (it != symbols.end()) return it->second;
|
||||
if (it != symbols.end()) return SymbolIdx(it->second.second + 1);
|
||||
|
||||
auto & rawSym = store.emplace_back(s);
|
||||
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
|
||||
const auto & [rawSym, idx] = store.add(s);
|
||||
symbols.emplace(rawSym, std::make_pair(&rawSym, idx));
|
||||
return SymbolIdx(idx + 1);
|
||||
}
|
||||
|
||||
const Symbol & operator[](SymbolIdx s) const
|
||||
{
|
||||
if (s.id == 0 || s.id > store.size())
|
||||
abort();
|
||||
return 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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,15 +82,15 @@ 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"));
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ struct Env;
|
||||
struct Expr;
|
||||
struct ExprLambda;
|
||||
struct PrimOp;
|
||||
class Symbol;
|
||||
class SymbolIdx;
|
||||
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
|
||||
@ -252,7 +253,7 @@ public:
|
||||
|
||||
inline void mkString(const Symbol & s)
|
||||
{
|
||||
mkString(((const std::string &) s).c_str());
|
||||
mkString(std::string_view(s).data());
|
||||
}
|
||||
|
||||
inline void mkPath(const char * s)
|
||||
@ -368,7 +369,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
|
||||
@ -409,12 +410,12 @@ public:
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
|
||||
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
|
||||
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap;
|
||||
typedef std::map<SymbolIdx, Value *, std::less<SymbolIdx>, traceable_allocator<std::pair<const SymbolIdx, Value *> > > ValueMap;
|
||||
typedef std::map<SymbolIdx, ValueVector, std::less<SymbolIdx>, traceable_allocator<std::pair<const SymbolIdx, ValueVector> > > ValueVectorMap;
|
||||
#else
|
||||
typedef std::vector<Value *> ValueVector;
|
||||
typedef std::map<Symbol, Value *> ValueMap;
|
||||
typedef std::map<Symbol, ValueVector> ValueVectorMap;
|
||||
typedef std::map<SymbolIdx, Value *> ValueMap;
|
||||
typedef std::map<SymbolIdx, ValueVector> ValueVectorMap;
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ static std::string runHg(const Strings & args, const std::optional<std::string>
|
||||
auto res = runProgram(std::move(opts));
|
||||
|
||||
if (!statusOk(res.first))
|
||||
throw ExecError(res.first, fmt("hg %1%", statusToString(res.first)));
|
||||
throw ExecError(res.first, "hg %1%", statusToString(res.first));
|
||||
|
||||
return res.second;
|
||||
}
|
||||
@ -273,7 +273,7 @@ struct MercurialInputScheme : InputScheme
|
||||
runHg({ "recover", "-R", cacheDir });
|
||||
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
|
||||
} else {
|
||||
throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
|
||||
throw ExecError(e.status, "'hg pull' %s", statusToString(e.status));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -60,37 +60,37 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||
{
|
||||
if (!willBuild.empty()) {
|
||||
if (willBuild.size() == 1)
|
||||
printMsg(lvl, fmt("this derivation will be built:"));
|
||||
printMsg(lvl, "this derivation will be built:");
|
||||
else
|
||||
printMsg(lvl, fmt("these %d derivations will be built:", willBuild.size()));
|
||||
printMsg(lvl, "these %d derivations will be built:", willBuild.size());
|
||||
auto sorted = store->topoSortPaths(willBuild);
|
||||
reverse(sorted.begin(), sorted.end());
|
||||
for (auto & i : sorted)
|
||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
||||
printMsg(lvl, " %s", store->printStorePath(i));
|
||||
}
|
||||
|
||||
if (!willSubstitute.empty()) {
|
||||
const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
|
||||
const float narSizeMiB = narSize / (1024.f * 1024.f);
|
||||
if (willSubstitute.size() == 1) {
|
||||
printMsg(lvl, fmt("this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
printMsg(lvl, "this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
downloadSizeMiB,
|
||||
narSizeMiB));
|
||||
narSizeMiB);
|
||||
} else {
|
||||
printMsg(lvl, fmt("these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
printMsg(lvl, "these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
willSubstitute.size(),
|
||||
downloadSizeMiB,
|
||||
narSizeMiB));
|
||||
narSizeMiB);
|
||||
}
|
||||
for (auto & i : willSubstitute)
|
||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
||||
printMsg(lvl, " %s", store->printStorePath(i));
|
||||
}
|
||||
|
||||
if (!unknown.empty()) {
|
||||
printMsg(lvl, fmt("don't know how to build these paths%s:",
|
||||
(settings.readOnlyMode ? " (may be caused by read-only store access)" : "")));
|
||||
printMsg(lvl, "don't know how to build these paths%s:",
|
||||
(settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||
for (auto & i : unknown)
|
||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
||||
printMsg(lvl, " %s", store->printStorePath(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,27 @@ create table if not exists Realisations (
|
||||
|
||||
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||
|
||||
-- We can end-up in a weird edge-case where a path depends on itself because
|
||||
-- it’s an output of a CA derivation, that happens to be the same as one of its
|
||||
-- dependencies.
|
||||
-- In that case we have a dependency loop (path -> realisation1 -> realisation2
|
||||
-- -> path) that we need to break by removing the dependencies between the
|
||||
-- realisations
|
||||
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
|
||||
begin
|
||||
delete from RealisationsRefs where realisationReference in (
|
||||
select id from Realisations where outputPath = old.id
|
||||
);
|
||||
end;
|
||||
|
||||
create table if not exists RealisationsRefs (
|
||||
referrer integer not null,
|
||||
realisationReference integer,
|
||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||
);
|
||||
-- used by deletion trigger
|
||||
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
|
||||
|
||||
-- used by QueryRealisationReferences
|
||||
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
||||
|
@ -443,14 +443,13 @@ struct curlFileTransfer : public FileTransfer
|
||||
: httpStatus != 0
|
||||
? FileTransferError(err,
|
||||
std::move(response),
|
||||
fmt("unable to %s '%s': HTTP error %d ('%s')",
|
||||
request.verb(), request.uri, httpStatus, statusMsg)
|
||||
+ (code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
||||
)
|
||||
"unable to %s '%s': HTTP error %d%s",
|
||||
request.verb(), request.uri, httpStatus,
|
||||
code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
||||
: FileTransferError(err,
|
||||
std::move(response),
|
||||
fmt("unable to %s '%s': %s (%d)",
|
||||
request.verb(), request.uri, curl_easy_strerror(code), code));
|
||||
"unable to %s '%s': %s (%d)",
|
||||
request.verb(), request.uri, curl_easy_strerror(code), code);
|
||||
|
||||
/* If this is a transient error, then maybe retry the
|
||||
download after a while. If we're writing to a
|
||||
@ -704,7 +703,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
auto s3Res = s3Helper.getObject(bucketName, key);
|
||||
FileTransferResult res;
|
||||
if (!s3Res.data)
|
||||
throw FileTransferError(NotFound, {}, "S3 object '%s' does not exist", request.uri);
|
||||
throw FileTransferError(NotFound, "S3 object '%s' does not exist", request.uri);
|
||||
res.data = std::move(*s3Res.data);
|
||||
callback(std::move(res));
|
||||
#else
|
||||
|
@ -81,7 +81,7 @@ int getSchema(Path schemaPath)
|
||||
|
||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||
{
|
||||
const int nixCASchemaVersion = 3;
|
||||
const int nixCASchemaVersion = 4;
|
||||
int curCASchema = getSchema(schemaPath);
|
||||
if (curCASchema != nixCASchemaVersion) {
|
||||
if (curCASchema > nixCASchemaVersion) {
|
||||
@ -143,6 +143,21 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||
)");
|
||||
txn.commit();
|
||||
}
|
||||
if (curCASchema < 4) {
|
||||
SQLiteTxn txn(db);
|
||||
db.exec(R"(
|
||||
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
|
||||
begin
|
||||
delete from RealisationsRefs where realisationReference in (
|
||||
select id from Realisations where outputPath = old.id
|
||||
);
|
||||
end;
|
||||
-- used by deletion trigger
|
||||
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
|
||||
)");
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
||||
lockFile(lockFd.get(), ltRead, true);
|
||||
}
|
||||
@ -482,18 +497,18 @@ void LocalStore::openDB(State & state, bool create)
|
||||
SQLiteStmt stmt;
|
||||
stmt.create(db, "pragma main.journal_mode;");
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||
throwSQLiteError(db, "querying journal mode");
|
||||
SQLiteError::throw_(db, "querying journal mode");
|
||||
prevMode = std::string((const char *) sqlite3_column_text(stmt, 0));
|
||||
}
|
||||
if (prevMode != mode &&
|
||||
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, "setting journal mode");
|
||||
SQLiteError::throw_(db, "setting journal mode");
|
||||
|
||||
/* Increase the auto-checkpoint interval to 40000 pages. This
|
||||
seems enough to ensure that instantiating the NixOS system
|
||||
derivation is done in a single fsync(). */
|
||||
if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, "setting autocheckpoint interval");
|
||||
SQLiteError::throw_(db, "setting autocheckpoint interval");
|
||||
|
||||
/* Initialise the database schema, if necessary. */
|
||||
if (create) {
|
||||
|
@ -8,22 +8,32 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs)
|
||||
SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf)
|
||||
: Error(""), path(path), errNo(errNo), extendedErrNo(extendedErrNo)
|
||||
{
|
||||
err.msg = hintfmt("%s: %s (in '%s')",
|
||||
normaltxt(hf.str()),
|
||||
sqlite3_errstr(extendedErrNo),
|
||||
path ? path : "(in-memory)");
|
||||
}
|
||||
|
||||
[[noreturn]] void SQLiteError::throw_(sqlite3 * db, hintformat && hf)
|
||||
{
|
||||
int err = sqlite3_errcode(db);
|
||||
int exterr = sqlite3_extended_errcode(db);
|
||||
|
||||
auto path = sqlite3_db_filename(db, nullptr);
|
||||
if (!path) path = "(in-memory)";
|
||||
|
||||
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
||||
throw SQLiteBusy(
|
||||
auto exp = SQLiteBusy(path, err, exterr, std::move(hf));
|
||||
exp.err.msg = hintfmt(
|
||||
err == SQLITE_PROTOCOL
|
||||
? fmt("SQLite database '%s' is busy (SQLITE_PROTOCOL)", path)
|
||||
: fmt("SQLite database '%s' is busy", path));
|
||||
}
|
||||
else
|
||||
throw SQLiteError("%s: %s (in '%s')", fs.s, sqlite3_errstr(exterr), path);
|
||||
? "SQLite database '%s' is busy (SQLITE_PROTOCOL)"
|
||||
: "SQLite database '%s' is busy",
|
||||
path ? path : "(in-memory)");
|
||||
throw exp;
|
||||
} else
|
||||
throw SQLiteError(path, err, exterr, std::move(hf));
|
||||
}
|
||||
|
||||
SQLite::SQLite(const Path & path, bool create)
|
||||
@ -37,7 +47,7 @@ SQLite::SQLite(const Path & path, bool create)
|
||||
throw Error("cannot open SQLite database '%s'", path);
|
||||
|
||||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||
throwSQLiteError(db, "setting timeout");
|
||||
SQLiteError::throw_(db, "setting timeout");
|
||||
|
||||
exec("pragma foreign_keys = 1");
|
||||
}
|
||||
@ -46,7 +56,7 @@ SQLite::~SQLite()
|
||||
{
|
||||
try {
|
||||
if (db && sqlite3_close(db) != SQLITE_OK)
|
||||
throwSQLiteError(db, "closing database");
|
||||
SQLiteError::throw_(db, "closing database");
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
@ -62,7 +72,7 @@ void SQLite::exec(const std::string & stmt)
|
||||
{
|
||||
retrySQLite<void>([&]() {
|
||||
if (sqlite3_exec(db, stmt.c_str(), 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, format("executing SQLite statement '%s'") % stmt);
|
||||
SQLiteError::throw_(db, "executing SQLite statement '%s'", stmt);
|
||||
});
|
||||
}
|
||||
|
||||
@ -76,7 +86,7 @@ void SQLiteStmt::create(sqlite3 * db, const std::string & sql)
|
||||
checkInterrupt();
|
||||
assert(!stmt);
|
||||
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, fmt("creating statement '%s'", sql));
|
||||
SQLiteError::throw_(db, "creating statement '%s'", sql);
|
||||
this->db = db;
|
||||
this->sql = sql;
|
||||
}
|
||||
@ -85,7 +95,7 @@ SQLiteStmt::~SQLiteStmt()
|
||||
{
|
||||
try {
|
||||
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
|
||||
throwSQLiteError(db, fmt("finalizing statement '%s'", sql));
|
||||
SQLiteError::throw_(db, "finalizing statement '%s'", sql);
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
@ -109,7 +119,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool not
|
||||
{
|
||||
if (notNull) {
|
||||
if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||
throwSQLiteError(stmt.db, "binding argument");
|
||||
SQLiteError::throw_(stmt.db, "binding argument");
|
||||
} else
|
||||
bind();
|
||||
return *this;
|
||||
@ -119,7 +129,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (const unsigned char * data, size
|
||||
{
|
||||
if (notNull) {
|
||||
if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||
throwSQLiteError(stmt.db, "binding argument");
|
||||
SQLiteError::throw_(stmt.db, "binding argument");
|
||||
} else
|
||||
bind();
|
||||
return *this;
|
||||
@ -129,7 +139,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
|
||||
{
|
||||
if (notNull) {
|
||||
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
||||
throwSQLiteError(stmt.db, "binding argument");
|
||||
SQLiteError::throw_(stmt.db, "binding argument");
|
||||
} else
|
||||
bind();
|
||||
return *this;
|
||||
@ -138,7 +148,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
|
||||
SQLiteStmt::Use & SQLiteStmt::Use::bind()
|
||||
{
|
||||
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
||||
throwSQLiteError(stmt.db, "binding argument");
|
||||
SQLiteError::throw_(stmt.db, "binding argument");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -152,14 +162,14 @@ void SQLiteStmt::Use::exec()
|
||||
int r = step();
|
||||
assert(r != SQLITE_ROW);
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(stmt.db, fmt("executing SQLite statement '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||
SQLiteError::throw_(stmt.db, fmt("executing SQLite statement '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||
}
|
||||
|
||||
bool SQLiteStmt::Use::next()
|
||||
{
|
||||
int r = step();
|
||||
if (r != SQLITE_DONE && r != SQLITE_ROW)
|
||||
throwSQLiteError(stmt.db, fmt("executing SQLite query '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||
SQLiteError::throw_(stmt.db, fmt("executing SQLite query '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||
return r == SQLITE_ROW;
|
||||
}
|
||||
|
||||
@ -185,14 +195,14 @@ SQLiteTxn::SQLiteTxn(sqlite3 * db)
|
||||
{
|
||||
this->db = db;
|
||||
if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, "starting transaction");
|
||||
SQLiteError::throw_(db, "starting transaction");
|
||||
active = true;
|
||||
}
|
||||
|
||||
void SQLiteTxn::commit()
|
||||
{
|
||||
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, "committing transaction");
|
||||
SQLiteError::throw_(db, "committing transaction");
|
||||
active = false;
|
||||
}
|
||||
|
||||
@ -200,7 +210,7 @@ SQLiteTxn::~SQLiteTxn()
|
||||
{
|
||||
try {
|
||||
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, "aborting transaction");
|
||||
SQLiteError::throw_(db, "aborting transaction");
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
|
@ -96,10 +96,30 @@ struct SQLiteTxn
|
||||
};
|
||||
|
||||
|
||||
MakeError(SQLiteError, Error);
|
||||
MakeError(SQLiteBusy, SQLiteError);
|
||||
struct SQLiteError : Error
|
||||
{
|
||||
const char *path;
|
||||
int errNo, extendedErrNo;
|
||||
|
||||
[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs);
|
||||
template<typename... Args>
|
||||
[[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) {
|
||||
throw_(db, hintfmt(fs, args...));
|
||||
}
|
||||
|
||||
SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf);
|
||||
|
||||
protected:
|
||||
|
||||
template<typename... Args>
|
||||
SQLiteError(const char *path, int errNo, int extendedErrNo, const std::string & fs, const Args & ... args)
|
||||
: SQLiteError(path, errNo, extendedErrNo, hintfmt(fs, args...))
|
||||
{ }
|
||||
|
||||
[[noreturn]] static void throw_(sqlite3 * db, hintformat && hf);
|
||||
|
||||
};
|
||||
|
||||
MakeError(SQLiteBusy, SQLiteError);
|
||||
|
||||
void handleSQLiteBusy(const SQLiteBusy & e);
|
||||
|
||||
|
68
src/libutil/chunked-vector.hh
Normal file
68
src/libutil/chunked-vector.hh
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <bits/stdint-uintn.h>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Provides an indexable container like vector<> with memory overhead
|
||||
guarantees like list<> by allocating storage in chunks of ChunkSize
|
||||
elements instead of using a contiguous memory allocation like vector<>
|
||||
does. Not using a single vector that is resized reduces memory overhead
|
||||
on large data sets by on average (growth factor)/2, mostly
|
||||
eliminates copies within the vector during resizing, and provides stable
|
||||
references to its elements. */
|
||||
template<typename T, size_t ChunkSize>
|
||||
class ChunkedVector {
|
||||
private:
|
||||
uint32_t size_ = 0;
|
||||
std::vector<std::vector<T>> chunks;
|
||||
|
||||
/* keep this out of the ::add hot path */
|
||||
[[gnu::noinline]]
|
||||
auto & addChunk()
|
||||
{
|
||||
if (size_ >= std::numeric_limits<uint32_t>::max() - ChunkSize)
|
||||
abort();
|
||||
chunks.emplace_back();
|
||||
chunks.back().reserve(ChunkSize);
|
||||
return chunks.back();
|
||||
}
|
||||
|
||||
public:
|
||||
ChunkedVector(uint32_t reserve)
|
||||
{
|
||||
chunks.reserve(reserve);
|
||||
addChunk();
|
||||
}
|
||||
|
||||
uint32_t size() const { return size_; }
|
||||
|
||||
std::pair<T &, uint32_t> add(T value)
|
||||
{
|
||||
const auto idx = size_++;
|
||||
auto & chunk = [&] () -> auto & {
|
||||
if (auto & back = chunks.back(); back.size() < ChunkSize)
|
||||
return back;
|
||||
return addChunk();
|
||||
}();
|
||||
auto & result = chunk.emplace_back(std::move(value));
|
||||
return {result, idx};
|
||||
}
|
||||
|
||||
const T & operator[](uint32_t idx) const
|
||||
{
|
||||
return chunks[idx / ChunkSize][idx % ChunkSize];
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
void forEach(Fn fn) const
|
||||
{
|
||||
for (const auto & c : chunks)
|
||||
for (const auto & e : c)
|
||||
fn(e);
|
||||
}
|
||||
};
|
||||
}
|
@ -87,11 +87,7 @@ struct ErrPos {
|
||||
origin = pos.origin;
|
||||
line = pos.line;
|
||||
column = pos.column;
|
||||
// is file symbol null?
|
||||
if (pos.file.set())
|
||||
file = pos.file;
|
||||
else
|
||||
file = "";
|
||||
file = pos.file;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include "ansicolor.hh"
|
||||
|
||||
|
||||
@ -155,15 +154,4 @@ inline hintformat hintfmt(std::string plain_string)
|
||||
return hintfmt("%s", normaltxt(plain_string));
|
||||
}
|
||||
|
||||
/* Highlight all the given matches in the given string `s` by wrapping
|
||||
them between `prefix` and `postfix`.
|
||||
|
||||
If some matches overlap, then their union will be wrapped rather
|
||||
than the individual matches. */
|
||||
std::string hiliteMatches(
|
||||
std::string_view s,
|
||||
std::vector<std::smatch> matches,
|
||||
std::string_view prefix,
|
||||
std::string_view postfix);
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
#include "fmt.hh"
|
||||
|
||||
#include <regex>
|
||||
#include "hilite.hh"
|
||||
|
||||
namespace nix {
|
||||
|
20
src/libutil/hilite.hh
Normal file
20
src/libutil/hilite.hh
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Highlight all the given matches in the given string `s` by wrapping
|
||||
them between `prefix` and `postfix`.
|
||||
|
||||
If some matches overlap, then their union will be wrapped rather
|
||||
than the individual matches. */
|
||||
std::string hiliteMatches(
|
||||
std::string_view s,
|
||||
std::vector<std::smatch> matches,
|
||||
std::string_view prefix,
|
||||
std::string_view postfix);
|
||||
|
||||
}
|
@ -99,47 +99,4 @@ make_ref(Args&&... args)
|
||||
return ref<T>(p);
|
||||
}
|
||||
|
||||
|
||||
/* A non-nullable pointer.
|
||||
This is similar to a C++ "& reference", but mutable.
|
||||
This is similar to ref<T> but backed by a regular pointer instead of a smart pointer.
|
||||
*/
|
||||
template<typename T>
|
||||
class ptr {
|
||||
private:
|
||||
T * p;
|
||||
|
||||
public:
|
||||
ptr<T>(const ptr<T> & r)
|
||||
: p(r.p)
|
||||
{ }
|
||||
|
||||
explicit ptr<T>(T * p)
|
||||
: p(p)
|
||||
{
|
||||
if (!p)
|
||||
throw std::invalid_argument("null pointer cast to ptr");
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
{
|
||||
return &*p;
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
{
|
||||
return *p;
|
||||
}
|
||||
|
||||
bool operator == (const ptr<T> & other) const
|
||||
{
|
||||
return p == other.p;
|
||||
}
|
||||
|
||||
bool operator != (const ptr<T> & other) const
|
||||
{
|
||||
return p != other.p;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
54
src/libutil/tests/chunked-vector.cc
Normal file
54
src/libutil/tests/chunked-vector.cc
Normal file
@ -0,0 +1,54 @@
|
||||
#include "chunked-vector.hh"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace nix {
|
||||
TEST(ChunkedVector, InitEmpty) {
|
||||
auto v = ChunkedVector<int, 2>(100);
|
||||
ASSERT_EQ(v.size(), 0);
|
||||
}
|
||||
|
||||
TEST(ChunkedVector, GrowsCorrectly) {
|
||||
auto v = ChunkedVector<int, 2>(100);
|
||||
for (auto i = 1; i < 20; i++) {
|
||||
v.add(i);
|
||||
ASSERT_EQ(v.size(), i);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ChunkedVector, AddAndGet) {
|
||||
auto v = ChunkedVector<int, 2>(100);
|
||||
for (auto i = 1; i < 20; i++) {
|
||||
auto [i2, idx] = v.add(i);
|
||||
auto & i3 = v[idx];
|
||||
ASSERT_EQ(i, i2);
|
||||
ASSERT_EQ(&i2, &i3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ChunkedVector, ForEach) {
|
||||
auto v = ChunkedVector<int, 2>(100);
|
||||
for (auto i = 1; i < 20; i++) {
|
||||
v.add(i);
|
||||
}
|
||||
int count = 0;
|
||||
v.forEach([&count](int elt) {
|
||||
count++;
|
||||
});
|
||||
ASSERT_EQ(count, v.size());
|
||||
}
|
||||
|
||||
TEST(ChunkedVector, OverflowOK) {
|
||||
// Similar to the AddAndGet, but intentionnally use a small
|
||||
// initial ChunkedVector to force it to overflow
|
||||
auto v = ChunkedVector<int, 2>(2);
|
||||
for (auto i = 1; i < 20; i++) {
|
||||
auto [i2, idx] = v.add(i);
|
||||
auto & i3 = v[idx];
|
||||
ASSERT_EQ(i, i2);
|
||||
ASSERT_EQ(&i2, &i3);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
#include "fmt.hh"
|
||||
#include "hilite.hh"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
/* ----------- tests for fmt.hh -------------------------------------------------*/
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
@ -1082,7 +1082,7 @@ std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||
auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input});
|
||||
|
||||
if (!statusOk(res.first))
|
||||
throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
|
||||
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
|
||||
|
||||
return res.second;
|
||||
}
|
||||
@ -1210,7 +1210,7 @@ void runProgram2(const RunOptions & options)
|
||||
if (source) promise.get_future().get();
|
||||
|
||||
if (status)
|
||||
throw ExecError(status, fmt("program '%1%' %2%", options.program, statusToString(status)));
|
||||
throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1241,7 +1241,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||
Attr & a(*attrs.find(i.name));
|
||||
if(a.value->type() != nString) continue;
|
||||
XMLAttrs attrs3;
|
||||
attrs3["type"] = i.name;
|
||||
attrs3["type"] = globals.state->symbols[i.name];
|
||||
attrs3["value"] = a.value->string.s;
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
the store; we need it for future modifications of the
|
||||
environment. */
|
||||
std::ostringstream str;
|
||||
manifest.print(str, true);
|
||||
manifest.print(state.symbols, str, true);
|
||||
auto manifestFile = state.store->addTextToStore("env-manifest.nix",
|
||||
str.str(), references);
|
||||
|
||||
@ -134,9 +134,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
|
||||
PathSet context;
|
||||
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
|
||||
auto topLevelDrv = state.coerceToStorePath(*aDrvPath.pos, *aDrvPath.value, context);
|
||||
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context);
|
||||
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
|
||||
auto topLevelOut = state.coerceToStorePath(*aOutPath.pos, *aOutPath.value, context);
|
||||
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context);
|
||||
|
||||
/* Realise the resulting store expression. */
|
||||
debug("building user environment");
|
||||
|
@ -31,7 +31,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
bool evalOnly, OutputKind output, bool location, Expr * e)
|
||||
{
|
||||
if (parseOnly) {
|
||||
std::cout << format("%1%\n") % *e;
|
||||
e->show(state.symbols, std::cout);
|
||||
std::cout << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,7 +56,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
|
||||
else {
|
||||
if (strict) state.forceValueDeep(vRes);
|
||||
std::cout << vRes << std::endl;
|
||||
vRes.print(state.symbols, std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
} else {
|
||||
DrvInfos drvs;
|
||||
|
@ -85,9 +85,9 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
||||
|
||||
else if (type == "derivation") {
|
||||
auto drvPath = cursor->forceDerivation();
|
||||
auto outPath = cursor->getAttr(state.sOutPath)->getString();
|
||||
auto outputName = cursor->getAttr(state.sOutputName)->getString();
|
||||
auto name = cursor->getAttr(state.sName)->getString();
|
||||
auto outPath = cursor->getAttr("outPath")->getString();
|
||||
auto outputName = cursor->getAttr("outputName")->getString();
|
||||
auto name = cursor->getAttr("name")->getString();
|
||||
auto aPname = cursor->maybeGetAttr("pname");
|
||||
auto aMeta = cursor->maybeGetAttr("meta");
|
||||
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
|
||||
|
@ -97,21 +97,23 @@ struct CmdBundle : InstallableCommand
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
|
||||
PathSet context2;
|
||||
auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2);
|
||||
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2);
|
||||
|
||||
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
||||
if (!attr2)
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
|
||||
auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2);
|
||||
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2);
|
||||
|
||||
store->buildPaths({ DerivedPath::Built { drvPath } });
|
||||
|
||||
auto outPathS = store->printStorePath(outPath);
|
||||
|
||||
if (!outLink) {
|
||||
auto &attr = vRes->attrs->need(evalState->sName);
|
||||
outLink = evalState->forceStringNoCtx(*attr.value,*attr.pos);
|
||||
auto * attr = vRes->attrs->get(evalState->sName);
|
||||
if (!attr)
|
||||
throw Error("attribute 'name' missing");
|
||||
outLink = evalState->forceStringNoCtx(*attr->value, attr->pos);
|
||||
}
|
||||
|
||||
// TODO: will crash if not a localFSStore?
|
||||
|
@ -28,19 +28,19 @@ struct CmdEdit : InstallableCommand
|
||||
{
|
||||
auto state = getEvalState();
|
||||
|
||||
auto [v, pos] = installable->toValue(*state);
|
||||
const auto [file, line] = [&] {
|
||||
auto [v, pos] = installable->toValue(*state);
|
||||
|
||||
try {
|
||||
pos = findPackageFilename(*state, *v, installable->what());
|
||||
} catch (NoPositionInfo &) {
|
||||
}
|
||||
|
||||
if (pos == noPos)
|
||||
throw Error("cannot find position information for '%s", installable->what());
|
||||
try {
|
||||
return findPackageFilename(*state, *v, installable->what());
|
||||
} catch (NoPositionInfo &) {
|
||||
throw Error("cannot find position information for '%s", installable->what());
|
||||
}
|
||||
}();
|
||||
|
||||
stopProgressBar();
|
||||
|
||||
auto args = editorFor(pos);
|
||||
auto args = editorFor(file, line);
|
||||
|
||||
restoreProcessContext();
|
||||
|
||||
|
@ -77,9 +77,9 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||
if (pathExists(*writeTo))
|
||||
throw Error("path '%s' already exists", *writeTo);
|
||||
|
||||
std::function<void(Value & v, const Pos & pos, const Path & path)> recurse;
|
||||
std::function<void(Value & v, const PosIdx pos, const Path & path)> recurse;
|
||||
|
||||
recurse = [&](Value & v, const Pos & pos, const Path & path)
|
||||
recurse = [&](Value & v, const PosIdx pos, const Path & path)
|
||||
{
|
||||
state->forceValue(v, pos);
|
||||
if (v.type() == nString)
|
||||
@ -88,18 +88,22 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||
else if (v.type() == nAttrs) {
|
||||
if (mkdir(path.c_str(), 0777) == -1)
|
||||
throw SysError("creating directory '%s'", path);
|
||||
for (auto & attr : *v.attrs)
|
||||
for (auto & attr : *v.attrs) {
|
||||
std::string_view name = state->symbols[attr.name];
|
||||
try {
|
||||
if (attr.name == "." || attr.name == "..")
|
||||
throw Error("invalid file name '%s'", attr.name);
|
||||
recurse(*attr.value, *attr.pos, path + "/" + std::string(attr.name));
|
||||
if (name == "." || name == "..")
|
||||
throw Error("invalid file name '%s'", name);
|
||||
recurse(*attr.value, attr.pos, concatStrings(path, "/", name));
|
||||
} catch (Error & e) {
|
||||
e.addTrace(*attr.pos, hintfmt("while evaluating the attribute '%s'", attr.name));
|
||||
e.addTrace(
|
||||
state->positions[attr.pos],
|
||||
hintfmt("while evaluating the attribute '%s'", name));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
throw TypeError("value at '%s' is not a string or an attribute set", pos);
|
||||
throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]);
|
||||
};
|
||||
|
||||
recurse(*v, pos, *writeTo);
|
||||
@ -117,7 +121,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||
|
||||
else {
|
||||
state->forceValueDeep(*v);
|
||||
logger->cout("%s", *v);
|
||||
logger->cout("%s", printValue(*state, *v));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
178
src/nix/flake.cc
178
src/nix/flake.cc
@ -123,7 +123,7 @@ struct CmdFlakeLock : FlakeCommand
|
||||
};
|
||||
|
||||
static void enumerateOutputs(EvalState & state, Value & vFlake,
|
||||
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
|
||||
std::function<void(const std::string & name, Value & vProvide, const PosIdx pos)> callback)
|
||||
{
|
||||
auto pos = vFlake.determinePos(noPos);
|
||||
state.forceAttrs(vFlake, pos);
|
||||
@ -139,11 +139,11 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
|
||||
else. This way we can disable IFD for hydraJobs and then enable
|
||||
it for other outputs. */
|
||||
if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
|
||||
callback(attr->name, *attr->value, *attr->pos);
|
||||
callback(state.symbols[attr->name], *attr->value, attr->pos);
|
||||
|
||||
for (auto & attr : *aOutputs->value->attrs) {
|
||||
if (attr.name != sHydraJobs)
|
||||
callback(attr.name, *attr.value, *attr.pos);
|
||||
callback(state.symbols[attr.name], *attr.value, attr.pos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,14 +254,6 @@ struct CmdFlakeInfo : CmdFlakeMetadata
|
||||
}
|
||||
};
|
||||
|
||||
static bool argHasName(std::string_view arg, std::string_view expected)
|
||||
{
|
||||
return
|
||||
arg == expected
|
||||
|| arg == "_"
|
||||
|| (hasPrefix(arg, "_") && arg.substr(1) == expected);
|
||||
}
|
||||
|
||||
struct CmdFlakeCheck : FlakeCommand
|
||||
{
|
||||
bool build = true;
|
||||
@ -315,13 +307,25 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
|
||||
// FIXME: rewrite to use EvalCache.
|
||||
|
||||
auto checkSystemName = [&](const std::string & system, const Pos & pos) {
|
||||
// FIXME: what's the format of "system"?
|
||||
if (system.find('-') == std::string::npos)
|
||||
reportError(Error("'%s' is not a valid system type, at %s", system, pos));
|
||||
auto resolve = [&] (PosIdx p) {
|
||||
return state->positions[p];
|
||||
};
|
||||
|
||||
auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) -> std::optional<StorePath> {
|
||||
auto argHasName = [&] (SymbolIdx arg, std::string_view expected) {
|
||||
std::string_view name = state->symbols[arg];
|
||||
return
|
||||
name == expected
|
||||
|| name == "_"
|
||||
|| (hasPrefix(name, "_") && name.substr(1) == expected);
|
||||
};
|
||||
|
||||
auto checkSystemName = [&](const std::string & system, const PosIdx pos) {
|
||||
// FIXME: what's the format of "system"?
|
||||
if (system.find('-') == std::string::npos)
|
||||
reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos)));
|
||||
};
|
||||
|
||||
auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional<StorePath> {
|
||||
try {
|
||||
auto drvInfo = getDerivation(*state, v, false);
|
||||
if (!drvInfo)
|
||||
@ -329,7 +333,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
// FIXME: check meta attributes
|
||||
return drvInfo->queryDrvPath();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the derivation '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
return std::nullopt;
|
||||
@ -337,7 +341,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
|
||||
std::vector<DerivedPath> drvPaths;
|
||||
|
||||
auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||
auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||
try {
|
||||
#if 0
|
||||
// FIXME
|
||||
@ -348,12 +352,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
}
|
||||
#endif
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the app definition '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the app definition '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
};
|
||||
|
||||
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||
auto checkOverlay = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||
try {
|
||||
state->forceValue(v, pos);
|
||||
if (!v.isLambda()
|
||||
@ -368,12 +372,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||
// evaluate the overlay.
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the overlay '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the overlay '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
};
|
||||
|
||||
auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||
auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||
try {
|
||||
state->forceValue(v, pos);
|
||||
if (v.isLambda()) {
|
||||
@ -382,9 +386,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
} else if (v.type() == nAttrs) {
|
||||
for (auto & attr : *v.attrs)
|
||||
try {
|
||||
state->forceValue(*attr.value, *attr.pos);
|
||||
state->forceValue(*attr.value, attr.pos);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(*attr.pos, hintfmt("while evaluating the option '%s'", attr.name));
|
||||
e.addTrace(
|
||||
state->positions[attr.pos],
|
||||
hintfmt("while evaluating the option '%s'", state->symbols[attr.name]));
|
||||
throw;
|
||||
}
|
||||
} else
|
||||
@ -392,14 +398,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||
// check the module.
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the NixOS module '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the NixOS module '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
};
|
||||
|
||||
std::function<void(const std::string & attrPath, Value & v, const Pos & pos)> checkHydraJobs;
|
||||
std::function<void(const std::string & attrPath, Value & v, const PosIdx pos)> checkHydraJobs;
|
||||
|
||||
checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||
checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||
try {
|
||||
state->forceAttrs(v, pos);
|
||||
|
||||
@ -407,23 +413,23 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
throw Error("jobset should not be a derivation at top-level");
|
||||
|
||||
for (auto & attr : *v.attrs) {
|
||||
state->forceAttrs(*attr.value, *attr.pos);
|
||||
auto attrPath2 = attrPath + "." + (std::string) attr.name;
|
||||
state->forceAttrs(*attr.value, attr.pos);
|
||||
auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]);
|
||||
if (state->isDerivation(*attr.value)) {
|
||||
Activity act(*logger, lvlChatty, actUnknown,
|
||||
fmt("checking Hydra job '%s'", attrPath2));
|
||||
checkDerivation(attrPath2, *attr.value, *attr.pos);
|
||||
checkDerivation(attrPath2, *attr.value, attr.pos);
|
||||
} else
|
||||
checkHydraJobs(attrPath2, *attr.value, *attr.pos);
|
||||
checkHydraJobs(attrPath2, *attr.value, attr.pos);
|
||||
}
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the Hydra jobset '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the Hydra jobset '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
};
|
||||
|
||||
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||
try {
|
||||
Activity act(*logger, lvlChatty, actUnknown,
|
||||
fmt("checking NixOS configuration '%s'", attrPath));
|
||||
@ -433,12 +439,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
if (!state->isDerivation(*vToplevel))
|
||||
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the NixOS configuration '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the NixOS configuration '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
};
|
||||
|
||||
auto checkTemplate = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||
auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||
try {
|
||||
Activity act(*logger, lvlChatty, actUnknown,
|
||||
fmt("checking template '%s'", attrPath));
|
||||
@ -448,7 +454,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
if (auto attr = v.attrs->get(state->symbols.create("path"))) {
|
||||
if (attr->name == state->symbols.create("path")) {
|
||||
PathSet context;
|
||||
auto path = state->coerceToPath(*attr->pos, *attr->value, context);
|
||||
auto path = state->coerceToPath(attr->pos, *attr->value, context);
|
||||
if (!store->isInStore(path))
|
||||
throw Error("template '%s' has a bad 'path' attribute");
|
||||
// TODO: recursively check the flake in 'path'.
|
||||
@ -457,29 +463,29 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
||||
|
||||
if (auto attr = v.attrs->get(state->symbols.create("description")))
|
||||
state->forceStringNoCtx(*attr->value, *attr->pos);
|
||||
state->forceStringNoCtx(*attr->value, attr->pos);
|
||||
else
|
||||
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
||||
|
||||
for (auto & attr : *v.attrs) {
|
||||
std::string name(attr.name);
|
||||
std::string_view name(state->symbols[attr.name]);
|
||||
if (name != "path" && name != "description" && name != "welcomeText")
|
||||
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
};
|
||||
|
||||
auto checkBundler = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||
auto checkBundler = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||
try {
|
||||
state->forceValue(v, pos);
|
||||
if (!v.isLambda())
|
||||
throw Error("bundler must be a function");
|
||||
// TODO: check types of inputs/outputs?
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
|
||||
reportError(e);
|
||||
}
|
||||
};
|
||||
@ -492,7 +498,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
|
||||
enumerateOutputs(*state,
|
||||
*vFlake,
|
||||
[&](const std::string & name, Value & vOutput, const Pos & pos) {
|
||||
[&](const std::string & name, Value & vOutput, const PosIdx pos) {
|
||||
Activity act(*logger, lvlChatty, actUnknown,
|
||||
fmt("checking flake output '%s'", name));
|
||||
|
||||
@ -516,13 +522,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
if (name == "checks") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
state->forceAttrs(*attr.value, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos);
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
auto drvPath = checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
||||
*attr2.value, *attr2.pos);
|
||||
if (drvPath && (std::string) attr.name == settings.thisSystem.get())
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
if (drvPath && attr_name == settings.thisSystem.get())
|
||||
drvPaths.push_back(DerivedPath::Built{*drvPath});
|
||||
}
|
||||
}
|
||||
@ -531,61 +538,66 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "formatter") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "packages" || name == "devShells") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
state->forceAttrs(*attr.value, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos);
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
||||
*attr2.value, *attr2.pos);
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "apps") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
state->forceAttrs(*attr.value, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos);
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
checkApp(
|
||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
||||
*attr2.value, *attr2.pos);
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "defaultPackage" || name == "devShell") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkDerivation(
|
||||
fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "defaultApp") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "legacyPackages") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
checkSystemName(state->symbols[attr.name], attr.pos);
|
||||
// FIXME: do getDerivations?
|
||||
}
|
||||
}
|
||||
@ -596,8 +608,8 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "overlays") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkOverlay(fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
|
||||
else if (name == "nixosModule")
|
||||
@ -606,15 +618,15 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "nixosModules") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkModule(fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
checkModule(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
|
||||
else if (name == "nixosConfigurations") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkNixOSConfiguration(fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
|
||||
else if (name == "hydraJobs")
|
||||
@ -626,29 +638,31 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "templates") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkTemplate(fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
|
||||
else if (name == "defaultBundler") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkBundler(
|
||||
fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "bundlers") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
state->forceAttrs(*attr.value, *attr.pos);
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos);
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
checkBundler(
|
||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
||||
*attr2.value, *attr2.pos);
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -657,7 +671,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
warn("unknown flake output '%s'", name);
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, hintfmt("while checking flake output '%s'", name));
|
||||
e.addTrace(resolve(pos), hintfmt("while checking flake output '%s'", name));
|
||||
reportError(e);
|
||||
}
|
||||
});
|
||||
@ -994,7 +1008,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||
|
||||
auto showDerivation = [&]()
|
||||
{
|
||||
auto name = visitor.getAttr(state->sName)->getString();
|
||||
auto name = visitor.getAttr("name")->getString();
|
||||
if (json) {
|
||||
std::optional<std::string> description;
|
||||
if (auto aMeta = visitor.maybeGetAttr("meta")) {
|
||||
|
@ -302,7 +302,7 @@ void mainWrapped(int argc, char * * argv)
|
||||
b["arity"] = primOp->arity;
|
||||
b["args"] = primOp->args;
|
||||
b["doc"] = trim(stripIndentation(primOp->doc));
|
||||
res[(std::string) builtin.name] = std::move(b);
|
||||
res[state.symbols[builtin.name]] = std::move(b);
|
||||
}
|
||||
std::cout << res.dump() << "\n";
|
||||
return;
|
||||
|
@ -199,11 +199,13 @@ static int main_nix_prefetch_url(int argc, char * * argv)
|
||||
state->forceAttrs(v, noPos);
|
||||
|
||||
/* Extract the URL. */
|
||||
auto & attr = v.attrs->need(state->symbols.create("urls"));
|
||||
state->forceList(*attr.value, noPos);
|
||||
if (attr.value->listSize() < 1)
|
||||
auto * attr = v.attrs->get(state->symbols.create("urls"));
|
||||
if (!attr)
|
||||
throw Error("attribute 'urls' missing");
|
||||
state->forceList(*attr->value, noPos);
|
||||
if (attr->value->listSize() < 1)
|
||||
throw Error("'urls' list is empty");
|
||||
url = state->forceString(*attr.value->listElems()[0]);
|
||||
url = state->forceString(*attr->value->listElems()[0]);
|
||||
|
||||
/* Extract the hash mode. */
|
||||
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
|
||||
|
@ -73,7 +73,7 @@ struct NixRepl
|
||||
void initEnv();
|
||||
void reloadFiles();
|
||||
void addAttrsToScope(Value & attrs);
|
||||
void addVarToScope(const Symbol & name, Value & v);
|
||||
void addVarToScope(const SymbolIdx name, Value & v);
|
||||
Expr * parseString(std::string s);
|
||||
void evalString(std::string s, Value & v);
|
||||
|
||||
@ -120,7 +120,7 @@ std::string runNix(Path program, const Strings & args,
|
||||
});
|
||||
|
||||
if (!statusOk(res.first))
|
||||
throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
|
||||
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
|
||||
|
||||
return res.second;
|
||||
}
|
||||
@ -347,9 +347,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
state->forceAttrs(v, noPos);
|
||||
|
||||
for (auto & i : *v.attrs) {
|
||||
std::string name = i.name;
|
||||
std::string_view name = state->symbols[i.name];
|
||||
if (name.substr(0, cur2.size()) != cur2) continue;
|
||||
completions.insert(prev + expr + "." + name);
|
||||
completions.insert(concatStrings(prev, expr, ".", name));
|
||||
}
|
||||
|
||||
} catch (ParseError & e) {
|
||||
@ -461,21 +461,23 @@ bool NixRepl::processLine(std::string line)
|
||||
Value v;
|
||||
evalString(arg, v);
|
||||
|
||||
Pos pos;
|
||||
|
||||
if (v.type() == nPath || v.type() == nString) {
|
||||
PathSet context;
|
||||
auto filename = state->coerceToString(noPos, v, context);
|
||||
pos.file = state->symbols.create(*filename);
|
||||
} else if (v.isLambda()) {
|
||||
pos = v.lambda.fun->pos;
|
||||
} else {
|
||||
// assume it's a derivation
|
||||
pos = findPackageFilename(*state, v, arg);
|
||||
}
|
||||
const auto [file, line] = [&] () -> std::pair<std::string, uint32_t> {
|
||||
if (v.type() == nPath || v.type() == nString) {
|
||||
PathSet context;
|
||||
auto filename = state->coerceToString(noPos, v, context).toOwned();
|
||||
state->symbols.create(filename);
|
||||
return {filename, 0};
|
||||
} else if (v.isLambda()) {
|
||||
auto pos = state->positions[v.lambda.fun->pos];
|
||||
return {pos.file, pos.line};
|
||||
} else {
|
||||
// assume it's a derivation
|
||||
return findPackageFilename(*state, v, arg);
|
||||
}
|
||||
}();
|
||||
|
||||
// Open in EDITOR
|
||||
auto args = editorFor(pos);
|
||||
auto args = editorFor(file, line);
|
||||
auto editor = args.front();
|
||||
args.pop_front();
|
||||
|
||||
@ -498,7 +500,7 @@ bool NixRepl::processLine(std::string line)
|
||||
Value v, f, result;
|
||||
evalString(arg, v);
|
||||
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
|
||||
state->callFunction(f, v, result, Pos());
|
||||
state->callFunction(f, v, result, PosIdx());
|
||||
|
||||
StorePath drvPath = getDerivationPath(result);
|
||||
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
||||
@ -671,7 +673,7 @@ void NixRepl::initEnv()
|
||||
|
||||
varNames.clear();
|
||||
for (auto & i : state->staticBaseEnv.vars)
|
||||
varNames.insert(i.first);
|
||||
varNames.emplace(state->symbols[i.first]);
|
||||
}
|
||||
|
||||
|
||||
@ -701,7 +703,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||
for (auto & i : *attrs.attrs) {
|
||||
staticEnv.vars.emplace_back(i.name, displ);
|
||||
env->values[displ++] = i.value;
|
||||
varNames.insert((std::string) i.name);
|
||||
varNames.emplace(state->symbols[i.name]);
|
||||
}
|
||||
staticEnv.sort();
|
||||
staticEnv.deduplicate();
|
||||
@ -709,7 +711,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
||||
void NixRepl::addVarToScope(const SymbolIdx name, Value & v)
|
||||
{
|
||||
if (displ >= envSize)
|
||||
throw Error("environment full; cannot add more variables");
|
||||
@ -718,7 +720,7 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
||||
staticEnv.vars.emplace_back(name, displ);
|
||||
staticEnv.sort();
|
||||
env->values[displ++] = &v;
|
||||
varNames.insert((std::string) name);
|
||||
varNames.emplace(state->symbols[name]);
|
||||
}
|
||||
|
||||
|
||||
@ -799,7 +801,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
||||
PathSet context;
|
||||
if (i != v.attrs->end())
|
||||
str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context));
|
||||
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context));
|
||||
else
|
||||
str << "???";
|
||||
str << "»";
|
||||
@ -811,7 +813,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||
typedef std::map<std::string, Value *> Sorted;
|
||||
Sorted sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted[i.name] = i.value;
|
||||
sorted.emplace(state->symbols[i.name], i.value);
|
||||
|
||||
for (auto & i : sorted) {
|
||||
if (isVarName(i.first))
|
||||
@ -861,7 +863,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||
case nFunction:
|
||||
if (v.isLambda()) {
|
||||
std::ostringstream s;
|
||||
s << v.lambda.fun->pos;
|
||||
s << state->positions[v.lambda.fun->pos];
|
||||
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
||||
} else if (v.isPrimOp()) {
|
||||
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "shared.hh"
|
||||
#include "eval-cache.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "fmt.hh"
|
||||
#include "hilite.hh"
|
||||
|
||||
#include <regex>
|
||||
#include <fstream>
|
||||
@ -154,7 +154,7 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||
recurse();
|
||||
|
||||
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
|
||||
auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations);
|
||||
auto attr = cursor.maybeGetAttr("recurseForDerivations");
|
||||
if (attr && attr->getBool())
|
||||
recurse();
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ source common.sh
|
||||
|
||||
file=build-hook-ca-floating.nix
|
||||
|
||||
enableFeatures "ca-derivations ca-references"
|
||||
enableFeatures "ca-derivations"
|
||||
|
||||
CONTENT_ADDRESSED=true
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
source ../common.sh
|
||||
|
||||
enableFeatures "ca-derivations ca-references"
|
||||
enableFeatures "ca-derivations"
|
||||
|
||||
restartDaemon
|
||||
|
11
tests/ca/selfref-gc.sh
Executable file
11
tests/ca/selfref-gc.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source common.sh
|
||||
|
||||
requireDaemonNewerThan "2.4pre20210626"
|
||||
|
||||
enableFeatures "ca-derivations nix-command flakes"
|
||||
|
||||
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||
cd ..
|
||||
source ./selfref-gc.sh
|
@ -20,6 +20,8 @@ nix eval --expr 'assert 1 + 2 == 3; true'
|
||||
[[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]]
|
||||
[[ $(nix eval int -f - < "./eval.nix") == 123 ]]
|
||||
|
||||
# Check if toFile can be utilized during restricted eval
|
||||
[[ $(nix eval --restrict-eval --expr 'import (builtins.toFile "source" "42")') == 42 ]]
|
||||
|
||||
nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
|
||||
[[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]]
|
||||
|
@ -92,6 +92,7 @@ nix_tests = \
|
||||
plugins.sh \
|
||||
build.sh \
|
||||
ca/nix-run.sh \
|
||||
selfref-gc.sh ca/selfref-gc.sh \
|
||||
db-migration.sh \
|
||||
bash-profile.sh \
|
||||
pass-as-file.sh \
|
||||
|
@ -3,7 +3,7 @@ source common.sh
|
||||
clearStore
|
||||
clearProfiles
|
||||
|
||||
enableFeatures "ca-derivations ca-references"
|
||||
enableFeatures "ca-derivations"
|
||||
restartDaemon
|
||||
|
||||
# Make a flake.
|
||||
|
@ -13,7 +13,7 @@ MySettings mySettings;
|
||||
|
||||
static GlobalConfig::Register rs(&mySettings);
|
||||
|
||||
static void prim_anotherNull (EvalState & state, const Pos & pos, Value ** args, Value & v)
|
||||
static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
if (mySettings.settingSet)
|
||||
v.mkNull();
|
||||
|
30
tests/selfref-gc.sh
Normal file
30
tests/selfref-gc.sh
Normal file
@ -0,0 +1,30 @@
|
||||
source common.sh
|
||||
|
||||
requireDaemonNewerThan "2.6.0pre20211215"
|
||||
|
||||
clearStore
|
||||
|
||||
nix-build --no-out-link -E '
|
||||
with import ./config.nix;
|
||||
|
||||
let d1 = mkDerivation {
|
||||
name = "selfref-gc";
|
||||
outputs = [ "out" ];
|
||||
buildCommand = "
|
||||
echo SELF_REF: $out > $out
|
||||
";
|
||||
}; in
|
||||
|
||||
# the only change from d1 is d1 as an (unused) build input
|
||||
# to get identical store path in CA.
|
||||
mkDerivation {
|
||||
name = "selfref-gc";
|
||||
outputs = [ "out" ];
|
||||
buildCommand = "
|
||||
echo UNUSED: ${d1}
|
||||
echo SELF_REF: $out > $out
|
||||
";
|
||||
}
|
||||
'
|
||||
|
||||
nix-collect-garbage
|
Loading…
Reference in New Issue
Block a user