* Greatly reduced the amount of stack space used by the Nix expression
evaluator. This was important because the NixOS expressions started to hit 2 MB default stack size on Linux. GCC is really dumb about stack space: it just adds up all the local variables and temporaries of every scope into one huge stack frame. This is really bad for deeply recursive functions. For instance, every `throw Error(format("error message"))' causes a format object of a few hundred bytes to be allocated on the stack. As a result, every recursive call to evalExpr2() consumed 4680 bytes. By splitting evalExpr2() and by moving the exception-throwing code out of the main functions, evalExpr2() now only consumes 40 bytes. Similar for evalExpr().
This commit is contained in:
parent
adce01a8d0
commit
044b6482c1
|
@ -8,6 +8,10 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
|
||||||
|
|
||||||
|
#define LocalNoInline(f) static f __attribute__((noinline)); f
|
||||||
|
#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,6 +33,42 @@ void EvalState::addPrimOp(const string & name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Every "format" object (even temporary) takes up a few hundred bytes
|
||||||
|
of stack space, which is a real killer in the recursive
|
||||||
|
evaluator. So here are some helper functions for throwing
|
||||||
|
exceptions. */
|
||||||
|
|
||||||
|
LocalNoInlineNoReturn(void throwEvalError(const char * s))
|
||||||
|
{
|
||||||
|
throw EvalError(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
||||||
|
{
|
||||||
|
throw EvalError(format(s) % s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
|
||||||
|
{
|
||||||
|
throw TypeError(format(s) % s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s))
|
||||||
|
{
|
||||||
|
e.addPrefix(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
|
||||||
|
{
|
||||||
|
e.addPrefix(format(s) % s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const string & s3))
|
||||||
|
{
|
||||||
|
e.addPrefix(format(s) % s2 % s3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Substitute an argument set into the body of a function. */
|
/* Substitute an argument set into the body of a function. */
|
||||||
static Expr substArgs(EvalState & state,
|
static Expr substArgs(EvalState & state,
|
||||||
Expr body, ATermList formals, Expr arg)
|
Expr body, ATermList formals, Expr arg)
|
||||||
|
@ -111,7 +151,7 @@ static Expr substArgs(EvalState & state,
|
||||||
to attributes substituted with selection expressions on the
|
to attributes substituted with selection expressions on the
|
||||||
original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
|
original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
|
||||||
(e.x) (e.y); y = e.x;}'. */
|
(e.x) (e.y); y = e.x;}'. */
|
||||||
ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds)
|
LocalNoInline(ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds))
|
||||||
{
|
{
|
||||||
ATerm name;
|
ATerm name;
|
||||||
Expr e2;
|
Expr e2;
|
||||||
|
@ -147,7 +187,7 @@ ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr updateAttrs(Expr e1, Expr e2)
|
LocalNoInline(Expr updateAttrs(Expr e1, Expr e2))
|
||||||
{
|
{
|
||||||
/* Note: e1 and e2 should be in normal form. */
|
/* Note: e1 and e2 should be in normal form. */
|
||||||
|
|
||||||
|
@ -164,7 +204,7 @@ string evalString(EvalState & state, Expr e, PathSet & context)
|
||||||
e = evalExpr(state, e);
|
e = evalExpr(state, e);
|
||||||
string s;
|
string s;
|
||||||
if (!matchStr(e, s, context))
|
if (!matchStr(e, s, context))
|
||||||
throw TypeError(format("value is %1% while a string was expected") % showType(e));
|
throwTypeError("value is %1% while a string was expected", showType(e));
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +225,7 @@ int evalInt(EvalState & state, Expr e)
|
||||||
e = evalExpr(state, e);
|
e = evalExpr(state, e);
|
||||||
int i;
|
int i;
|
||||||
if (!matchInt(e, i))
|
if (!matchInt(e, i))
|
||||||
throw TypeError(format("value is %1% while an integer was expected") % showType(e));
|
throwTypeError("value is %1% while an integer was expected", showType(e));
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +235,7 @@ bool evalBool(EvalState & state, Expr e)
|
||||||
e = evalExpr(state, e);
|
e = evalExpr(state, e);
|
||||||
if (e == eTrue) return true;
|
if (e == eTrue) return true;
|
||||||
else if (e == eFalse) return false;
|
else if (e == eFalse) return false;
|
||||||
else throw TypeError(format("value is %1% while a boolean was expected") % showType(e));
|
else throwTypeError("value is %1% while a boolean was expected", showType(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -204,7 +244,7 @@ ATermList evalList(EvalState & state, Expr e)
|
||||||
e = evalExpr(state, e);
|
e = evalExpr(state, e);
|
||||||
ATermList list;
|
ATermList list;
|
||||||
if (!matchList(e, list))
|
if (!matchList(e, list))
|
||||||
throw TypeError(format("value is %1% while a list was expected") % showType(e));
|
throwTypeError("value is %1% while a list was expected", showType(e));
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +332,7 @@ string coerceToString(EvalState & state, Expr e, PathSet & context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw TypeError(format("cannot coerce %1% to a string") % showType(e));
|
throwTypeError("cannot coerce %1% to a string", showType(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,6 +402,215 @@ Expr autoCallFunction(Expr e, const ATermMap & args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Evaluation of various language constructs. These have been taken
|
||||||
|
out of evalExpr2 to reduce stack space usage. (GCC is really dumb
|
||||||
|
about stack space: it just adds up all the local variables and
|
||||||
|
temporaries of every scope into one huge stack frame. This is
|
||||||
|
really bad for deeply recursive functions.) */
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalVar(EvalState & state, ATerm name))
|
||||||
|
{
|
||||||
|
ATerm primOp = state.primOps.get(name);
|
||||||
|
if (!primOp)
|
||||||
|
throw EvalError(format("impossible: undefined variable `%1%'") % aterm2String(name));
|
||||||
|
int arity;
|
||||||
|
ATermBlob fun;
|
||||||
|
if (!matchPrimOpDef(primOp, arity, fun)) abort();
|
||||||
|
if (arity == 0)
|
||||||
|
/* !!! backtrace for primop call */
|
||||||
|
return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
|
||||||
|
else
|
||||||
|
return makePrimOp(arity, fun, ATempty);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
||||||
|
{
|
||||||
|
ATermList formals;
|
||||||
|
ATerm pos, name;
|
||||||
|
Expr body;
|
||||||
|
|
||||||
|
/* Evaluate the left-hand side. */
|
||||||
|
fun = evalExpr(state, fun);
|
||||||
|
|
||||||
|
/* Is it a primop or a function? */
|
||||||
|
int arity;
|
||||||
|
ATermBlob funBlob;
|
||||||
|
ATermList args;
|
||||||
|
if (matchPrimOp(fun, arity, funBlob, args)) {
|
||||||
|
args = ATinsert(args, arg);
|
||||||
|
if (ATgetLength(args) == arity) {
|
||||||
|
/* Put the arguments in a vector in reverse (i.e.,
|
||||||
|
actual) order. */
|
||||||
|
ATermVector args2(arity);
|
||||||
|
for (ATermIterator i(args); i; ++i)
|
||||||
|
args2[--arity] = *i;
|
||||||
|
/* !!! backtrace for primop call */
|
||||||
|
return ((PrimOp) ATgetBlobData(funBlob))
|
||||||
|
(state, args2);
|
||||||
|
} else
|
||||||
|
/* Need more arguments, so propagate the primop. */
|
||||||
|
return makePrimOp(arity, funBlob, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (matchFunction(fun, formals, body, pos)) {
|
||||||
|
arg = evalExpr(state, arg);
|
||||||
|
try {
|
||||||
|
return evalExpr(state, substArgs(state, body, formals, arg));
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||||
|
showPos(pos));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (matchFunction1(fun, name, body, pos)) {
|
||||||
|
try {
|
||||||
|
ATermMap subs(1);
|
||||||
|
subs.set(name, arg);
|
||||||
|
return evalExpr(state, substitute(Substitution(0, &subs), body));
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||||
|
showPos(pos));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else throwTypeError(
|
||||||
|
"the left-hand side of the function call is neither a function nor a primop (built-in operation) but %1%",
|
||||||
|
showType(fun));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalSelect(EvalState & state, Expr e, ATerm name))
|
||||||
|
{
|
||||||
|
ATerm pos;
|
||||||
|
string s = aterm2String(name);
|
||||||
|
Expr a = queryAttr(evalExpr(state, e), s, pos);
|
||||||
|
if (!a) throwEvalError("attribute `%1%' missing", s);
|
||||||
|
try {
|
||||||
|
return evalExpr(state, a);
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
|
||||||
|
s, showPos(pos));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalAssert(EvalState & state, Expr cond, Expr body, ATerm pos))
|
||||||
|
{
|
||||||
|
if (!evalBool(state, cond))
|
||||||
|
throw AssertionError(format("assertion failed at %1%") % showPos(pos));
|
||||||
|
return evalExpr(state, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos))
|
||||||
|
{
|
||||||
|
ATermMap attrs;
|
||||||
|
try {
|
||||||
|
defs = evalExpr(state, defs);
|
||||||
|
queryAllAttrs(defs, attrs);
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorPrefix(e, "while evaluating the `with' definitions at %1%:\n",
|
||||||
|
showPos(pos));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
body = substitute(Substitution(0, &attrs), body);
|
||||||
|
checkVarDefs(state.primOps, body);
|
||||||
|
return evalExpr(state, body);
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorPrefix(e, "while evaluating the `with' body at %1%:\n",
|
||||||
|
showPos(pos));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalHasAttr(EvalState & state, Expr e, ATerm name))
|
||||||
|
{
|
||||||
|
ATermMap attrs;
|
||||||
|
queryAllAttrs(evalExpr(state, e), attrs);
|
||||||
|
return makeBool(attrs.get(name) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalPlusConcat(EvalState & state, Expr e))
|
||||||
|
{
|
||||||
|
Expr e1, e2;
|
||||||
|
ATermList es;
|
||||||
|
|
||||||
|
ATermVector args;
|
||||||
|
|
||||||
|
if (matchOpPlus(e, e1, e2)) {
|
||||||
|
|
||||||
|
/* !!! Awful compatibility hack for `drv + /path'.
|
||||||
|
According to regular concatenation, /path should be
|
||||||
|
copied to the store and its store path should be
|
||||||
|
appended to the string. However, in Nix <= 0.10, /path
|
||||||
|
was concatenated. So handle that case separately, but
|
||||||
|
do print out a warning. This code can go in Nix 0.12,
|
||||||
|
maybe. */
|
||||||
|
e1 = evalExpr(state, e1);
|
||||||
|
e2 = evalExpr(state, e2);
|
||||||
|
|
||||||
|
ATermList as;
|
||||||
|
ATerm p;
|
||||||
|
if (matchAttrs(e1, as) && matchPath(e2, p)) {
|
||||||
|
static bool haveWarned = false;
|
||||||
|
warnOnce(haveWarned, format(
|
||||||
|
"concatenation of a derivation and a path is deprecated; "
|
||||||
|
"you should write `drv + \"%1%\"' instead of `drv + %1%'")
|
||||||
|
% aterm2String(p));
|
||||||
|
PathSet context;
|
||||||
|
return makeStr(
|
||||||
|
coerceToString(state, makeSelect(e1, toATerm("outPath")), context)
|
||||||
|
+ aterm2String(p), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push_back(e1);
|
||||||
|
args.push_back(e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (matchConcatStrings(e, es))
|
||||||
|
for (ATermIterator i(es); i; ++i) args.push_back(*i);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return concatStrings(state, args);
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorPrefix(e, "in a string concatenation:\n");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalSubPath(EvalState & state, Expr e1, Expr e2))
|
||||||
|
{
|
||||||
|
static bool haveWarned = false;
|
||||||
|
warnOnce(haveWarned, "the subpath operator (~) is deprecated, use string concatenation (+) instead");
|
||||||
|
ATermVector args;
|
||||||
|
args.push_back(e1);
|
||||||
|
args.push_back(e2);
|
||||||
|
return concatStrings(state, args, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInline(Expr evalOpConcat(EvalState & state, Expr e1, Expr e2))
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ATermList l1 = evalList(state, e1);
|
||||||
|
ATermList l2 = evalList(state, e2);
|
||||||
|
return makeList(ATconcat(l1, l2));
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorPrefix(e, "in a list concatenation:\n");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char * deepestStack = (char *) -1; /* for measuring stack usage */
|
static char * deepestStack = (char *) -1; /* for measuring stack usage */
|
||||||
|
|
||||||
|
|
||||||
|
@ -370,7 +619,7 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
char x;
|
char x;
|
||||||
if (&x < deepestStack) deepestStack = &x;
|
if (&x < deepestStack) deepestStack = &x;
|
||||||
|
|
||||||
Expr e1, e2, e3, e4;
|
Expr e1, e2, e3;
|
||||||
ATerm name, pos;
|
ATerm name, pos;
|
||||||
AFun sym = ATgetAFun(e);
|
AFun sym = ATgetAFun(e);
|
||||||
|
|
||||||
|
@ -394,91 +643,13 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
|
|
||||||
/* Any encountered variables must be primops (since undefined
|
/* Any encountered variables must be primops (since undefined
|
||||||
variables are detected after parsing). */
|
variables are detected after parsing). */
|
||||||
if (matchVar(e, name)) {
|
if (matchVar(e, name)) return evalVar(state, name);
|
||||||
ATerm primOp = state.primOps.get(name);
|
|
||||||
if (!primOp)
|
|
||||||
throw EvalError(format("impossible: undefined variable `%1%'") % aterm2String(name));
|
|
||||||
int arity;
|
|
||||||
ATermBlob fun;
|
|
||||||
if (!matchPrimOpDef(primOp, arity, fun)) abort();
|
|
||||||
if (arity == 0)
|
|
||||||
/* !!! backtrace for primop call */
|
|
||||||
return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
|
|
||||||
else
|
|
||||||
return makePrimOp(arity, fun, ATempty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Function application. */
|
/* Function application. */
|
||||||
if (matchCall(e, e1, e2)) {
|
if (matchCall(e, e1, e2)) return evalCall(state, e1, e2);
|
||||||
|
|
||||||
ATermList formals;
|
|
||||||
ATerm pos;
|
|
||||||
|
|
||||||
/* Evaluate the left-hand side. */
|
|
||||||
e1 = evalExpr(state, e1);
|
|
||||||
|
|
||||||
/* Is it a primop or a function? */
|
|
||||||
int arity;
|
|
||||||
ATermBlob fun;
|
|
||||||
ATermList args;
|
|
||||||
if (matchPrimOp(e1, arity, fun, args)) {
|
|
||||||
args = ATinsert(args, e2);
|
|
||||||
if (ATgetLength(args) == arity) {
|
|
||||||
/* Put the arguments in a vector in reverse (i.e.,
|
|
||||||
actual) order. */
|
|
||||||
ATermVector args2(arity);
|
|
||||||
for (ATermIterator i(args); i; ++i)
|
|
||||||
args2[--arity] = *i;
|
|
||||||
/* !!! backtrace for primop call */
|
|
||||||
return ((PrimOp) ATgetBlobData((ATermBlob) fun))
|
|
||||||
(state, args2);
|
|
||||||
} else
|
|
||||||
/* Need more arguments, so propagate the primop. */
|
|
||||||
return makePrimOp(arity, fun, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (matchFunction(e1, formals, e4, pos)) {
|
|
||||||
e2 = evalExpr(state, e2);
|
|
||||||
try {
|
|
||||||
return evalExpr(state, substArgs(state, e4, formals, e2));
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addPrefix(format("while evaluating the function at %1%:\n")
|
|
||||||
% showPos(pos));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (matchFunction1(e1, name, e4, pos)) {
|
|
||||||
try {
|
|
||||||
ATermMap subs(1);
|
|
||||||
subs.set(name, e2);
|
|
||||||
return evalExpr(state, substitute(Substitution(0, &subs), e4));
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addPrefix(format("while evaluating the function at %1%:\n")
|
|
||||||
% showPos(pos));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else throw TypeError(
|
|
||||||
format("the left-hand side of the function call is neither a function nor a primop (built-in operation) but %1%")
|
|
||||||
% showType(e1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attribute selection. */
|
/* Attribute selection. */
|
||||||
if (matchSelect(e, e1, name)) {
|
if (matchSelect(e, e1, name)) return evalSelect(state, e1, name);
|
||||||
ATerm pos;
|
|
||||||
string s1 = aterm2String(name);
|
|
||||||
Expr a = queryAttr(evalExpr(state, e1), s1, pos);
|
|
||||||
if (!a) throw EvalError(format("attribute `%1%' missing") % s1);
|
|
||||||
try {
|
|
||||||
return evalExpr(state, a);
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addPrefix(format("while evaluating the attribute `%1%' at %2%:\n")
|
|
||||||
% s1 % showPos(pos));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mutually recursive sets. */
|
/* Mutually recursive sets. */
|
||||||
ATermList rbnds, nrbnds;
|
ATermList rbnds, nrbnds;
|
||||||
|
@ -486,41 +657,14 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
return expandRec(e, rbnds, nrbnds);
|
return expandRec(e, rbnds, nrbnds);
|
||||||
|
|
||||||
/* Conditionals. */
|
/* Conditionals. */
|
||||||
if (matchIf(e, e1, e2, e3)) {
|
if (matchIf(e, e1, e2, e3))
|
||||||
if (evalBool(state, e1))
|
return evalExpr(state, evalBool(state, e1) ? e2 : e3);
|
||||||
return evalExpr(state, e2);
|
|
||||||
else
|
|
||||||
return evalExpr(state, e3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assertions. */
|
/* Assertions. */
|
||||||
if (matchAssert(e, e1, e2, pos)) {
|
if (matchAssert(e, e1, e2, pos)) return evalAssert(state, e1, e2, pos);
|
||||||
if (!evalBool(state, e1))
|
|
||||||
throw AssertionError(format("assertion failed at %1%") % showPos(pos));
|
|
||||||
return evalExpr(state, e2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Withs. */
|
/* Withs. */
|
||||||
if (matchWith(e, e1, e2, pos)) {
|
if (matchWith(e, e1, e2, pos)) return evalWith(state, e1, e2, pos);
|
||||||
ATermMap attrs;
|
|
||||||
try {
|
|
||||||
e1 = evalExpr(state, e1);
|
|
||||||
queryAllAttrs(e1, attrs);
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addPrefix(format("while evaluating the `with' definitions at %1%:\n")
|
|
||||||
% showPos(pos));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
e2 = substitute(Substitution(0, &attrs), e2);
|
|
||||||
checkVarDefs(state.primOps, e2);
|
|
||||||
return evalExpr(state, e2);
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addPrefix(format("while evaluating the `with' body at %1%:\n")
|
|
||||||
% showPos(pos));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generic equality/inequality. Note that the behaviour on
|
/* Generic equality/inequality. Note that the behaviour on
|
||||||
composite data (lists, attribute sets) and functions is
|
composite data (lists, attribute sets) and functions is
|
||||||
|
@ -555,79 +699,20 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
return updateAttrs(evalExpr(state, e1), evalExpr(state, e2));
|
return updateAttrs(evalExpr(state, e1), evalExpr(state, e2));
|
||||||
|
|
||||||
/* Attribute existence test (?). */
|
/* Attribute existence test (?). */
|
||||||
if (matchOpHasAttr(e, e1, name)) {
|
if (matchOpHasAttr(e, e1, name)) return evalHasAttr(state, e1, name);
|
||||||
ATermMap attrs;
|
|
||||||
queryAllAttrs(evalExpr(state, e1), attrs);
|
|
||||||
return makeBool(attrs.get(name) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* String or path concatenation. */
|
/* String or path concatenation. */
|
||||||
ATermList es = ATempty;
|
if (sym == symOpPlus || sym == symConcatStrings)
|
||||||
if (matchOpPlus(e, e1, e2) || matchConcatStrings(e, es)) {
|
return evalPlusConcat(state, e);
|
||||||
ATermVector args;
|
|
||||||
if (matchOpPlus(e, e1, e2)) {
|
|
||||||
|
|
||||||
/* !!! Awful compatibility hack for `drv + /path'.
|
|
||||||
According to regular concatenation, /path should be
|
|
||||||
copied to the store and its store path should be
|
|
||||||
appended to the string. However, in Nix <= 0.10, /path
|
|
||||||
was concatenated. So handle that case separately, but
|
|
||||||
do print out a warning. This code can go in Nix 0.12,
|
|
||||||
maybe. */
|
|
||||||
e1 = evalExpr(state, e1);
|
|
||||||
e2 = evalExpr(state, e2);
|
|
||||||
|
|
||||||
ATermList as;
|
|
||||||
ATerm p;
|
|
||||||
if (matchAttrs(e1, as) && matchPath(e2, p)) {
|
|
||||||
static bool haveWarned = false;
|
|
||||||
warnOnce(haveWarned, format(
|
|
||||||
"concatenation of a derivation and a path is deprecated; "
|
|
||||||
"you should write `drv + \"%1%\"' instead of `drv + %1%'")
|
|
||||||
% aterm2String(p));
|
|
||||||
PathSet context;
|
|
||||||
return makeStr(
|
|
||||||
coerceToString(state, makeSelect(e1, toATerm("outPath")), context)
|
|
||||||
+ aterm2String(p), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
args.push_back(e1);
|
|
||||||
args.push_back(e2);
|
|
||||||
} else
|
|
||||||
for (ATermIterator i(es); i; ++i) args.push_back(*i);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return concatStrings(state, args);
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addPrefix(format("in a string concatenation:\n"));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Backwards compatability: subpath operator (~). */
|
/* Backwards compatability: subpath operator (~). */
|
||||||
if (matchSubPath(e, e1, e2)) {
|
if (matchSubPath(e, e1, e2)) return evalSubPath(state, e1, e2);
|
||||||
static bool haveWarned = false;
|
|
||||||
warnOnce(haveWarned, "the subpath operator (~) is deprecated, use string concatenation (+) instead");
|
|
||||||
ATermVector args;
|
|
||||||
args.push_back(e1);
|
|
||||||
args.push_back(e2);
|
|
||||||
return concatStrings(state, args, "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* List concatenation. */
|
/* List concatenation. */
|
||||||
if (matchOpConcat(e, e1, e2)) {
|
if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
|
||||||
try {
|
|
||||||
ATermList l1 = evalList(state, e1);
|
|
||||||
ATermList l2 = evalList(state, e2);
|
|
||||||
return makeList(ATconcat(l1, l2));
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addPrefix(format("in a list concatenation:\n"));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Barf. */
|
/* Barf. */
|
||||||
throw badTerm("invalid expression", e);
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -635,8 +720,10 @@ Expr evalExpr(EvalState & state, Expr e)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
#if 0
|
||||||
startNest(nest, lvlVomit,
|
startNest(nest, lvlVomit,
|
||||||
format("evaluating expression: %1%") % e);
|
format("evaluating expression: %1%") % e);
|
||||||
|
#endif
|
||||||
|
|
||||||
state.nrEvaluated++;
|
state.nrEvaluated++;
|
||||||
|
|
||||||
|
@ -655,7 +742,6 @@ Expr evalExpr(EvalState & state, Expr e)
|
||||||
try {
|
try {
|
||||||
nf = evalExpr2(state, e);
|
nf = evalExpr2(state, e);
|
||||||
} catch (Error & err) {
|
} catch (Error & err) {
|
||||||
debug("removing black hole");
|
|
||||||
state.normalForms.remove(e);
|
state.normalForms.remove(e);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue