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