From a8fef9a6b10c34e450d4251f7e9808c906e2f488 Mon Sep 17 00:00:00 2001 From: Ben Burdette Date: Mon, 10 May 2021 18:36:57 -0600 Subject: [PATCH] throwTypeError with debugger/env --- src/libexpr/eval.cc | 123 ++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 38 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 51feef923..a48c38e0d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -35,6 +36,7 @@ namespace nix { +std::function & env)> debuggerHook; static char * dupString(const char * s) { @@ -615,7 +617,6 @@ std::optional EvalState::getDoc(Value & v) return {}; } - /* Every "format" object (even temporary) takes up a few hundred bytes of stack space, which is a real killer in the recursive evaluator. So here are some helper functions for throwing @@ -656,22 +657,46 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const }); } -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s)) +// #define valmap(x) std::optional> + +LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const std::optional> & env)) { - throw TypeError({ + auto error = TypeError({ .msg = hintfmt(s), .errPos = pos }); + + if (debuggerHook) + debuggerHook(error, *env); + throw error; } -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2)) +LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, const std::optional> & env)) { - throw TypeError({ + auto error = TypeError({ + .msg = hintfmt(s, v), + .errPos = pos + }); + + if (debuggerHook) + debuggerHook(error, *env); + throw error; +} + +LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2, const std::optional> & env)) +{ + auto error = TypeError({ .msg = hintfmt(s, fun.showNamePos(), s2), .errPos = pos }); + + if (debuggerHook) + debuggerHook(error, *env); + throw error; } + + LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1)) { throw AssertionError({ @@ -688,12 +713,16 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * }); } -LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1)) +LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1, const std::optional> & env)) { - throw MissingArgumentError({ + auto error = MissingArgumentError({ .msg = hintfmt(s, s1), .errPos = pos }); + + if (debuggerHook) + debuggerHook(error, *env); + throw error; } LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2)) @@ -1246,7 +1275,6 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) } } -std::function & env)> debuggerHook; void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & pos) { @@ -1276,14 +1304,20 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po } if (!fun.isLambda()) { - auto error = TypeError({ - // .hint = hintfmt("attempt to call something which is not a function but %1%", showType(fun)), - .msg = hintfmt("attempt to call something which is not a function but %1%", fun), - .errPos = pos - }); - if (debuggerHook) - debuggerHook(error, {{"fun", &fun}, {"arg", &arg}}); - throw error; + throwTypeError( + pos, + "attempt to call something which is not a function but %1%", + fun, + std::optional>({{"fun", &fun}, {"arg", &arg}})); + + // auto error = TypeError({ + // // .hint = hintfmt("attempt to call something which is not a function but %1%", showType(fun)), + // .msg = hintfmt("attempt to call something which is not a function but %1%", fun), + // .errPos = pos + // }); + // if (debuggerHook) + // debuggerHook(error, {{"fun", &fun}, {"arg", &arg}}); + // throw error; } ExprLambda & lambda(*fun.lambda.fun); @@ -1312,8 +1346,13 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po for (auto & i : lambda.formals->formals) { Bindings::iterator j = arg.attrs->find(i.name); if (j == arg.attrs->end()) { - if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'", - lambda, i.name); + if (!i.def) + throwTypeError( + pos, + "%1% called without required argument '%2%'", + lambda, + i.name, + std::optional>({{"fun", &fun}, {"arg", &arg}})); env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { attrsUsed++; @@ -1328,7 +1367,11 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po user. */ for (auto & i : *arg.attrs) if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end()) - throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name); + throwTypeError(pos, + "%1% called with unexpected argument '%2%'", + lambda, + i.name, + std::optional>({{"fun", &fun}, {"arg", &arg}})); abort(); // can't happen } } @@ -1398,16 +1441,14 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (j != args.end()) { actualArgs->attrs->push_back(*j); } else if (!i.def) { - auto error = MissingArgumentError({ - .msg = hintfmt(R"(cannot evaluate a function that has an argument without a value ('%1%') + throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%') Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. See -https://nixos.org/manual/nix/stable/#ss-functions.)", i.name), - .errPos = i.pos - }); - +https://nixos.org/manual/nix/stable/#ss-functions.)", i.name, + std::optional>({{"fun", &fun}})); // todo add bindings. + // throwMissingArgumentError(i.pos // , R"(cannot evaluate a function that has an argument without a value ('%1%') @@ -1416,10 +1457,11 @@ https://nixos.org/manual/nix/stable/#ss-functions.)", i.name), // values, or passed explicitly with '--arg' or '--argstr'. See // https://nixos.org/manual/nix/stable/#ss-functions.)", i.name); - if (debuggerHook) - debuggerHook(error, {{"fun", &fun}}); + // if (debuggerHook) + // // debuggerHook(error, args); + // debuggerHook(error, {{"fun", &fun}}); - throw error; + // throw error; } } @@ -1673,7 +1715,8 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos) { forceValue(v, pos); if (v.type() != nInt) - throwTypeError(pos, "value is %1% while an integer was expected", v); + throwTypeError(pos, "value is %1% while an integer was expected", v, + std::optional>({{"value", &v}})); return v.integer; } @@ -1684,7 +1727,8 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos) if (v.type() == nInt) return v.integer; else if (v.type() != nFloat) - throwTypeError(pos, "value is %1% while a float was expected", v); + throwTypeError(pos, "value is %1% while a float was expected", v, + std::optional>({{"value", &v}})); return v.fpoint; } @@ -1693,7 +1737,8 @@ bool EvalState::forceBool(Value & v, const Pos & pos) { forceValue(v, pos); if (v.type() != nBool) - throwTypeError(pos, "value is %1% while a Boolean was expected", v); + throwTypeError(pos, "value is %1% while a Boolean was expected", v, + std::optional>({{"value", &v}})); return v.boolean; } @@ -1708,7 +1753,8 @@ void EvalState::forceFunction(Value & v, const Pos & pos) { forceValue(v, pos); if (v.type() != nFunction && !isFunctor(v)) - throwTypeError(pos, "value is %1% while a function was expected", v); + throwTypeError(pos, "value is %1% while a function was expected", v, + std::optional>({{"value", &v}})); } @@ -1716,10 +1762,8 @@ string EvalState::forceString(Value & v, const Pos & pos) { forceValue(v, pos); if (v.type() != nString) { - if (pos) - throwTypeError(pos, "value is %1% while a string was expected", v); - else - throwTypeError("value is %1% while a string was expected", v); + throwTypeError(pos, "value is %1% while a string was expected", v, + std::optional>({{"value", &v}})); } return string(v.string.s); } @@ -1826,7 +1870,9 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, return *maybeString; } auto i = v.attrs->find(sOutPath); - if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string"); + if (i == v.attrs->end()) + throwTypeError(pos, "cannot coerce a set to a string", + std::optional>({{"value", &v}})); return coerceToString(pos, *i->value, context, coerceMore, copyToStore); } @@ -1857,7 +1903,8 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, } } - throwTypeError(pos, "cannot coerce %1% to a string", v); + throwTypeError(pos, "cannot coerce %1% to a string", v, + std::optional>({{"value", &v}})); }