aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/eval.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr/eval.hh')
-rw-r--r--src/libexpr/eval.hh178
1 files changed, 84 insertions, 94 deletions
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index df6ac431d..e4d5906bd 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -86,6 +86,43 @@ struct DebugTrace {
void debugError(Error * e, Env & env, Expr & expr);
+class ErrorBuilder
+{
+ private:
+ EvalState & state;
+ ErrorInfo info;
+
+ ErrorBuilder(EvalState & s, ErrorInfo && i): state(s), info(i) { }
+
+ public:
+ template<typename... Args>
+ [[nodiscard, gnu::noinline]]
+ static ErrorBuilder * create(EvalState & s, const Args & ... args)
+ {
+ return new ErrorBuilder(s, ErrorInfo { .msg = hintfmt(args...) });
+ }
+
+ [[nodiscard, gnu::noinline]]
+ ErrorBuilder & atPos(PosIdx pos);
+
+ [[nodiscard, gnu::noinline]]
+ ErrorBuilder & withTrace(PosIdx pos, const std::string_view text);
+
+ [[nodiscard, gnu::noinline]]
+ ErrorBuilder & withFrameTrace(PosIdx pos, const std::string_view text);
+
+ [[nodiscard, gnu::noinline]]
+ ErrorBuilder & withSuggestions(Suggestions & s);
+
+ [[nodiscard, gnu::noinline]]
+ ErrorBuilder & withFrame(const Env & e, const Expr & ex);
+
+ template<class ErrorType>
+ [[gnu::noinline, gnu::noreturn]]
+ void debugThrow();
+};
+
+
class EvalState : public std::enable_shared_from_this<EvalState>
{
public:
@@ -145,29 +182,38 @@ public:
template<class E>
[[gnu::noinline, gnu::noreturn]]
- void debugThrow(E && error, const Env & env, const Expr & expr)
+ void debugThrowLastTrace(E && error)
{
- if (debugRepl)
- runDebugRepl(&error, env, expr);
-
- throw std::move(error);
+ debugThrow(error, nullptr, nullptr);
}
template<class E>
[[gnu::noinline, gnu::noreturn]]
- void debugThrowLastTrace(E && e)
+ void debugThrow(E && error, const Env * env, const Expr * expr)
{
- // Call this in the situation where Expr and Env are inaccessible.
- // The debugger will start in the last context that's in the
- // DebugTrace stack.
- if (debugRepl && !debugTraces.empty()) {
- const DebugTrace & last = debugTraces.front();
- runDebugRepl(&e, last.env, last.expr);
+ if (debugRepl && ((env && expr) || !debugTraces.empty())) {
+ if (!env || !expr) {
+ const DebugTrace & last = debugTraces.front();
+ env = &last.env;
+ expr = &last.expr;
+ }
+ runDebugRepl(&error, *env, *expr);
}
- throw std::move(e);
+ throw std::move(error);
}
+ // This is dangerous, but gets in line with the idea that error creation and
+ // throwing should not allocate on the stack of hot functions.
+ // as long as errors are immediately thrown, it works.
+ ErrorBuilder * errorBuilder;
+
+ template<typename... Args>
+ [[nodiscard, gnu::noinline]]
+ ErrorBuilder & error(const Args & ... args) {
+ errorBuilder = ErrorBuilder::create(*this, args...);
+ return *errorBuilder;
+ }
private:
SrcToStore srcToStore;
@@ -282,8 +328,8 @@ public:
/* Evaluation the expression, then verify that it has the expected
type. */
inline bool evalBool(Env & env, Expr * e);
- inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
- inline void evalAttrs(Env & env, Expr * e, Value & v);
+ inline bool evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx);
+ inline void evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx);
/* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function
@@ -299,89 +345,25 @@ public:
void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */
- NixInt forceInt(Value & v, const PosIdx pos);
- NixFloat forceFloat(Value & v, const PosIdx pos);
- bool forceBool(Value & v, const PosIdx pos);
+ NixInt forceInt(Value & v, const PosIdx pos, std::string_view errorCtx);
+ NixFloat forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx);
+ bool forceBool(Value & v, const PosIdx pos, std::string_view errorCtx);
- void forceAttrs(Value & v, const PosIdx pos);
+ void forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx);
template <typename Callable>
- inline void forceAttrs(Value & v, Callable getPos);
-
- inline void forceList(Value & v, const PosIdx pos);
- void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
- std::string_view forceString(Value & v, const PosIdx pos = noPos);
- std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
- std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
-
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const char * s, const std::string & s2);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s, const std::string & s2);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const char * s, const std::string & s2,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const char * s, const std::string & s2, const std::string & s3,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const char * s, const std::string & s2, const std::string & s3);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string & s2,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2,
- Env & env, Expr & expr);
+ inline void forceAttrs(Value & v, Callable getPos, std::string_view errorCtx);
- [[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s, const Value & v);
- [[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s, const Value & v,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s);
- [[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2,
- Env & env, Expr & expr);
- [[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const char * s, const Value & v,
- Env & env, Expr & expr);
-
- [[gnu::noinline, gnu::noreturn]]
- void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1,
- Env & env, Expr & expr);
-
- [[gnu::noinline, gnu::noreturn]]
- void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1,
- Env & env, Expr & expr);
-
- [[gnu::noinline, gnu::noreturn]]
- void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1,
- Env & env, Expr & expr);
+ inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
+ void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx); // either lambda or primop
+ std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
+ std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx);
+ std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
[[gnu::noinline]]
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
[[gnu::noinline]]
- void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
+ void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame = false) const;
public:
/* Return true iff the value `v' denotes a derivation (i.e. a
@@ -396,6 +378,7 @@ public:
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
+ std::string_view errorCtx,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
@@ -404,10 +387,10 @@ public:
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
- Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
+ Path coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
/* Like coerceToPath, but the result must be a store path. */
- StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
+ StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
public:
@@ -467,7 +450,7 @@ public:
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
- bool eqValues(Value & v1, Value & v2);
+ bool eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx);
bool isFunctor(Value & fun);
@@ -502,7 +485,7 @@ public:
void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, PosIdx pos);
- void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
+ void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
/* Print statistics. */
void printStats();
@@ -665,6 +648,13 @@ extern EvalSettings evalSettings;
static const std::string corepkgsPrefix{"/__corepkgs__/"};
+template<class ErrorType>
+void ErrorBuilder::debugThrow()
+{
+ // NOTE: We always use the -LastTrace version as we push the new trace in withFrame()
+ state.debugThrowLastTrace(ErrorType(info));
+}
+
}
#include "eval-inline.hh"