aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/attr-set.hh1
-rw-r--r--src/libexpr/eval-cache.cc4
-rw-r--r--src/libexpr/eval-cache.hh2
-rw-r--r--src/libexpr/eval.cc20
-rw-r--r--src/libexpr/flake/config.cc4
-rw-r--r--src/libexpr/flake/flake.hh2
-rw-r--r--src/libexpr/get-drvs.cc3
-rw-r--r--src/libexpr/local.mk2
-rw-r--r--src/libexpr/nixexpr.hh4
-rw-r--r--src/libexpr/parser.y2
-rw-r--r--src/libexpr/primops.cc212
-rw-r--r--src/libexpr/primops/fetchTree.cc12
-rw-r--r--src/libexpr/value.hh2
13 files changed, 194 insertions, 76 deletions
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index 6d68e5df3..1da8d91df 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -35,6 +35,7 @@ class Bindings
{
public:
typedef uint32_t size_t;
+ Pos *pos;
private:
size_t size_, capacity_;
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 98d91c905..d7e21783d 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -486,11 +486,11 @@ std::shared_ptr<AttrCursor> AttrCursor::getAttr(std::string_view name)
return getAttr(root->state.symbols.create(name));
}
-std::shared_ptr<AttrCursor> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath)
+std::shared_ptr<AttrCursor> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
{
auto res = shared_from_this();
for (auto & attr : attrPath) {
- res = res->maybeGetAttr(attr);
+ res = res->maybeGetAttr(attr, force);
if (!res) return {};
}
return res;
diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh
index e23e45c94..43b34ebcb 100644
--- a/src/libexpr/eval-cache.hh
+++ b/src/libexpr/eval-cache.hh
@@ -102,7 +102,7 @@ public:
std::shared_ptr<AttrCursor> getAttr(std::string_view name);
- std::shared_ptr<AttrCursor> findAlongAttrPath(const std::vector<Symbol> & attrPath);
+ std::shared_ptr<AttrCursor> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
std::string getString();
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 3afe2e47b..ef9f8efca 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -201,6 +201,15 @@ string showType(const Value & v)
}
}
+Pos Value::determinePos(const Pos &pos) const
+{
+ switch (internalType) {
+ case tAttrs: return *attrs->pos;
+ case tLambda: return lambda.fun->pos;
+ case tApp: return app.left->determinePos(pos);
+ default: return pos;
+ }
+}
bool Value::isTrivial() const
{
@@ -1060,6 +1069,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), &i.pos));
v.attrs->sort(); // FIXME: inefficient
}
+
+ v.attrs->pos = &pos;
}
@@ -2091,9 +2102,12 @@ Strings EvalSettings::getDefaultNixPath()
}
};
- add(getHome() + "/.nix-defexpr/channels");
- add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
- add(settings.nixStateDir + "/profiles/per-user/root/channels");
+ if (!evalSettings.restrictEval && !evalSettings.pureEval) {
+ add(getHome() + "/.nix-defexpr/channels");
+ add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
+ add(settings.nixStateDir + "/profiles/per-user/root/channels");
+ }
+
return res;
}
diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc
index 63566131e..c8a5a319f 100644
--- a/src/libexpr/flake/config.cc
+++ b/src/libexpr/flake/config.cc
@@ -22,7 +22,9 @@ static TrustedList readTrustedList()
static void writeTrustedList(const TrustedList & trustedList)
{
- writeFile(trustedListPath(), nlohmann::json(trustedList).dump());
+ auto path = trustedListPath();
+ createDirs(dirOf(path));
+ writeFile(path, nlohmann::json(trustedList).dump());
}
void ConfigFile::apply()
diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh
index 65ed1ad0a..d17d5e183 100644
--- a/src/libexpr/flake/flake.hh
+++ b/src/libexpr/flake/flake.hh
@@ -113,7 +113,7 @@ struct LockFlags
/* Whether to commit changes to flake.lock. */
bool commitLockFile = false;
- /* Flake inputs to be overriden. */
+ /* Flake inputs to be overridden. */
std::map<InputPath, FlakeRef> inputOverrides;
/* Flake inputs to be updated. This means that any existing lock
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 1a3990ea1..f774e6493 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -2,6 +2,7 @@
#include "util.hh"
#include "eval-inline.hh"
#include "store-api.hh"
+#include "path-with-outputs.hh"
#include <cstring>
#include <regex>
@@ -19,7 +20,7 @@ DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs)
: state(&state), attrs(nullptr), attrPath("")
{
- auto [drvPath, selectedOutputs] = store->parsePathWithOutputs(drvPathWithOutputs);
+ auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
this->drvPath = store->printStorePath(drvPath);
diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk
index 26c53d301..c40abfb78 100644
--- a/src/libexpr/local.mk
+++ b/src/libexpr/local.mk
@@ -16,7 +16,7 @@ libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/lib
libexpr_LIBS = libutil libstore libfetchers
libexpr_LDFLAGS = -lboost_context
-ifneq ($(OS), FreeBSD)
+ifeq ($(OS), Linux)
libexpr_LDFLAGS += -ldl
endif
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 8df8055b3..51a14cd59 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -180,6 +180,7 @@ struct ExprOpHasAttr : Expr
struct ExprAttrs : Expr
{
bool recursive;
+ Pos pos;
struct AttrDef {
bool inherited;
Expr * e;
@@ -199,7 +200,8 @@ struct ExprAttrs : Expr
};
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs;
- ExprAttrs() : recursive(false) { };
+ ExprAttrs(const Pos &pos) : recursive(false), pos(pos) { };
+ ExprAttrs() : recursive(false), pos(noPos) { };
COMMON_METHODS
};
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 49d995bb9..f948dde47 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -478,7 +478,7 @@ binds
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data));
}
}
- | { $$ = new ExprAttrs; }
+ | { $$ = new ExprAttrs(makeCurPos(@0, data)); }
;
attrs
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 1d1afa768..e8569b654 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -21,6 +21,8 @@
#include <regex>
#include <dlfcn.h>
+#include <cmath>
+
namespace nix {
@@ -35,7 +37,7 @@ InvalidPathError::InvalidPathError(const Path & path) :
void EvalState::realiseContext(const PathSet & context)
{
- std::vector<StorePathWithOutputs> drvs;
+ std::vector<DerivedPath::Built> drvs;
for (auto & i : context) {
auto [ctxS, outputName] = decodeContext(i);
@@ -43,7 +45,7 @@ void EvalState::realiseContext(const PathSet & context)
if (!store->isValidPath(ctx))
throw InvalidPathError(store->printStorePath(ctx));
if (!outputName.empty() && ctx.isDerivation()) {
- drvs.push_back(StorePathWithOutputs{ctx, {outputName}});
+ drvs.push_back({ctx, {outputName}});
}
}
@@ -51,14 +53,16 @@ void EvalState::realiseContext(const PathSet & context)
if (!evalSettings.enableImportFromDerivation)
throw EvalError("attempted to realize '%1%' during evaluation but 'allow-import-from-derivation' is false",
- store->printStorePath(drvs.begin()->path));
+ store->printStorePath(drvs.begin()->drvPath));
/* For performance, prefetch all substitute info. */
StorePathSet willBuild, willSubstitute, unknown;
uint64_t downloadSize, narSize;
- store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
+ std::vector<DerivedPath> buildReqs;
+ for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
+ store->queryMissing(buildReqs, willBuild, willSubstitute, unknown, downloadSize, narSize);
- store->buildPaths(drvs);
+ store->buildPaths(buildReqs);
/* Add the output of this derivations to the allowed
paths. */
@@ -545,18 +549,56 @@ typedef list<Value *> ValueList;
#endif
+static Bindings::iterator getAttr(
+ EvalState & state,
+ string funcName,
+ string attrName,
+ Bindings * attrSet,
+ const Pos & pos)
+{
+ Bindings::iterator value = attrSet->find(state.symbols.create(attrName));
+ if (value == attrSet->end()) {
+ hintformat errorMsg = hintfmt(
+ "attribute '%s' missing for call to '%s'",
+ attrName,
+ funcName
+ );
+
+ Pos aPos = *attrSet->pos;
+ if (aPos == noPos) {
+ throw TypeError({
+ .msg = errorMsg,
+ .errPos = pos,
+ });
+ } else {
+ auto e = TypeError({
+ .msg = errorMsg,
+ .errPos = aPos,
+ });
+
+ // Adding another trace for the function name to make it clear
+ // which call received wrong arguments.
+ e.addTrace(pos, hintfmt("while invoking '%s'", funcName));
+ throw e;
+ }
+ }
+
+ return value;
+}
+
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
/* Get the start set. */
- Bindings::iterator startSet =
- args[0]->attrs->find(state.symbols.create("startSet"));
- if (startSet == args[0]->attrs->end())
- throw EvalError({
- .msg = hintfmt("attribute 'startSet' required"),
- .errPos = pos
- });
+ Bindings::iterator startSet = getAttr(
+ state,
+ "genericClosure",
+ "startSet",
+ args[0]->attrs,
+ pos
+ );
+
state.forceList(*startSet->value, pos);
ValueList workSet;
@@ -564,13 +606,14 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
workSet.push_back(startSet->value->listElems()[n]);
/* Get the operator. */
- Bindings::iterator op =
- args[0]->attrs->find(state.symbols.create("operator"));
- if (op == args[0]->attrs->end())
- throw EvalError({
- .msg = hintfmt("attribute 'operator' required"),
- .errPos = pos
- });
+ Bindings::iterator op = getAttr(
+ state,
+ "genericClosure",
+ "operator",
+ args[0]->attrs,
+ pos
+ );
+
state.forceValue(*op->value, pos);
/* Construct the closure by applying the operator to element of
@@ -673,6 +716,44 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info {
.fun = prim_addErrorContext,
});
+static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
+ mkInt(v, ceil(value));
+}
+
+static RegisterPrimOp primop_ceil({
+ .name = "__ceil",
+ .args = {"double"},
+ .doc = R"(
+ Converts an IEEE-754 double-precision floating-point number (*double*) to
+ the next higher integer.
+
+ If the datatype is neither an integer nor a "float", an evaluation error will be
+ thrown.
+ )",
+ .fun = prim_ceil,
+});
+
+static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
+ mkInt(v, floor(value));
+}
+
+static RegisterPrimOp primop_floor({
+ .name = "__floor",
+ .args = {"double"},
+ .doc = R"(
+ Converts an IEEE-754 double-precision floating-point number (*double*) to
+ the next lower integer.
+
+ If the datatype is neither an integer nor a "float", an evaluation error will be
+ thrown.
+ )",
+ .fun = prim_floor,
+});
+
/* Try evaluating the argument. Success => {success=true; value=something;},
* else => {success=false; value=false;} */
static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -814,12 +895,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
state.forceAttrs(*args[0], pos);
/* Figure out the name first (for stack backtraces). */
- Bindings::iterator attr = args[0]->attrs->find(state.sName);
- if (attr == args[0]->attrs->end())
- throw EvalError({
- .msg = hintfmt("required attribute 'name' missing"),
- .errPos = pos
- });
+ Bindings::iterator attr = getAttr(
+ state,
+ "derivationStrict",
+ state.sName,
+ args[0]->attrs,
+ pos
+ );
+
string drvName;
Pos & posDrvName(*attr->pos);
try {
@@ -951,7 +1034,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} else {
- auto s = state.coerceToString(posDrvName, *i->value, context, true);
+ auto s = state.coerceToString(*i->pos, *i->value, context, true);
drv.env.emplace(key, s);
if (i->name == state.sBuilder) drv.builder = s;
else if (i->name == state.sSystem) drv.platform = s;
@@ -1208,7 +1291,10 @@ static RegisterPrimOp primop_toPath({
static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
if (evalSettings.pureEval)
- throw EvalError("builtins.storePath' is not allowed in pure evaluation mode");
+ throw EvalError({
+ .msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"),
+ .errPos = pos
+ });
PathSet context;
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context));
@@ -1367,12 +1453,13 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
if (i != v2.attrs->end())
prefix = state.forceStringNoCtx(*i->value, pos);
- i = v2.attrs->find(state.symbols.create("path"));
- if (i == v2.attrs->end())
- throw EvalError({
- .msg = hintfmt("attribute 'path' missing"),
- .errPos = pos
- });
+ i = getAttr(
+ state,
+ "findFile",
+ "path",
+ v2.attrs,
+ pos
+ );
PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false);
@@ -1918,26 +2005,26 @@ static RegisterPrimOp primop_path({
An enrichment of the built-in path type, based on the attributes
present in *args*. All are optional except `path`:
- - path
+ - path\
The underlying path.
- - name
+ - name\
The name of the path when added to the store. This can used to
reference paths that have nix-illegal characters in their names,
like `@`.
- - filter
+ - filter\
A function of the type expected by `builtins.filterSource`,
with the same semantics.
- - recursive
+ - recursive\
When `false`, when `path` is added to the store it is with a
flat hash, rather than a hash of the NAR serialization of the
file. Thus, `path` must refer to a regular file, not a
directory. This allows similar behavior to `fetchurl`. Defaults
to `true`.
- - sha256
+ - sha256\
When provided, this is the expected hash of the file at the
path. Evaluation will fail if the hash is incorrect, and
providing a hash allows `builtins.path` to be used even when the
@@ -2014,12 +2101,13 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
string attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
// !!! Should we create a symbol here or just do a lookup?
- Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
- if (i == args[1]->attrs->end())
- throw EvalError({
- .msg = hintfmt("attribute '%1%' missing", attr),
- .errPos = pos
- });
+ Bindings::iterator i = getAttr(
+ state,
+ "getAttr",
+ attr,
+ args[1]->attrs,
+ pos
+ );
// !!! add to stack trace?
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
state.forceValue(*i->value, pos);
@@ -2146,22 +2234,25 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
Value & v2(*args[0]->listElems()[i]);
state.forceAttrs(v2, pos);
- Bindings::iterator j = v2.attrs->find(state.sName);
- if (j == v2.attrs->end())
- throw TypeError({
- .msg = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
- .errPos = pos
- });
- string name = state.forceStringNoCtx(*j->value, pos);
+ Bindings::iterator j = getAttr(
+ state,
+ "listToAttrs",
+ state.sName,
+ v2.attrs,
+ pos
+ );
+
+ string name = state.forceStringNoCtx(*j->value, *j->pos);
Symbol sym = state.symbols.create(name);
if (seen.insert(sym).second) {
- Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
- if (j2 == v2.attrs->end())
- throw TypeError({
- .msg = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
- .errPos = pos
- });
+ Bindings::iterator j2 = getAttr(
+ state,
+ "listToAttrs",
+ state.sValue,
+ v2.attrs,
+ pos
+ );
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
}
}
@@ -2802,7 +2893,12 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V
for (unsigned int n = 0; n < nrLists; ++n) {
Value * vElem = args[1]->listElems()[n];
state.callFunction(*args[0], *vElem, lists[n], pos);
- state.forceList(lists[n], pos);
+ try {
+ state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)));
+ } catch (TypeError &e) {
+ e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap"));
+ throw e;
+ }
len += lists[n].listSize();
}
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 27d8ddf35..b8b99d4fa 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -303,17 +303,17 @@ static RegisterPrimOp primop_fetchGit({
of the repo at that URL is fetched. Otherwise, it can be an
attribute with the following attributes (all except `url` optional):
- - url
+ - url\
The URL of the repo.
- - name
+ - name\
The name of the directory the repo should be exported to in the
store. Defaults to the basename of the URL.
- - rev
+ - rev\
The git revision to fetch. Defaults to the tip of `ref`.
- - ref
+ - ref\
The git ref to look for the requested revision under. This is
often a branch or tag name. Defaults to `HEAD`.
@@ -321,11 +321,11 @@ static RegisterPrimOp primop_fetchGit({
of Nix 2.3.0 Nix will not prefix `refs/heads/` if `ref` starts
with `refs/`.
- - submodules
+ - submodules\
A Boolean parameter that specifies whether submodules should be
checked out. Defaults to `false`.
- - allRefs
+ - allRefs\
Whether to fetch all refs of the repository. With this argument being
true, it's possible to load a `rev` from *any* `ref` (by default only
`rev`s from the specified `ref` are supported).
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index b317c1898..a1f131f9e 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -341,6 +341,8 @@ public:
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
}
+ Pos determinePos(const Pos &pos) const;
+
/* Check whether forcing this value requires a trivial amount of
computation. In particular, function applications are
non-trivial. */