1 //===-------- Error.h - Enforced error checking for ORC RT ------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 #define ORC_RT_ERROR_H
13 #include "extensible_rtti.h"
14 #include "stl_extras.h"
19 #include <type_traits>
23 /// Base class for all errors.
24 class ErrorInfoBase
: public RTTIExtends
<ErrorInfoBase
, RTTIRoot
> {
26 virtual std::string
toString() const = 0;
29 /// Represents an environmental error.
30 class ORC_RT_NODISCARD Error
{
32 template <typename ErrT
, typename
... ArgTs
>
33 friend Error
make_error(ArgTs
&&...Args
);
35 friend Error
repackage_error(std::unique_ptr
<ErrorInfoBase
>);
37 template <typename ErrT
> friend std::unique_ptr
<ErrT
> error_cast(Error
&);
39 template <typename T
> friend class Expected
;
42 /// Destroy this error. Aborts if error was not checked, or was checked but
44 ~Error() { assertIsChecked(); }
46 Error(const Error
&) = delete;
47 Error
&operator=(const Error
&) = delete;
49 /// Move-construct an error. The newly constructed error is considered
50 /// unchecked, even if the source error had been checked. The original error
51 /// becomes a checked success value.
52 Error(Error
&&Other
) {
54 *this = std::move(Other
);
57 /// Move-assign an error value. The current error must represent success, you
58 /// you cannot overwrite an unhandled error. The current error is then
59 /// considered unchecked. The source error becomes a checked success value,
60 /// regardless of its original state.
61 Error
&operator=(Error
&&Other
) {
62 // Don't allow overwriting of unchecked values.
64 setPtr(Other
.getPtr());
66 // This Error is unchecked, even if the source error was checked.
69 // Null out Other's payload and set its checked bit.
70 Other
.setPtr(nullptr);
71 Other
.setChecked(true);
76 /// Create a success value.
77 static Error
success() { return Error(); }
79 /// Error values convert to true for failure values, false otherwise.
80 explicit operator bool() {
81 setChecked(getPtr() == nullptr);
82 return getPtr() != nullptr;
85 /// Return true if this Error contains a failure value of the given type.
86 template <typename ErrT
> bool isA() const {
87 return getPtr() && getPtr()->isA
<ErrT
>();
93 Error(std::unique_ptr
<ErrorInfoBase
> ErrInfo
) {
94 auto RawErrPtr
= reinterpret_cast<uintptr_t>(ErrInfo
.release());
95 assert((RawErrPtr
& 0x1) == 0 && "ErrorInfo is insufficiently aligned");
96 ErrPtr
= RawErrPtr
| 0x1;
99 void assertIsChecked() {
100 if (ORC_RT_UNLIKELY(!isChecked() || getPtr())) {
101 fprintf(stderr
, "Error must be checked prior to destruction.\n");
102 abort(); // Some sort of JIT program abort?
106 template <typename ErrT
= ErrorInfoBase
> ErrT
*getPtr() const {
107 return reinterpret_cast<ErrT
*>(ErrPtr
& ~uintptr_t(1));
110 void setPtr(ErrorInfoBase
*Ptr
) {
111 ErrPtr
= (reinterpret_cast<uintptr_t>(Ptr
) & ~uintptr_t(1)) | (ErrPtr
& 1);
114 bool isChecked() const { return ErrPtr
& 0x1; }
116 void setChecked(bool Checked
) { ErrPtr
= (ErrPtr
& ~uintptr_t(1)) | Checked
; }
118 template <typename ErrT
= ErrorInfoBase
> std::unique_ptr
<ErrT
> takePayload() {
119 static_assert(std::is_base_of
<ErrorInfoBase
, ErrT
>::value
,
120 "ErrT is not an ErrorInfoBase subclass");
121 std::unique_ptr
<ErrT
> Tmp(getPtr
<ErrT
>());
127 uintptr_t ErrPtr
= 0;
130 /// Construct an error of ErrT with the given arguments.
131 template <typename ErrT
, typename
... ArgTs
> Error
make_error(ArgTs
&&...Args
) {
132 static_assert(std::is_base_of
<ErrorInfoBase
, ErrT
>::value
,
133 "ErrT is not an ErrorInfoBase subclass");
134 return Error(std::make_unique
<ErrT
>(std::forward
<ArgTs
>(Args
)...));
137 /// Construct an error of ErrT using a std::unique_ptr<ErrorInfoBase>. The
138 /// primary use-case for this is 're-packaging' errors after inspecting them
139 /// using error_cast, hence the name.
140 inline Error
repackage_error(std::unique_ptr
<ErrorInfoBase
> EIB
) {
141 return Error(std::move(EIB
));
144 /// If the argument is an error of type ErrT then this function unpacks it
145 /// and returns a std::unique_ptr<ErrT>. Otherwise returns a nullptr and
146 /// leaves the error untouched. Common usage looks like:
149 /// if (Error E = foo()) {
150 /// if (auto EV1 = error_cast<ErrorType1>(E)) {
151 /// // use unwrapped EV1 value.
152 /// } else if (EV2 = error_cast<ErrorType2>(E)) {
153 /// // use unwrapped EV2 value.
157 template <typename ErrT
> std::unique_ptr
<ErrT
> error_cast(Error
&Err
) {
158 static_assert(std::is_base_of
<ErrorInfoBase
, ErrT
>::value
,
159 "ErrT is not an ErrorInfoBase subclass");
161 return Err
.takePayload
<ErrT
>();
165 /// Helper for Errors used as out-parameters.
166 /// Sets the 'checked' flag on construction, resets it on destruction.
167 class ErrorAsOutParameter
{
169 ErrorAsOutParameter(Error
*Err
) : Err(Err
) {
170 // Raise the checked bit if Err is success.
175 ~ErrorAsOutParameter() {
176 // Clear the checked bit.
178 *Err
= Error::success();
185 template <typename T
> class ORC_RT_NODISCARD Expected
{
187 template <class OtherT
> friend class Expected
;
189 static constexpr bool IsRef
= std::is_reference
<T
>::value
;
190 using wrap
= std::reference_wrapper
<std::remove_reference_t
<T
>>;
191 using error_type
= std::unique_ptr
<ErrorInfoBase
>;
192 using storage_type
= std::conditional_t
<IsRef
, wrap
, T
>;
193 using value_type
= T
;
195 using reference
= std::remove_reference_t
<T
> &;
196 using const_reference
= const std::remove_reference_t
<T
> &;
197 using pointer
= std::remove_reference_t
<T
> *;
198 using const_pointer
= const std::remove_reference_t
<T
> *;
201 /// Create an Expected from a failure value.
202 Expected(Error Err
) : HasError(true), Unchecked(true) {
203 assert(Err
&& "Cannot create Expected<T> from Error success value");
204 new (getErrorStorage()) error_type(Err
.takePayload());
207 /// Create an Expected from a T value.
208 template <typename OtherT
>
209 Expected(OtherT
&&Val
,
210 std::enable_if_t
<std::is_convertible
<OtherT
, T
>::value
> * = nullptr)
211 : HasError(false), Unchecked(true) {
212 new (getStorage()) storage_type(std::forward
<OtherT
>(Val
));
215 /// Move-construct an Expected<T> from an Expected<OtherT>.
216 Expected(Expected
&&Other
) { moveConstruct(std::move(Other
)); }
218 /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
219 /// must be convertible to T.
220 template <class OtherT
>
222 Expected
<OtherT
> &&Other
,
223 std::enable_if_t
<std::is_convertible
<OtherT
, T
>::value
> * = nullptr) {
224 moveConstruct(std::move(Other
));
227 /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
228 /// isn't convertible to T.
229 template <class OtherT
>
231 Expected
<OtherT
> &&Other
,
232 std::enable_if_t
<!std::is_convertible
<OtherT
, T
>::value
> * = nullptr) {
233 moveConstruct(std::move(Other
));
236 /// Move-assign from another Expected<T>.
237 Expected
&operator=(Expected
&&Other
) {
238 moveAssign(std::move(Other
));
242 /// Destroy an Expected<T>.
246 getStorage()->~storage_type();
248 getErrorStorage()->~error_type();
251 /// Returns true if this Expected value is in a success state (holding a T),
252 /// and false if this Expected value is in a failure state.
253 explicit operator bool() {
254 Unchecked
= HasError
;
258 /// Returns true if this Expected value holds an Error of type error_type.
259 template <typename ErrT
> bool isFailureOfType() const {
260 return HasError
&& (*getErrorStorage())->template isFailureOfType
<ErrT
>();
263 /// Take ownership of the stored error.
265 /// If this Expected value is in a success state (holding a T) then this
266 /// method is a no-op and returns Error::success.
268 /// If thsi Expected value is in a failure state (holding an Error) then this
269 /// method returns the contained error and leaves this Expected in an
270 /// 'empty' state from which it may be safely destructed but not otherwise
274 return HasError
? Error(std::move(*getErrorStorage())) : Error::success();
277 /// Returns a pointer to the stored T value.
278 pointer
operator->() {
280 return toPointer(getStorage());
283 /// Returns a pointer to the stored T value.
284 const_pointer
operator->() const {
286 return toPointer(getStorage());
289 /// Returns a reference to the stored T value.
290 reference
operator*() {
292 return *getStorage();
295 /// Returns a reference to the stored T value.
296 const_reference
operator*() const {
298 return *getStorage();
303 static bool compareThisIfSameType(const T1
&a
, const T1
&b
) {
307 template <class T1
, class T2
>
308 static bool compareThisIfSameType(const T1
&a
, const T2
&b
) {
312 template <class OtherT
> void moveConstruct(Expected
<OtherT
> &&Other
) {
313 HasError
= Other
.HasError
;
315 Other
.Unchecked
= false;
318 new (getStorage()) storage_type(std::move(*Other
.getStorage()));
320 new (getErrorStorage()) error_type(std::move(*Other
.getErrorStorage()));
323 template <class OtherT
> void moveAssign(Expected
<OtherT
> &&Other
) {
326 if (compareThisIfSameType(*this, Other
))
330 new (this) Expected(std::move(Other
));
333 pointer
toPointer(pointer Val
) { return Val
; }
335 const_pointer
toPointer(const_pointer Val
) const { return Val
; }
337 pointer
toPointer(wrap
*Val
) { return &Val
->get(); }
339 const_pointer
toPointer(const wrap
*Val
) const { return &Val
->get(); }
341 storage_type
*getStorage() {
342 assert(!HasError
&& "Cannot get value when an error exists!");
343 return reinterpret_cast<storage_type
*>(&TStorage
);
346 const storage_type
*getStorage() const {
347 assert(!HasError
&& "Cannot get value when an error exists!");
348 return reinterpret_cast<const storage_type
*>(&TStorage
);
351 error_type
*getErrorStorage() {
352 assert(HasError
&& "Cannot get error when a value exists!");
353 return reinterpret_cast<error_type
*>(&ErrorStorage
);
356 const error_type
*getErrorStorage() const {
357 assert(HasError
&& "Cannot get error when a value exists!");
358 return reinterpret_cast<const error_type
*>(&ErrorStorage
);
361 void assertIsChecked() {
362 if (ORC_RT_UNLIKELY(Unchecked
)) {
364 "Expected<T> must be checked before access or destruction.\n");
370 std::aligned_union_t
<1, storage_type
> TStorage
;
371 std::aligned_union_t
<1, error_type
> ErrorStorage
;
378 /// Consume an error without doing anything.
379 inline void consumeError(Error Err
) {
381 (void)error_cast
<ErrorInfoBase
>(Err
);
384 /// Consumes success values. It is a programmatic error to call this function
385 /// on a failure value.
386 inline void cantFail(Error Err
) {
387 assert(!Err
&& "cantFail called on failure value");
388 consumeError(std::move(Err
));
391 /// Auto-unwrap an Expected<T> value in the success state. It is a programmatic
392 /// error to call this function on a failure value.
393 template <typename T
> T
cantFail(Expected
<T
> E
) {
394 assert(E
&& "cantFail called on failure value");
395 consumeError(E
.takeError());
396 return std::move(*E
);
399 /// Auto-unwrap an Expected<T> value in the success state. It is a programmatic
400 /// error to call this function on a failure value.
401 template <typename T
> T
&cantFail(Expected
<T
&> E
) {
402 assert(E
&& "cantFail called on failure value");
403 consumeError(E
.takeError());
407 /// Convert the given error to a string. The error value is consumed in the
409 inline std::string
toString(Error Err
) {
410 if (auto EIB
= error_cast
<ErrorInfoBase
>(Err
))
411 return EIB
->toString();
415 class StringError
: public RTTIExtends
<StringError
, ErrorInfoBase
> {
417 StringError(std::string ErrMsg
) : ErrMsg(std::move(ErrMsg
)) {}
418 std::string
toString() const override
{ return ErrMsg
; }
424 } // end namespace __orc_rt
426 #endif // ORC_RT_ERROR_H