Halide 20.0.0
Halide compiler and libraries
Error.h
Go to the documentation of this file.
1#ifndef HALIDE_ERROR_H
2#define HALIDE_ERROR_H
3
4#include <sstream>
5#include <stdexcept>
6
7#include "Debug.h"
8#include "runtime/HalideRuntime.h" // for HALIDE_ALWAYS_INLINE
9
10namespace Halide {
11
12/** Query whether Halide was compiled with exceptions. */
14
15/** A base class for Halide errors.
16 *
17 * Note that this deliberately does *not* descend from std::runtime_error, or
18 * even std::exception; unfortunately, std::runtime_error is not marked as
19 * DLLEXPORT on Windows, but Error needs to be marked as such, and mismatching
20 * DLLEXPORT annotations in a class inheritance hierarchy in this way can lead
21 * to ODR violations. Instead, we just attempt to replicate the API of
22 * runtime_error here. */
24 Error() = delete;
25
26 // Give each class a non-inlined constructor so that the type
27 // doesn't get separately instantiated in each compilation unit.
28 explicit Error(const char *msg);
29 explicit Error(const std::string &msg);
30
31 Error(const Error &);
33 Error(Error &&) noexcept;
34 Error &operator=(Error &&) noexcept;
35
36 virtual ~Error();
37
38 virtual const char *what() const noexcept;
39
40private:
41 // Using a std::string here will cause MSVC to complain about the fact
42 // that class std::string isn't declared DLLEXPORT, even though the
43 // field is private; rather than suppress the warning, we'll just use
44 // an old-fashioned new-and-delete to keep it nice and clean.
45 char *what_;
46};
47
48/** An error that occurs while running a JIT-compiled Halide pipeline. */
50 static constexpr auto error_name = "Runtime error";
51
52 explicit RuntimeError(const char *msg);
53 explicit RuntimeError(const std::string &msg);
54};
55
56/** An error that occurs while compiling a Halide pipeline that Halide
57 * attributes to a user error. */
59 static constexpr auto error_name = "User error";
60
61 explicit CompileError(const char *msg);
62 explicit CompileError(const std::string &msg);
63};
64
65/** An error that occurs while compiling a Halide pipeline that Halide
66 * attributes to an internal compiler bug, or to an invalid use of
67 * Halide's internals. */
69 static constexpr auto error_name = "Internal error";
70
71 explicit InternalError(const char *msg);
72 explicit InternalError(const std::string &msg);
73};
74
75/** CompileTimeErrorReporter is used at compile time (*not* runtime) when
76 * an error or warning is generated by Halide. Note that error() is called
77 * a fatal error has occurred, and returning to Halide may cause a crash;
78 * implementations of CompileTimeErrorReporter::error() should never return.
79 * (Implementations of CompileTimeErrorReporter::warning() may return but
80 * may also abort(), exit(), etc.)
81 */
83public:
84 virtual ~CompileTimeErrorReporter() = default;
85 virtual void warning(const char *msg) = 0;
86 [[noreturn]] virtual void error(const char *msg) = 0;
87};
88
89/** The default error reporter logs to stderr, then throws an exception
90 * (if HALIDE_WITH_EXCEPTIONS) or calls abort (if not). This allows customization
91 * of that behavior if a more gentle response to error reporting is desired.
92 * Note that error_reporter is expected to remain valid across all Halide usage;
93 * it is up to the caller to ensure that this is the case (and to do any
94 * cleanup necessary).
95 */
97
98namespace Internal {
99
100/**
101 * If a custom error reporter is configured, notifies the reporter by calling
102 * its error() function with the value of \p e.what()
103 *
104 * Otherwise, if Halide was built with exceptions, throw \p e unless an
105 * existing exception is in flight. On the other hand, if Halide was built
106 * without exceptions, print the error message to stderr and abort().
107 *
108 * @param e The error to throw or report
109 */
110/// @{
111[[noreturn]] void throw_error(const RuntimeError &e);
112[[noreturn]] void throw_error(const CompileError &e);
113[[noreturn]] void throw_error(const InternalError &e);
114/// @}
115
116/**
117 * If a custom error reporter is configured, notifies the reporter by calling
118 * its warning() function. Otherwise, prints the warning to stderr.
119 *
120 * @param warning The warning to issue
121 */
122void issue_warning(const char *warning);
123
124template<typename T>
126 template<typename S>
128 msg << x;
129 return *static_cast<T *>(this);
130 }
131
132 HALIDE_ALWAYS_INLINE operator bool() const {
133 return !finalized;
134 }
135
136protected:
137 std::ostringstream msg{};
138 bool finalized{false};
139
140 // This function is called as part of issue() below. We can't use a
141 // virtual function because issue() needs to be marked [[noreturn]]
142 // for errors and be left alone for warnings (i.e., they have
143 // different signatures).
144 std::string finalize_message() {
145 if (!msg.str().empty() && msg.str().back() != '\n') {
146 msg << "\n";
147 }
148 finalized = true;
149 return msg.str();
150 }
151
152 T &init(const char *file, const char *function, const int line, const char *condition_string, const char *prefix) {
153 if (debug_is_active_impl(1, file, function, line)) {
154 msg << prefix << " at " << file << ":" << line << ' ';
155 if (condition_string) {
156 msg << "Condition failed: " << condition_string << ' ';
157 }
158 }
159 return *static_cast<T *>(this);
160 }
161};
162
163template<typename Exception>
164struct ErrorReport final : ReportBase<ErrorReport<Exception>> {
165 ErrorReport &init(const char *file, const char *function, const int line, const char *condition_string) {
166 return ReportBase<ErrorReport>::init(file, function, line, condition_string, Exception::error_name) << "Error: ";
167 }
168
169 [[noreturn]] void issue() noexcept(false) {
170 throw_error(Exception(this->finalize_message()));
171 }
172};
173
174struct WarningReport final : ReportBase<WarningReport> {
175 WarningReport &init(const char *file, const char *function, const int line, const char *condition_string) {
176 return ReportBase::init(file, function, line, condition_string, "Warning") << "Warning: ";
177 }
178
179 void issue() {
180 issue_warning(this->finalize_message().c_str());
181 }
182};
183
184/**
185 * _halide_internal_diagnostic is used to implement our assertion macros
186 * in such a way that the messages output for the assertion are only
187 * evaluated if the assertion's value is false.
188 *
189 * This (regrettably) requires a macro to work, but has the highly desirable
190 * effect that all assertion parameters are totally skipped (not ever evaluated)
191 * when the assertion is true.
192 *
193 * The macro works by deferring the call to issue() until after the stream
194 * has been evaluated. This previously used a trick where ErrorReport would
195 * throw in the destructor, but throwing in a destructor is UB in a lot of
196 * scenarios, and it was easy to break things by mistake.
197 */
198// clang-format off
199#define _halide_internal_diagnostic(condition, type, condition_string) \
200 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
201 if (!(condition)) for (type _err; _err; _err.issue()) _err.init(__FILE__, __FUNCTION__, __LINE__, condition_string)
202// clang-format on
203
204#define _halide_internal_error(type) \
205 _halide_internal_diagnostic(0, Halide::Internal::ErrorReport<type>, nullptr)
206
207#define _halide_internal_assertion(condition, type) \
208 _halide_internal_diagnostic(condition, Halide::Internal::ErrorReport<type>, #condition)
209
210#define user_error _halide_internal_error(Halide::CompileError)
211#define internal_error _halide_internal_error(Halide::InternalError)
212#define halide_runtime_error _halide_internal_error(Halide::RuntimeError)
213
214#define user_warning _halide_internal_diagnostic(0, Halide::Internal::WarningReport, nullptr)
215
216#define internal_assert(c) _halide_internal_assertion(c, Halide::InternalError)
217#define user_assert(c) _halide_internal_assertion(c, Halide::CompileError)
218
219// The nicely named versions get cleaned up at the end of Halide.h,
220// but user code might want to do halide-style user_asserts (e.g. the
221// Extern macros introduce calls to user_assert), so for that purpose
222// we define an equivalent macro that can be used outside of Halide.h
223#define _halide_internal_assert(c) _halide_internal_assertion(c, Halide::InternalError)
224#define _halide_user_assert(c) _halide_internal_assertion(c, Halide::CompileError)
225
226// N.B. Any function that might throw a user_assert or user_error may
227// not be inlined into the user's code, or the line number will be
228// misattributed to Halide.h. Either make such functions internal to
229// libHalide, or mark them as HALIDE_NO_USER_CODE_INLINE.
230
231// handler suitable for use with std::terminate; it will catch unhandled exceptions
232// and log the `what()` to stderr, then abort. Exposed as a function to minimize
233// the need for external code to need to know the definition of Halide::Error.
235
236} // namespace Internal
237
238} // namespace Halide
239
240#endif
Defines functions for debug logging during code generation.
This file declares the routines used by Halide internally in its runtime.
#define HALIDE_EXPORT_SYMBOL
Definition: HalideRuntime.h:83
#define HALIDE_ALWAYS_INLINE
Definition: HalideRuntime.h:49
CompileTimeErrorReporter is used at compile time (not runtime) when an error or warning is generated ...
Definition: Error.h:82
virtual void warning(const char *msg)=0
virtual void error(const char *msg)=0
virtual ~CompileTimeErrorReporter()=default
void throw_error(const InternalError &e)
If a custom error reporter is configured, notifies the reporter by calling its error() function with ...
void issue_warning(const char *warning)
If a custom error reporter is configured, notifies the reporter by calling its warning() function.
HALIDE_EXPORT_SYMBOL void unhandled_exception_handler()
bool debug_is_active_impl(int verbosity, const char *file, const char *function, int line)
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
@ Internal
Not visible externally, similar to 'static' linkage in C.
bool exceptions_enabled()
Query whether Halide was compiled with exceptions.
void set_custom_compile_time_error_reporter(CompileTimeErrorReporter *error_reporter)
The default error reporter logs to stderr, then throws an exception (if HALIDE_WITH_EXCEPTIONS) or ca...
An error that occurs while compiling a Halide pipeline that Halide attributes to a user error.
Definition: Error.h:58
CompileError(const char *msg)
CompileError(const std::string &msg)
A base class for Halide errors.
Definition: Error.h:23
Error(Error &&) noexcept
Error(const std::string &msg)
Error(const char *msg)
Error()=delete
Error & operator=(const Error &)
Error(const Error &)
void issue() noexcept(false)
Definition: Error.h:169
ErrorReport & init(const char *file, const char *function, const int line, const char *condition_string)
Definition: Error.h:165
std::string finalize_message()
Definition: Error.h:144
T & init(const char *file, const char *function, const int line, const char *condition_string, const char *prefix)
Definition: Error.h:152
HALIDE_ALWAYS_INLINE T & operator<<(const S &x)
Definition: Error.h:127
WarningReport & init(const char *file, const char *function, const int line, const char *condition_string)
Definition: Error.h:175
An error that occurs while compiling a Halide pipeline that Halide attributes to an internal compiler...
Definition: Error.h:68
InternalError(const std::string &msg)
InternalError(const char *msg)
An error that occurs while running a JIT-compiled Halide pipeline.
Definition: Error.h:49
RuntimeError(const char *msg)
RuntimeError(const std::string &msg)