aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/eval-error.hh
blob: 1faf76781d2b027df89335239e7fd2ea2162ac83 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#pragma once

#include <algorithm>

#include "error.hh"
#include "pos-idx.hh"

namespace nix {

struct Env;
struct Expr;
struct Value;

class EvalState;
template<class T>
class EvalErrorBuilder;

class EvalError : public Error
{
    template<class T>
    friend class EvalErrorBuilder;
public:
    EvalState & state;

    EvalError(EvalState & state, ErrorInfo && errorInfo)
        : Error(errorInfo)
        , state(state)
    {
    }

    template<typename... Args>
    explicit EvalError(EvalState & state, const std::string & formatString, const Args &... formatArgs)
        : Error(formatString, formatArgs...)
        , state(state)
    {
    }
};

MakeError(ParseError, Error);
MakeError(AssertionError, EvalError);
MakeError(ThrownError, AssertionError);
MakeError(Abort, EvalError);
MakeError(TypeError, EvalError);
MakeError(UndefinedVarError, EvalError);
MakeError(MissingArgumentError, EvalError);
MakeError(RestrictedPathError, Error);
MakeError(CachedEvalError, EvalError);
MakeError(InfiniteRecursionError, EvalError);

struct InvalidPathError : public EvalError
{
public:
    Path path;
    InvalidPathError(EvalState & state, const Path & path)
        : EvalError(state, "path '%s' is not valid", path)
    {
    }
};

template<class T>
class EvalErrorBuilder final
{
    friend class EvalState;

    template<typename... Args>
    explicit EvalErrorBuilder(EvalState & state, const Args &... args)
        : error(T(state, args...))
    {
    }

public:
    T error;

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withExitStatus(unsigned int exitStatus);

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & atPos(PosIdx pos);

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & atPos(Value & value, PosIdx fallback = noPos);

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withTrace(PosIdx pos, const std::string_view text);

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrameTrace(PosIdx pos, const std::string_view text);

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withSuggestions(Suggestions & s);

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex);

    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, hintformat hint, bool frame = false);

    template<typename... Args>
    [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> &
    addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs);

    [[gnu::noinline, gnu::noreturn]] void debugThrow();
};

/**
 * The size needed to allocate any `EvalErrorBuilder<T>`.
 *
 * The list of classes here needs to be kept in sync with the list of `template
 * class` declarations in `eval-error.cc`.
 *
 * This is used by `EvalState` to preallocate a buffer of sufficient size for
 * any `EvalErrorBuilder<T>` to avoid allocating while evaluating Nix code.
 */
constexpr size_t EVAL_ERROR_BUILDER_SIZE = std::max({
    sizeof(EvalErrorBuilder<EvalError>),
    sizeof(EvalErrorBuilder<AssertionError>),
    sizeof(EvalErrorBuilder<ThrownError>),
    sizeof(EvalErrorBuilder<Abort>),
    sizeof(EvalErrorBuilder<TypeError>),
    sizeof(EvalErrorBuilder<UndefinedVarError>),
    sizeof(EvalErrorBuilder<MissingArgumentError>),
    sizeof(EvalErrorBuilder<InfiniteRecursionError>),
    sizeof(EvalErrorBuilder<CachedEvalError>),
    sizeof(EvalErrorBuilder<InvalidPathError>),
});

}