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