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'
			
			
This commit is contained in:
		
							parent
							
								
									3f8e1f5682
								
							
						
					
					
						commit
						b72c8d2e5b
					
				
					 7 changed files with 45 additions and 22 deletions
				
			
		|  | @ -35,7 +35,7 @@ void EvalState::forceValue(Value & v) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else if (v.type == tApp) |     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) |     else if (v.type == tBlackhole) | ||||||
|         throwEvalError("infinite recursion encountered"); |         throwEvalError("infinite recursion encountered"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -130,12 +130,14 @@ string showType(const Value & v) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #if HAVE_BOEHMGC | ||||||
| /* Called when the Boehm GC runs out of memory. */ | /* Called when the Boehm GC runs out of memory. */ | ||||||
| static void * oomHandler(size_t requested) | static void * oomHandler(size_t requested) | ||||||
| { | { | ||||||
|     /* Convert this to a proper C++ exception. */ |     /* Convert this to a proper C++ exception. */ | ||||||
|     throw std::bad_alloc(); |     throw std::bad_alloc(); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static Symbol getName(const AttrName & name, EvalState & state, Env & env) { | 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); |     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)) | LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2)) | ||||||
| { | { | ||||||
|     throw TypeError(format(s) % s1 % 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)) | 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); |     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)) | 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. */ |     /* FIXME: vFun prevents GCC from doing tail call optimisation. */ | ||||||
|     Value vFun; |     Value vFun; | ||||||
|     e1->eval(state, env, 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) { |     if (fun.type == tPrimOp || fun.type == tPrimOpApp) { | ||||||
|         callPrimOp(fun, arg, v); |         callPrimOp(fun, arg, v); | ||||||
|  | @ -832,7 +839,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (fun.type != tLambda) |     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); |     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) { |         foreach (Formals::Formals_::iterator, i, lambda.formals->formals) { | ||||||
|             Bindings::iterator j = arg.attrs->find(i->name); |             Bindings::iterator j = arg.attrs->find(i->name); | ||||||
|             if (j == arg.attrs->end()) { |             if (j == arg.attrs->end()) { | ||||||
|                 if (!i->def) throwTypeError("%1% called without required argument `%2%'", |                 if (!i->def) throwTypeError("%1% called without required argument `%2%', at %3%", | ||||||
|                     lambda, i->name); |                     lambda, i->name, pos); | ||||||
|                 env2.values[displ++] = i->def->maybeThunk(*this, env2); |                 env2.values[displ++] = i->def->maybeThunk(*this, env2); | ||||||
|             } else { |             } else { | ||||||
|                 attrsUsed++; |                 attrsUsed++; | ||||||
|  | @ -876,7 +883,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
|                user. */ |                user. */ | ||||||
|             foreach (Bindings::iterator, i, *arg.attrs) |             foreach (Bindings::iterator, i, *arg.attrs) | ||||||
|                 if (lambda.formals->argNames.find(i->name) == lambda.formals->argNames.end()) |                 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
 |             abort(); // can't happen
 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -890,7 +897,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
|         try { |         try { | ||||||
|             lambda.body->eval(*this, env2, v); |             lambda.body->eval(*this, env2, v); | ||||||
|         } catch (Error & e) { |         } catch (Error & e) { | ||||||
|             addErrorPrefix(e, "while evaluating %1%:\n", lambda); |             addErrorPrefix(e, "while evaluating %1%, called from %2%:\n", lambda, pos); | ||||||
|             throw; |             throw; | ||||||
|         } |         } | ||||||
|     else |     else | ||||||
|  | @ -928,7 +935,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) | ||||||
| 
 | 
 | ||||||
|     actualArgs->attrs->sort(); |     actualArgs->attrs->sort(); | ||||||
| 
 | 
 | ||||||
|     callFunction(fun, *actualArgs, res); |     callFunction(fun, *actualArgs, res, noPos); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -226,7 +226,7 @@ public: | ||||||
|        elements and attributes are compared recursively. */ |        elements and attributes are compared recursively. */ | ||||||
|     bool eqValues(Value & v1, Value & v2); |     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); |     void callPrimOp(Value & fun, Value & arg, Value & v); | ||||||
| 
 | 
 | ||||||
|     /* Automatically call a function for which each argument has a
 |     /* Automatically call a function for which each argument has a
 | ||||||
|  |  | ||||||
|  | @ -373,7 +373,7 @@ void ExprLambda::setName(Symbol & name) | ||||||
| 
 | 
 | ||||||
| string ExprLambda::showNamePos() const | 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(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -273,6 +273,23 @@ struct ExprBuiltin : Expr | ||||||
|     COMMON_METHODS |     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) \ | #define MakeBinOp(name, s) \ | ||||||
|     struct Expr##name : Expr \ |     struct Expr##name : Expr \ | ||||||
|     { \ |     { \ | ||||||
|  | @ -289,7 +306,6 @@ struct ExprBuiltin : Expr | ||||||
|         void eval(EvalState & state, Env & env, Value & v); \ |         void eval(EvalState & state, Env & env, Value & v); \ | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| MakeBinOp(App, "") |  | ||||||
| MakeBinOp(OpEq, "==") | MakeBinOp(OpEq, "==") | ||||||
| MakeBinOp(OpNEq, "!=") | MakeBinOp(OpNEq, "!=") | ||||||
| MakeBinOp(OpAnd, "&&") | MakeBinOp(OpAnd, "&&") | ||||||
|  |  | ||||||
|  | @ -224,7 +224,7 @@ void backToString(yyscan_t scanner); | ||||||
| void backToIndString(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); |     return Pos(data->path, loc.first_line, loc.first_column); | ||||||
| } | } | ||||||
|  | @ -355,7 +355,7 @@ expr_op | ||||||
| 
 | 
 | ||||||
| expr_app | expr_app | ||||||
|   : expr_app expr_select |   : expr_app expr_select | ||||||
|     { $$ = new ExprApp($1, $2); } |     { $$ = new ExprApp(CUR_POS, $1, $2); } | ||||||
|   | expr_select { $$ = $1; } |   | expr_select { $$ = $1; } | ||||||
|   ; |   ; | ||||||
| 
 | 
 | ||||||
|  | @ -367,7 +367,7 @@ expr_select | ||||||
|   | /* Backwards compatibility: because Nixpkgs has a rarely used |   | /* Backwards compatibility: because Nixpkgs has a rarely used | ||||||
|        function named ‘or’, allow stuff like ‘map or [...]’. */ |        function named ‘or’, allow stuff like ‘map or [...]’. */ | ||||||
|     expr_simple OR_KW |     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; } |   | expr_simple { $$ = $1; } | ||||||
|   ; |   ; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -711,7 +711,7 @@ struct FilterFromExpr : PathFilter | ||||||
|         mkString(arg1, path); |         mkString(arg1, path); | ||||||
| 
 | 
 | ||||||
|         Value fun2; |         Value fun2; | ||||||
|         state.callFunction(filter, arg1, fun2); |         state.callFunction(filter, arg1, fun2, noPos); | ||||||
| 
 | 
 | ||||||
|         Value arg2; |         Value arg2; | ||||||
|         mkString(arg2, |         mkString(arg2, | ||||||
|  | @ -721,7 +721,7 @@ struct FilterFromExpr : PathFilter | ||||||
|             "unknown" /* not supported, will fail! */); |             "unknown" /* not supported, will fail! */); | ||||||
| 
 | 
 | ||||||
|         Value res; |         Value res; | ||||||
|         state.callFunction(fun2, arg2, res); |         state.callFunction(fun2, arg2, res, noPos); | ||||||
| 
 | 
 | ||||||
|         return state.forceBool(res); |         return state.forceBool(res); | ||||||
|     } |     } | ||||||
|  | @ -1008,7 +1008,7 @@ static void prim_filter(EvalState & state, Value * * args, Value & v) | ||||||
|     bool same = true; |     bool same = true; | ||||||
|     for (unsigned int n = 0; n < args[1]->list.length; ++n) { |     for (unsigned int n = 0; n < args[1]->list.length; ++n) { | ||||||
|         Value res; |         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)) |         if (state.forceBool(res)) | ||||||
|             vs[k++] = args[1]->list.elems[n]; |             vs[k++] = args[1]->list.elems[n]; | ||||||
|         else |         else | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue