1 #include "Error.h"
2 #include "Introspection.h"
3 #include "Util.h"  // for get_env_variable
4 
5 #include <signal.h>
6 
7 #ifdef _MSC_VER
8 #include <io.h>
isatty(int fd)9 inline int isatty(int fd) {
10     return _isatty(fd);
11 }
12 #else
13 #include <unistd.h>
14 #endif
15 
16 namespace Halide {
17 
18 namespace {
19 
20 CompileTimeErrorReporter *custom_error_reporter = nullptr;
21 
error_abort()22 void error_abort() {
23 #ifdef _MSC_VER
24     const std::string s = Internal::get_env_variable("HL_DISABLE_WINDOWS_ABORT_DIALOG");
25     const int disable = !s.empty() ? atoi(s.c_str()) : 0;
26     if (disable) {
27         // Debug variants of the MSVC runtime will present an "Abort, Retry, Ignore"
28         // dialog in response to a call to abort(); we want to be able to disable this
29         // for (e.g.) buildbots, where we never want that behavior. This is a close approximation
30         // that will kill the process in a similar way.
31         // (Note that 3 is the exit code for the "abort" button.)
32         raise(SIGABRT);
33         exit(1);
34     }
35 #endif
36 
37     abort();
38 }
39 
40 }  // namespace
41 
set_custom_compile_time_error_reporter(CompileTimeErrorReporter * error_reporter)42 void set_custom_compile_time_error_reporter(CompileTimeErrorReporter *error_reporter) {
43     custom_error_reporter = error_reporter;
44 }
45 
exceptions_enabled()46 bool exceptions_enabled() {
47 #ifdef HALIDE_WITH_EXCEPTIONS
48     return true;
49 #else
50     return false;
51 #endif
52 }
53 
Error(const std::string & msg)54 Error::Error(const std::string &msg)
55     : std::runtime_error(msg) {
56 }
57 
CompileError(const std::string & msg)58 CompileError::CompileError(const std::string &msg)
59     : Error(msg) {
60 }
61 
RuntimeError(const std::string & msg)62 RuntimeError::RuntimeError(const std::string &msg)
63     : Error(msg) {
64 }
65 
InternalError(const std::string & msg)66 InternalError::InternalError(const std::string &msg)
67     : Error(msg) {
68 }
69 
70 namespace Internal {
71 
72 // Force the classes to exist, even if exceptions are off
73 namespace {
74 CompileError _compile_error("");
75 RuntimeError _runtime_error("");
76 InternalError _internal_error("");
77 }  // namespace
78 
ErrorReport(const char * file,int line,const char * condition_string,int flags)79 ErrorReport::ErrorReport(const char *file, int line, const char *condition_string, int flags)
80     : flags(flags) {
81 // Note that we deliberately try to put the entire message into a single line
82 // (aside from newlines inserted by user code) to make it easy to filter
83 // specific warnings or messages via (e.g.) grep.... unless we are likely to be
84 // outputting to a proper terminal, in which case newlines are used to improve readability.
85 #if defined(HALIDE_WITH_EXCEPTIONS)
86     const bool use_newlines = false;
87 #else
88     const bool use_newlines = (custom_error_reporter == nullptr) && isatty(2);
89 #endif
90     const char sep = use_newlines ? '\n' : ' ';
91 
92     const std::string &source_loc = Introspection::get_source_location();
93     const char *what = (flags & Warning) ? "Warning" : "Error";
94     if (flags & User) {
95         // Only mention where inside of libHalide the error tripped if we have debug level > 0
96         debug(1) << "User error triggered at " << file << ":" << line << "\n";
97         if (condition_string) {
98             debug(1) << "Condition failed: " << condition_string << "\n";
99         }
100         msg << what << ":";
101         if (!source_loc.empty()) {
102             msg << " (at " << source_loc << ")";
103         }
104         msg << sep;
105     } else {
106         msg << "Internal " << what << " at " << file << ":" << line;
107         if (source_loc.empty()) {
108             msg << " triggered by user code at " << source_loc << ":";
109         }
110         msg << sep;
111         if (condition_string) {
112             msg << "Condition failed: " << condition_string << ":" << sep;
113         }
114     }
115 }
116 
~ErrorReport()117 ErrorReport::~ErrorReport()
118 #if __cplusplus >= 201100 || _MSC_VER >= 1900
119     noexcept(false)
120 #endif
121 {
122     if (!msg.str().empty() && msg.str().back() != '\n') {
123         msg << "\n";
124     }
125 
126     if (custom_error_reporter != nullptr) {
127         if (flags & Warning) {
128             custom_error_reporter->warning(msg.str().c_str());
129             return;
130         } else {
131             custom_error_reporter->error(msg.str().c_str());
132             // error() should not have returned to us, but just in case
133             // it does, make sure we don't continue.
134             error_abort();
135         }
136     }
137 
138     // TODO: Add an option to error out on warnings too
139     if (flags & Warning) {
140         std::cerr << msg.str();
141         return;
142     }
143 
144 #ifdef HALIDE_WITH_EXCEPTIONS
145     if (std::uncaught_exception()) {
146         // This should never happen - evaluating one of the arguments
147         // to the error message would have to throw an
148         // exception. Nonetheless, in case it does, preserve the
149         // exception already in flight and suppress this one.
150         return;
151     } else if (flags & Runtime) {
152         RuntimeError err(msg.str());
153         throw err;
154     } else if (flags & User) {
155         CompileError err(msg.str());
156         throw err;
157     } else {
158         InternalError err(msg.str());
159         throw err;
160     }
161 #else
162     std::cerr << msg.str();
163     error_abort();
164 #endif
165 }
166 }  // namespace Internal
167 
168 }  // namespace Halide
169