| 1 | //===-------- error.h - Enforced error checking for ORC RT ------*- C++ -*-===// |
| 2 | // |
| 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 |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef ORC_RT_ERROR_H |
| 10 | #define ORC_RT_ERROR_H |
| 11 | |
| 12 | #include "compiler.h" |
| 13 | #include "rtti.h" |
| 14 | #include "stl_extras.h" |
| 15 | |
| 16 | #include <cassert> |
| 17 | #include <memory> |
| 18 | #include <string> |
| 19 | #include <type_traits> |
| 20 | |
| 21 | namespace orc_rt { |
| 22 | |
| 23 | /// Base class for all errors. |
| 24 | class ErrorInfoBase : public RTTIExtends<ErrorInfoBase, RTTIRoot> { |
| 25 | public: |
| 26 | virtual std::string toString() const = 0; |
| 27 | }; |
| 28 | |
| 29 | /// Represents an environmental error. |
| 30 | class ORC_RT_NODISCARD Error { |
| 31 | |
| 32 | template <typename ErrT, typename... ArgTs> |
| 33 | friend Error make_error(ArgTs &&...Args); |
| 34 | |
| 35 | friend Error repackage_error(std::unique_ptr<ErrorInfoBase>); |
| 36 | |
| 37 | template <typename ErrT> friend std::unique_ptr<ErrT> error_cast(Error &); |
| 38 | |
| 39 | template <typename T> friend class Expected; |
| 40 | |
| 41 | public: |
| 42 | /// Destroy this error. Aborts if error was not checked, or was checked but |
| 43 | /// not handled. |
| 44 | ~Error() { assertIsChecked(); } |
| 45 | |
| 46 | Error(const Error &) = delete; |
| 47 | Error &operator=(const Error &) = delete; |
| 48 | |
| 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) { |
| 53 | setChecked(true); |
| 54 | *this = std::move(t&: Other); |
| 55 | } |
| 56 | |
| 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. |
| 63 | assertIsChecked(); |
| 64 | setPtr(Other.getPtr()); |
| 65 | |
| 66 | // This Error is unchecked, even if the source error was checked. |
| 67 | setChecked(false); |
| 68 | |
| 69 | // Null out Other's payload and set its checked bit. |
| 70 | Other.setPtr(nullptr); |
| 71 | Other.setChecked(true); |
| 72 | |
| 73 | return *this; |
| 74 | } |
| 75 | |
| 76 | /// Create a success value. |
| 77 | static Error success() { return Error(); } |
| 78 | |
| 79 | /// Error values convert to true for failure values, false otherwise. |
| 80 | explicit operator bool() { |
| 81 | setChecked(getPtr() == nullptr); |
| 82 | return getPtr() != nullptr; |
| 83 | } |
| 84 | |
| 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>(); |
| 88 | } |
| 89 | |
| 90 | private: |
| 91 | Error() = default; |
| 92 | |
| 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; |
| 97 | } |
| 98 | |
| 99 | void assertIsChecked() { |
| 100 | if (ORC_RT_UNLIKELY(!isChecked() || getPtr())) { |
| 101 | fprintf(stderr, format: "Error must be checked prior to destruction.\n" ); |
| 102 | abort(); // Some sort of JIT program abort? |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | template <typename ErrT = ErrorInfoBase> ErrT *getPtr() const { |
| 107 | return reinterpret_cast<ErrT *>(ErrPtr & ~uintptr_t(1)); |
| 108 | } |
| 109 | |
| 110 | void setPtr(ErrorInfoBase *Ptr) { |
| 111 | ErrPtr = (reinterpret_cast<uintptr_t>(Ptr) & ~uintptr_t(1)) | (ErrPtr & 1); |
| 112 | } |
| 113 | |
| 114 | bool isChecked() const { return ErrPtr & 0x1; } |
| 115 | |
| 116 | void setChecked(bool Checked) { ErrPtr = (ErrPtr & ~uintptr_t(1)) | Checked; } |
| 117 | |
| 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>()); |
| 122 | setPtr(nullptr); |
| 123 | setChecked(true); |
| 124 | return Tmp; |
| 125 | } |
| 126 | |
| 127 | uintptr_t ErrPtr = 0; |
| 128 | }; |
| 129 | |
| 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)...)); |
| 135 | } |
| 136 | |
| 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(t&: EIB)); |
| 142 | } |
| 143 | |
| 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: |
| 147 | /// |
| 148 | /// \code{.cpp} |
| 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. |
| 154 | /// } ... |
| 155 | /// } |
| 156 | /// \endcode |
| 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" ); |
| 160 | if (Err.isA<ErrT>()) |
| 161 | return Err.takePayload<ErrT>(); |
| 162 | return nullptr; |
| 163 | } |
| 164 | |
| 165 | /// Helper for Errors used as out-parameters. |
| 166 | /// Sets the 'checked' flag on construction, resets it on destruction. |
| 167 | class ErrorAsOutParameter { |
| 168 | public: |
| 169 | ErrorAsOutParameter(Error *Err) : Err(Err) { |
| 170 | // Raise the checked bit if Err is success. |
| 171 | if (Err) |
| 172 | (void)!!*Err; |
| 173 | } |
| 174 | |
| 175 | ~ErrorAsOutParameter() { |
| 176 | // Clear the checked bit. |
| 177 | if (Err && !*Err) |
| 178 | *Err = Error::success(); |
| 179 | } |
| 180 | |
| 181 | private: |
| 182 | Error *Err; |
| 183 | }; |
| 184 | |
| 185 | template <typename T> class ORC_RT_NODISCARD Expected { |
| 186 | |
| 187 | template <class OtherT> friend class Expected; |
| 188 | |
| 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; |
| 194 | |
| 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> *; |
| 199 | |
| 200 | public: |
| 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()); |
| 205 | } |
| 206 | |
| 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)); |
| 213 | } |
| 214 | |
| 215 | /// Move-construct an Expected<T> from an Expected<OtherT>. |
| 216 | Expected(Expected &&Other) { moveConstruct(std::move(Other)); } |
| 217 | |
| 218 | /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT |
| 219 | /// must be convertible to T. |
| 220 | template <class OtherT> |
| 221 | Expected( |
| 222 | Expected<OtherT> &&Other, |
| 223 | std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) { |
| 224 | moveConstruct(std::move(Other)); |
| 225 | } |
| 226 | |
| 227 | /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT |
| 228 | /// isn't convertible to T. |
| 229 | template <class OtherT> |
| 230 | explicit Expected( |
| 231 | Expected<OtherT> &&Other, |
| 232 | std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) { |
| 233 | moveConstruct(std::move(Other)); |
| 234 | } |
| 235 | |
| 236 | /// Move-assign from another Expected<T>. |
| 237 | Expected &operator=(Expected &&Other) { |
| 238 | moveAssign(std::move(Other)); |
| 239 | return *this; |
| 240 | } |
| 241 | |
| 242 | /// Destroy an Expected<T>. |
| 243 | ~Expected() { |
| 244 | assertIsChecked(); |
| 245 | if (!HasError) |
| 246 | getStorage()->~storage_type(); |
| 247 | else |
| 248 | getErrorStorage()->~error_type(); |
| 249 | } |
| 250 | |
| 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; |
| 255 | return !HasError; |
| 256 | } |
| 257 | |
| 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>(); |
| 261 | } |
| 262 | |
| 263 | /// Take ownership of the stored error. |
| 264 | /// |
| 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. |
| 267 | /// |
| 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 |
| 271 | /// accessed. |
| 272 | Error takeError() { |
| 273 | Unchecked = false; |
| 274 | return HasError ? Error(std::move(*getErrorStorage())) : Error::success(); |
| 275 | } |
| 276 | |
| 277 | /// Returns a pointer to the stored T value. |
| 278 | pointer operator->() { |
| 279 | assertIsChecked(); |
| 280 | return toPointer(getStorage()); |
| 281 | } |
| 282 | |
| 283 | /// Returns a pointer to the stored T value. |
| 284 | const_pointer operator->() const { |
| 285 | assertIsChecked(); |
| 286 | return toPointer(getStorage()); |
| 287 | } |
| 288 | |
| 289 | /// Returns a reference to the stored T value. |
| 290 | reference operator*() { |
| 291 | assertIsChecked(); |
| 292 | return *getStorage(); |
| 293 | } |
| 294 | |
| 295 | /// Returns a reference to the stored T value. |
| 296 | const_reference operator*() const { |
| 297 | assertIsChecked(); |
| 298 | return *getStorage(); |
| 299 | } |
| 300 | |
| 301 | private: |
| 302 | template <class T1> |
| 303 | static bool compareThisIfSameType(const T1 &a, const T1 &b) { |
| 304 | return &a == &b; |
| 305 | } |
| 306 | |
| 307 | template <class T1, class T2> |
| 308 | static bool compareThisIfSameType(const T1 &a, const T2 &b) { |
| 309 | return false; |
| 310 | } |
| 311 | |
| 312 | template <class OtherT> void moveConstruct(Expected<OtherT> &&Other) { |
| 313 | HasError = Other.HasError; |
| 314 | Unchecked = true; |
| 315 | Other.Unchecked = false; |
| 316 | |
| 317 | if (!HasError) |
| 318 | new (getStorage()) storage_type(std::move(*Other.getStorage())); |
| 319 | else |
| 320 | new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage())); |
| 321 | } |
| 322 | |
| 323 | template <class OtherT> void moveAssign(Expected<OtherT> &&Other) { |
| 324 | assertIsChecked(); |
| 325 | |
| 326 | if (compareThisIfSameType(*this, Other)) |
| 327 | return; |
| 328 | |
| 329 | this->~Expected(); |
| 330 | new (this) Expected(std::move(Other)); |
| 331 | } |
| 332 | |
| 333 | pointer toPointer(pointer Val) { return Val; } |
| 334 | |
| 335 | const_pointer toPointer(const_pointer Val) const { return Val; } |
| 336 | |
| 337 | pointer toPointer(wrap *Val) { return &Val->get(); } |
| 338 | |
| 339 | const_pointer toPointer(const wrap *Val) const { return &Val->get(); } |
| 340 | |
| 341 | storage_type *getStorage() { |
| 342 | assert(!HasError && "Cannot get value when an error exists!" ); |
| 343 | return reinterpret_cast<storage_type *>(&TStorage); |
| 344 | } |
| 345 | |
| 346 | const storage_type *getStorage() const { |
| 347 | assert(!HasError && "Cannot get value when an error exists!" ); |
| 348 | return reinterpret_cast<const storage_type *>(&TStorage); |
| 349 | } |
| 350 | |
| 351 | error_type *getErrorStorage() { |
| 352 | assert(HasError && "Cannot get error when a value exists!" ); |
| 353 | return reinterpret_cast<error_type *>(&ErrorStorage); |
| 354 | } |
| 355 | |
| 356 | const error_type *getErrorStorage() const { |
| 357 | assert(HasError && "Cannot get error when a value exists!" ); |
| 358 | return reinterpret_cast<const error_type *>(&ErrorStorage); |
| 359 | } |
| 360 | |
| 361 | void assertIsChecked() { |
| 362 | if (ORC_RT_UNLIKELY(Unchecked)) { |
| 363 | fprintf(stderr, |
| 364 | format: "Expected<T> must be checked before access or destruction.\n" ); |
| 365 | abort(); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | union { |
| 370 | alignas(storage_type) char TStorage[sizeof(storage_type)]; |
| 371 | alignas(error_type) char ErrorStorage[sizeof(error_type)]; |
| 372 | }; |
| 373 | |
| 374 | bool HasError : 1; |
| 375 | bool Unchecked : 1; |
| 376 | }; |
| 377 | |
| 378 | /// Consume an error without doing anything. |
| 379 | inline void consumeError(Error Err) { |
| 380 | if (Err) |
| 381 | (void)error_cast<ErrorInfoBase>(Err); |
| 382 | } |
| 383 | |
| 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(Err: std::move(t&: Err)); |
| 389 | } |
| 390 | |
| 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); |
| 397 | } |
| 398 | |
| 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()); |
| 404 | return *E; |
| 405 | } |
| 406 | |
| 407 | /// Convert the given error to a string. The error value is consumed in the |
| 408 | /// process. |
| 409 | inline std::string toString(Error Err) { |
| 410 | if (auto EIB = error_cast<ErrorInfoBase>(Err)) |
| 411 | return EIB->toString(); |
| 412 | return {}; |
| 413 | } |
| 414 | |
| 415 | class StringError : public RTTIExtends<StringError, ErrorInfoBase> { |
| 416 | public: |
| 417 | StringError(std::string ErrMsg) : ErrMsg(std::move(t&: ErrMsg)) {} |
| 418 | std::string toString() const override { return ErrMsg; } |
| 419 | |
| 420 | private: |
| 421 | std::string ErrMsg; |
| 422 | }; |
| 423 | |
| 424 | } // namespace orc_rt |
| 425 | |
| 426 | #endif // ORC_RT_ERROR_H |
| 427 | |