Halide 21.0.0
Halide compiler and libraries
Loading...
Searching...
No Matches
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 * The following three diagnostic macros are implemented such that the
186 * message is evaluated only if the assertion's value is false.
187 *
188 * This (regrettably) requires a macro to work, but has the highly desirable
189 * effect that all assertion parameters are totally skipped (not ever evaluated)
190 * when the assertion is true.
191 *
192 * The macros work by deferring the call to issue() until after the stream
193 * has been evaluated. This previously used a trick where ErrorReport would
194 * throw in the destructor, but throwing in a destructor is UB in a lot of
195 * scenarios, and it was easy to break things by mistake.
196 */
197/// @{
198#define _halide_error_impl(type) \
199 for (Halide::Internal::ErrorReport<type> _err; 1; _err.issue()) \
200 /**/ _err.init(__FILE__, __FUNCTION__, __LINE__, nullptr)
201
202#define _halide_assert_impl(condition, type) \
203 if (!(condition)) \
204 for (Halide::Internal::ErrorReport<type> _err; 1; _err.issue()) \
205 /*****/ _err.init(__FILE__, __FUNCTION__, __LINE__, #condition)
206
207#define _halide_user_warning \
208 for (Halide::Internal::WarningReport _err; _err; _err.issue()) \
209 /**/ _err.init(__FILE__, __FUNCTION__, __LINE__, nullptr)
210/// @}
211
212#define user_warning _halide_user_warning
213
214#define user_error _halide_error_impl(Halide::CompileError)
215#define internal_error _halide_error_impl(Halide::InternalError)
216#define halide_runtime_error _halide_error_impl(Halide::RuntimeError)
217
218#define internal_assert(c) _halide_assert_impl(c, Halide::InternalError)
219#define user_assert(c) _halide_assert_impl(c, Halide::CompileError)
220
221// The nicely named versions get cleaned up at the end of Halide.h,
222// but user code might want to do halide-style user_asserts (e.g. the
223// Extern macros introduce calls to user_assert), so for that purpose
224// we define an equivalent macro that can be used outside of Halide.h
225#define _halide_user_error _halide_error_impl(Halide::CompileError)
226#define _halide_internal_error _halide_error_impl(Halide::InternalError)
227#define _halide_runtime_error _halide_error_impl(Halide::RuntimeError)
228#define _halide_internal_assert(c) _halide_assert_impl(c, Halide::InternalError)
229#define _halide_user_assert(c) _halide_assert_impl(c, Halide::CompileError)
230
231// N.B. Any function that might throw a user_assert or user_error may
232// not be inlined into the user's code, or the line number will be
233// misattributed to Halide.h. Either make such functions internal to
234// libHalide, or mark them as HALIDE_NO_USER_CODE_INLINE.
235
236// handler suitable for use with std::terminate; it will catch unhandled exceptions
237// and log the `what()` to stderr, then abort. Exposed as a function to minimize
238// the need for external code to need to know the definition of Halide::Error.
240
241} // namespace Internal
242
243} // namespace Halide
244
245#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
#define HALIDE_ALWAYS_INLINE
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 RuntimeError &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,...
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
static constexpr auto error_name
Definition Error.h:59
CompileError(const char *msg)
CompileError(const std::string &msg)
Error(Error &&) noexcept
Error(const std::string &msg)
Error(const char *msg)
Error()=delete
Error & operator=(const Error &)
Error(const Error &)
virtual const char * what() const noexcept
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
std::ostringstream msg
Definition Error.h:137
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
static constexpr auto error_name
Definition Error.h:69
InternalError(const std::string &msg)
InternalError(const char *msg)
An error that occurs while running a JIT-compiled Halide pipeline.
Definition Error.h:49
static constexpr auto error_name
Definition Error.h:50
RuntimeError(const char *msg)
RuntimeError(const std::string &msg)