Skip to content

Commit 908d79a

Browse files
steven-johnsonardier
authored andcommitted
Halide::Error should not extend std::runtime_error (halide#6927)
* Halide::Error should not extend std::runtime_error Unfortunately, the std error/exception classes aren't marked for DLLEXPORT under MSVC; we need our Error classes to be DLLEXPORT for libHalide (and python bindings). The current situation basically causes MSVC to generator another version of `std::runtime_error` marked for DLLEXPORT, which can lead to ODR violations, which are bad. AFAICT we don't really rely on this inheritance anywhere, so this just eliminates the inheritance entirely. (Note that I can't point to a specific malfunction resulting from this, but casual googling based on the many warnings MSVC emits about the current situation has me convinced that it needs addressing.) * noexcept
1 parent 3b7f3fb commit 908d79a

3 files changed

Lines changed: 96 additions & 13 deletions

File tree

src/Error.cpp

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,48 @@ bool exceptions_enabled() {
3333
#endif
3434
}
3535

36+
Error::Error(const char *msg)
37+
: what_(new char[strlen(msg) + 1]) {
38+
strcpy(what_, msg);
39+
}
40+
3641
Error::Error(const std::string &msg)
37-
: std::runtime_error(msg) {
42+
: Error(msg.c_str()) {
43+
}
44+
45+
Error::Error(const Error &that)
46+
: Error(that.what_) {
47+
}
48+
49+
Error &Error::operator=(const Error &that) {
50+
if (this != &that) {
51+
delete[] this->what_;
52+
this->what_ = new char[strlen(that.what_) + 1];
53+
strcpy(this->what_, that.what_);
54+
}
55+
return *this;
56+
}
57+
58+
Error::Error(Error &&that) noexcept {
59+
this->what_ = that.what_;
60+
that.what_ = nullptr;
61+
}
62+
63+
Error &Error::operator=(Error &&that) noexcept {
64+
if (this != &that) {
65+
delete[] this->what_;
66+
this->what_ = that.what_;
67+
that.what_ = nullptr;
68+
}
69+
return *this;
70+
}
71+
72+
Error::~Error() {
73+
delete[] what_;
74+
}
75+
76+
const char *Error::what() const noexcept {
77+
return what_;
3878
}
3979

4080
CompileError::CompileError(const std::string &msg)
@@ -49,6 +89,18 @@ InternalError::InternalError(const std::string &msg)
4989
: Error(msg) {
5090
}
5191

92+
CompileError::CompileError(const char *msg)
93+
: Error(msg) {
94+
}
95+
96+
RuntimeError::RuntimeError(const char *msg)
97+
: Error(msg) {
98+
}
99+
100+
InternalError::InternalError(const char *msg)
101+
: Error(msg) {
102+
}
103+
52104
namespace Internal {
53105

54106
// Force the classes to exist, even if exceptions are off
@@ -96,11 +148,7 @@ ErrorReport::ErrorReport(const char *file, int line, const char *condition_strin
96148
}
97149
}
98150

99-
ErrorReport::~ErrorReport()
100-
#if __cplusplus >= 201100 || _MSC_VER >= 1900
101-
noexcept(false)
102-
#endif
103-
{
151+
ErrorReport::~ErrorReport() noexcept(false) {
104152
if (!msg.str().empty() && msg.str().back() != '\n') {
105153
msg << "\n";
106154
}

src/Error.h

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,58 @@ namespace Halide {
1212
/** Query whether Halide was compiled with exceptions. */
1313
bool exceptions_enabled();
1414

15-
/** A base class for Halide errors. */
16-
struct HALIDE_EXPORT_SYMBOL Error : public std::runtime_error {
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. */
23+
struct HALIDE_EXPORT_SYMBOL Error {
24+
Error() = delete;
25+
1726
// Give each class a non-inlined constructor so that the type
1827
// doesn't get separately instantiated in each compilation unit.
19-
Error(const std::string &msg);
28+
explicit Error(const char *msg);
29+
explicit Error(const std::string &msg);
30+
31+
Error(const Error &);
32+
Error &operator=(const Error &);
33+
Error(Error &&) noexcept;
34+
Error &operator=(Error &&) noexcept;
35+
36+
virtual ~Error();
37+
38+
virtual const char *what() const noexcept;
39+
40+
private:
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_;
2046
};
2147

2248
/** An error that occurs while running a JIT-compiled Halide pipeline. */
2349
struct HALIDE_EXPORT_SYMBOL RuntimeError : public Error {
24-
RuntimeError(const std::string &msg);
50+
explicit RuntimeError(const char *msg);
51+
explicit RuntimeError(const std::string &msg);
2552
};
2653

2754
/** An error that occurs while compiling a Halide pipeline that Halide
2855
* attributes to a user error. */
2956
struct HALIDE_EXPORT_SYMBOL CompileError : public Error {
30-
CompileError(const std::string &msg);
57+
explicit CompileError(const char *msg);
58+
explicit CompileError(const std::string &msg);
3159
};
3260

3361
/** An error that occurs while compiling a Halide pipeline that Halide
3462
* attributes to an internal compiler bug, or to an invalid use of
3563
* Halide's internals. */
3664
struct HALIDE_EXPORT_SYMBOL InternalError : public Error {
37-
InternalError(const std::string &msg);
65+
explicit InternalError(const char *msg);
66+
explicit InternalError(const std::string &msg);
3867
};
3968

4069
/** CompileTimeErrorReporter is used at compile time (*not* runtime) when

src/Generator.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1044,9 +1044,15 @@ namespace Internal {
10441044
int generate_filter_main(int argc, char **argv, const GeneratorFactoryProvider &generator_factory_provider) {
10451045
try {
10461046
return generate_filter_main_inner(argc, argv, generator_factory_provider);
1047-
} catch (std::runtime_error &err) {
1047+
} catch (::Halide::Error &err) {
10481048
user_error << "Unhandled exception: " << err.what() << "\n";
10491049
return -1;
1050+
} catch (std::exception &err) {
1051+
user_error << "Unhandled exception: " << err.what() << "\n";
1052+
return -1;
1053+
} catch (...) {
1054+
user_error << "Unhandled exception: (unknown)\n";
1055+
return -1;
10501056
}
10511057
}
10521058
#else

0 commit comments

Comments
 (0)