aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/eval.hh
diff options
context:
space:
mode:
authoreldritch horrors <pennae@lix.systems>2024-03-08 07:09:48 +0100
committereldritch horrors <pennae@lix.systems>2024-03-09 04:47:05 -0700
commit08252967a8c2ab15f3fb8bdfb848f007d4032d50 (patch)
tree909ec8357654d4d179f0f8e7dfc5d3d623256b46 /src/libexpr/eval.hh
parentd4c738fe4c587c3f09b0fda899f419f2de97ee2f (diff)
libexpr: Support structured error classes
While preparing PRs like #9753, I've had to change error messages in dozens of code paths. It would be nice if instead of EvalError("expected 'boolean' but found '%1%'", showType(v)) we could write TypeError(v, "boolean") or similar. Then, changing the error message could be a mechanical refactor with the compiler pointing out places the constructor needs to be changed, rather than the error-prone process of grepping through the codebase. Structured errors would also help prevent the "same" error from having multiple slightly different messages, and could be a first step towards error codes / an error index. This PR reworks the exception infrastructure in `libexpr` to support exception types with different constructor signatures than `BaseError`. Actually refactoring the exceptions to use structured data will come in a future PR (this one is big enough already, as it has to touch every exception in `libexpr`). The core design is in `eval-error.hh`. Generally, errors like this: state.error("'%s' is not a string", getAttrPathStr()) .debugThrow<TypeError>() are transformed like this: state.error<TypeError>("'%s' is not a string", getAttrPathStr()) .debugThrow() The type annotation has moved from `ErrorBuilder::debugThrow` to `EvalState::error`. (cherry picked from commit c6a89c1a1659b31694c0fbcd21d78a6dd521c732) Change-Id: Iced91ba4e00ca9e801518071fb43798936cbd05a
Diffstat (limited to 'src/libexpr/eval.hh')
-rw-r--r--src/libexpr/eval.hh90
1 files changed, 4 insertions, 86 deletions
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 3f661e73e..830db4558 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -2,6 +2,7 @@
///@file
#include "attr-set.hh"
+#include "eval-error.hh"
#include "types.hh"
#include "value.hh"
#include "nixexpr.hh"
@@ -150,45 +151,6 @@ struct DebugTrace {
bool isError;
};
-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:
@@ -262,39 +224,10 @@ public:
void runDebugRepl(const Error * error, const Env & env, const Expr & expr);
- template<class E>
- [[gnu::noinline, gnu::noreturn]]
- void debugThrowLastTrace(E && error)
- {
- debugThrow(error, nullptr, nullptr);
- }
-
- template<class E>
- [[gnu::noinline, gnu::noreturn]]
- void debugThrow(E && error, const Env * env, const Expr * 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(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>
+ template<class T, typename... Args>
[[nodiscard, gnu::noinline]]
- ErrorBuilder & error(const Args & ... args) {
- errorBuilder = ErrorBuilder::create(*this, args...);
- return *errorBuilder;
+ EvalErrorBuilder<T> & error(const Args & ... args) {
+ return *new EvalErrorBuilder<T>(*this, args...);
}
private:
@@ -840,23 +773,8 @@ std::string showType(const Value & v);
*/
SourcePath resolveExprPath(SourcePath path);
-struct InvalidPathError : EvalError
-{
- Path path;
- InvalidPathError(const Path & path);
-#ifdef EXCEPTION_NEEDS_THROW_SPEC
- ~InvalidPathError() throw () { };
-#endif
-};
-
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));
-}
}