Make function calls show up in stack traces again
Note that adding --show-trace prevents functions calls from being tail-recursive, so an expression that evaluates without --show-trace may fail with a stack overflow if --show-trace is given.
This commit is contained in:
		
							parent
							
								
									2bcb384e95
								
							
						
					
					
						commit
						89e6781cc5
					
				
					 4 changed files with 34 additions and 25 deletions
				
			
		|  | @ -279,6 +279,11 @@ 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)) | ||||
| { | ||||
|     e.addPrefix(format(s) % fun.showNamePos()); | ||||
| } | ||||
| 
 | ||||
| LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos)) | ||||
| { | ||||
|     e.addPrefix(format(s) % s2 % pos); | ||||
|  | @ -757,32 +762,34 @@ 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); | ||||
| 
 | ||||
|     ExprLambda & lambda(*fun.lambda.fun); | ||||
| 
 | ||||
|     unsigned int size = | ||||
|         (fun.lambda.fun->arg.empty() ? 0 : 1) + | ||||
|         (fun.lambda.fun->matchAttrs ? fun.lambda.fun->formals->formals.size() : 0); | ||||
|         (lambda.arg.empty() ? 0 : 1) + | ||||
|         (lambda.matchAttrs ? lambda.formals->formals.size() : 0); | ||||
|     Env & env2(allocEnv(size)); | ||||
|     env2.up = fun.lambda.env; | ||||
| 
 | ||||
|     unsigned int displ = 0; | ||||
| 
 | ||||
|     if (!fun.lambda.fun->matchAttrs) | ||||
|     if (!lambda.matchAttrs) | ||||
|         env2.values[displ++] = &arg; | ||||
| 
 | ||||
|     else { | ||||
|         forceAttrs(arg); | ||||
| 
 | ||||
|         if (!fun.lambda.fun->arg.empty()) | ||||
|         if (!lambda.arg.empty()) | ||||
|             env2.values[displ++] = &arg; | ||||
| 
 | ||||
|         /* For each formal argument, get the actual argument.  If
 | ||||
|            there is no matching actual argument but the formal | ||||
|            argument has a default, use the default. */ | ||||
|         unsigned int attrsUsed = 0; | ||||
|         foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) { | ||||
|         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%'", | ||||
|                     *fun.lambda.fun, i->name); | ||||
|                     lambda, i->name); | ||||
|                 env2.values[displ++] = i->def->maybeThunk(*this, env2); | ||||
|             } else { | ||||
|                 attrsUsed++; | ||||
|  | @ -792,29 +799,30 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) | |||
| 
 | ||||
|         /* Check that each actual argument is listed as a formal
 | ||||
|            argument (unless the attribute match specifies a `...'). */ | ||||
|         if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) { | ||||
|         if (!lambda.formals->ellipsis && attrsUsed != arg.attrs->size()) { | ||||
|             /* Nope, so show the first unexpected argument to the
 | ||||
|                user. */ | ||||
|             foreach (Bindings::iterator, i, *arg.attrs) | ||||
|                 if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end()) | ||||
|                     throwTypeError("%1% called with unexpected argument `%2%'", *fun.lambda.fun, i->name); | ||||
|                 if (lambda.formals->argNames.find(i->name) == lambda.formals->argNames.end()) | ||||
|                     throwTypeError("%1% called with unexpected argument `%2%'", lambda, i->name); | ||||
|             abort(); // can't happen
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     nrFunctionCalls++; | ||||
|     if (countCalls) incrFunctionCall(fun.lambda.fun); | ||||
|     if (countCalls) incrFunctionCall(&lambda); | ||||
| 
 | ||||
|     fun.lambda.fun->body->eval(*this, env2, v); | ||||
| 
 | ||||
| #if 0 | ||||
|     /* Evaluate the body.  This is conditional on showTrace, because
 | ||||
|        catching exceptions makes this function not tail-recursive. */ | ||||
|     if (settings.showTrace) | ||||
|         try { | ||||
|         fun.lambda.fun->body->eval(*this, env2, v); | ||||
|             lambda.body->eval(*this, env2, v); | ||||
|         } catch (Error & e) { | ||||
|         addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos()); | ||||
|             addErrorPrefix(e, "while evaluating %1%:\n", lambda); | ||||
|             throw; | ||||
|         } | ||||
| #endif | ||||
|     else | ||||
|         fun.lambda.fun->body->eval(*this, env2, v); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -89,9 +89,6 @@ static void setLogType(string lt) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static bool showTrace = false; | ||||
| 
 | ||||
| 
 | ||||
| string getArg(const string & opt, | ||||
|     Strings::iterator & i, const Strings::iterator & end) | ||||
| { | ||||
|  | @ -214,7 +211,7 @@ static void initAndRun(int argc, char * * argv) | |||
|         else if (arg == "--no-build-hook") | ||||
|             settings.useBuildHook = false; | ||||
|         else if (arg == "--show-trace") | ||||
|             showTrace = true; | ||||
|             settings.showTrace = true; | ||||
|         else if (arg == "--option") { | ||||
|             ++i; if (i == args.end()) throw UsageError("`--option' requires two arguments"); | ||||
|             string name = *i; | ||||
|  | @ -299,8 +296,8 @@ int main(int argc, char * * argv) | |||
|             % e.what() % programId); | ||||
|         return 1; | ||||
|     } catch (BaseError & e) { | ||||
|         printMsg(lvlError, format("error: %1%%2%") % (showTrace ? e.prefix() : "") % e.msg()); | ||||
|         if (e.prefix() != "" && !showTrace) | ||||
|         printMsg(lvlError, format("error: %1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); | ||||
|         if (e.prefix() != "" && !settings.showTrace) | ||||
|             printMsg(lvlError, "(use `--show-trace' to show detailed location information)"); | ||||
|         return e.status; | ||||
|     } catch (std::bad_alloc & e) { | ||||
|  |  | |||
|  | @ -55,6 +55,7 @@ Settings::Settings() | |||
|     autoOptimiseStore = false; | ||||
|     envKeepDerivations = false; | ||||
|     lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1"; | ||||
|     showTrace = false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -186,6 +186,9 @@ struct Settings { | |||
|     /* Whether to lock the Nix client and worker to the same CPU. */ | ||||
|     bool lockCPU; | ||||
| 
 | ||||
|     /* Whether to show a stack trace if Nix evaluation fails. */ | ||||
|     bool showTrace; | ||||
| 
 | ||||
| private: | ||||
|     SettingsMap settings, overrides; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue