From b72c8d2e5b5bbdc218f7c00694027cdd75b6a584 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 4 Apr 2014 17:53:52 +0200 Subject: [PATCH] Include position info in function application This allows error messages like: error: the anonymous function at `/etc/nixos/configuration.nix:1:1' called without required argument `foo', at `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/lib/modules.nix:77:59' --- src/libexpr/eval-inline.hh | 2 +- src/libexpr/eval.cc | 31 +++++++++++++++++++------------ src/libexpr/eval.hh | 2 +- src/libexpr/nixexpr.cc | 2 +- src/libexpr/nixexpr.hh | 18 +++++++++++++++++- src/libexpr/parser.y | 6 +++--- src/libexpr/primops.cc | 6 +++--- 7 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 5801a20c3..ffe8fa59c 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -35,7 +35,7 @@ void EvalState::forceValue(Value & v) } } else if (v.type == tApp) - callFunction(*v.app.left, *v.app.right, v); + callFunction(*v.app.left, *v.app.right, v, noPos); else if (v.type == tBlackhole) throwEvalError("infinite recursion encountered"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bbf494387..d26a0e258 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -130,12 +130,14 @@ string showType(const Value & v) } +#if HAVE_BOEHMGC /* Called when the Boehm GC runs out of memory. */ static void * oomHandler(size_t requested) { /* Convert this to a proper C++ exception. */ throw std::bad_alloc(); } +#endif static Symbol getName(const AttrName & name, EvalState & state, Env & env) { @@ -292,14 +294,19 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1)) throw TypeError(format(s) % s1); } +LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const Pos & pos)) +{ + throw TypeError(format(s) % showType(v) % pos); +} + LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2)) { throw TypeError(format(s) % s1 % s2); } -LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun, const Symbol & s2)) +LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun, const Symbol & s2, const Pos & pos)) { - throw TypeError(format(s) % fun.showNamePos() % s2); + throw TypeError(format(s) % fun.showNamePos() % s2 % pos); } LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) @@ -317,9 +324,9 @@ 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 ExprLambda & fun)) +LocalNoInline(void addErrorPrefix(Error & e, const char * s, const ExprLambda & fun, const Pos & pos)) { - e.addPrefix(format(s) % fun.showNamePos()); + e.addPrefix(format(s) % fun.showNamePos() % pos); } LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos)) @@ -783,7 +790,7 @@ void ExprApp::eval(EvalState & state, Env & env, Value & v) /* FIXME: vFun prevents GCC from doing tail call optimisation. */ Value vFun; e1->eval(state, env, vFun); - state.callFunction(vFun, *(e2->maybeThunk(state, env)), v); + state.callFunction(vFun, *(e2->maybeThunk(state, env)), v, pos); } @@ -824,7 +831,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v) } -void EvalState::callFunction(Value & fun, Value & arg, Value & v) +void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & pos) { if (fun.type == tPrimOp || fun.type == tPrimOpApp) { callPrimOp(fun, arg, v); @@ -832,7 +839,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) } if (fun.type != tLambda) - throwTypeError("attempt to call something which is not a function but %1%", fun); + throwTypeError("attempt to call something which is not a function but %1%, at %2%", fun, pos); ExprLambda & lambda(*fun.lambda.fun); @@ -860,8 +867,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) foreach (Formals::Formals_::iterator, i, lambda.formals->formals) { Bindings::iterator j = arg.attrs->find(i->name); if (j == arg.attrs->end()) { - if (!i->def) throwTypeError("%1% called without required argument `%2%'", - lambda, i->name); + if (!i->def) throwTypeError("%1% called without required argument `%2%', at %3%", + lambda, i->name, pos); env2.values[displ++] = i->def->maybeThunk(*this, env2); } else { attrsUsed++; @@ -876,7 +883,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) user. */ foreach (Bindings::iterator, i, *arg.attrs) if (lambda.formals->argNames.find(i->name) == lambda.formals->argNames.end()) - throwTypeError("%1% called with unexpected argument `%2%'", lambda, i->name); + throwTypeError("%1% called with unexpected argument `%2%', at %3%", lambda, i->name, pos); abort(); // can't happen } } @@ -890,7 +897,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) try { lambda.body->eval(*this, env2, v); } catch (Error & e) { - addErrorPrefix(e, "while evaluating %1%:\n", lambda); + addErrorPrefix(e, "while evaluating %1%, called from %2%:\n", lambda, pos); throw; } else @@ -928,7 +935,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) actualArgs->attrs->sort(); - callFunction(fun, *actualArgs, res); + callFunction(fun, *actualArgs, res, noPos); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 58d1318c2..170b74079 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -226,7 +226,7 @@ public: elements and attributes are compared recursively. */ bool eqValues(Value & v1, Value & v2); - void callFunction(Value & fun, Value & arg, Value & v); + void callFunction(Value & fun, Value & arg, Value & v, const Pos & pos); void callPrimOp(Value & fun, Value & arg, Value & v); /* Automatically call a function for which each argument has a diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index bca2b7913..ba1f4f078 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -373,7 +373,7 @@ void ExprLambda::setName(Symbol & name) string ExprLambda::showNamePos() const { - return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str(); + return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "anonymous function") % pos).str(); } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index a5c5d0533..527589147 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -273,6 +273,23 @@ struct ExprBuiltin : Expr COMMON_METHODS }; +struct ExprApp : Expr +{ + Pos pos; + Expr * e1, * e2; + ExprApp(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; + ExprApp(const Pos & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; + void show(std::ostream & str) + { + str << *e1 << " " << *e2; + } + void bindVars(const StaticEnv & env) + { + e1->bindVars(env); e2->bindVars(env); + } + void eval(EvalState & state, Env & env, Value & v); +}; + #define MakeBinOp(name, s) \ struct Expr##name : Expr \ { \ @@ -289,7 +306,6 @@ struct ExprBuiltin : Expr void eval(EvalState & state, Env & env, Value & v); \ }; -MakeBinOp(App, "") MakeBinOp(OpEq, "==") MakeBinOp(OpNEq, "!=") MakeBinOp(OpAnd, "&&") diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index ab0b86224..b8d4d7ca0 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -224,7 +224,7 @@ void backToString(yyscan_t scanner); void backToIndString(yyscan_t scanner); -static Pos makeCurPos(const YYLTYPE & loc, ParseData * data) +static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data) { return Pos(data->path, loc.first_line, loc.first_column); } @@ -355,7 +355,7 @@ expr_op expr_app : expr_app expr_select - { $$ = new ExprApp($1, $2); } + { $$ = new ExprApp(CUR_POS, $1, $2); } | expr_select { $$ = $1; } ; @@ -367,7 +367,7 @@ expr_select | /* Backwards compatibility: because Nixpkgs has a rarely used function named ‘or’, allow stuff like ‘map or [...]’. */ expr_simple OR_KW - { $$ = new ExprApp($1, new ExprVar(CUR_POS, data->symbols.create("or"))); } + { $$ = new ExprApp(CUR_POS, $1, new ExprVar(CUR_POS, data->symbols.create("or"))); } | expr_simple { $$ = $1; } ; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index ca316f08a..422d68f38 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -711,7 +711,7 @@ struct FilterFromExpr : PathFilter mkString(arg1, path); Value fun2; - state.callFunction(filter, arg1, fun2); + state.callFunction(filter, arg1, fun2, noPos); Value arg2; mkString(arg2, @@ -721,7 +721,7 @@ struct FilterFromExpr : PathFilter "unknown" /* not supported, will fail! */); Value res; - state.callFunction(fun2, arg2, res); + state.callFunction(fun2, arg2, res, noPos); return state.forceBool(res); } @@ -1008,7 +1008,7 @@ static void prim_filter(EvalState & state, Value * * args, Value & v) bool same = true; for (unsigned int n = 0; n < args[1]->list.length; ++n) { Value res; - state.callFunction(*args[0], *args[1]->list.elems[n], res); + state.callFunction(*args[0], *args[1]->list.elems[n], res, noPos); if (state.forceBool(res)) vs[k++] = args[1]->list.elems[n]; else