From 89e6781cc5885cbf6284a51c0403dded62ce8bc0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 Nov 2013 12:51:59 +0100 Subject: [PATCH] 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. --- src/libexpr/eval.cc | 46 ++++++++++++++++++++++++----------------- src/libmain/shared.cc | 9 +++----- src/libstore/globals.cc | 1 + src/libstore/globals.hh | 3 +++ 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0f87b0e71..b3fc6791a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -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 - try { + /* Evaluate the body. This is conditional on showTrace, because + catching exceptions makes this function not tail-recursive. */ + if (settings.showTrace) + 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); - } catch (Error & e) { - addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos()); - throw; - } -#endif } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index b71bca682..b0b69f7f6 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -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) { diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 7e0157cd3..1ecc629cb 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -55,6 +55,7 @@ Settings::Settings() autoOptimiseStore = false; envKeepDerivations = false; lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1"; + showTrace = false; } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index cbc6d4e98..9300edbe9 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -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;