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 "extensible_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(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, "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) { 117 ErrPtr = (reinterpret_cast<uintptr_t>(ErrPtr) & ~uintptr_t(1)) | Checked; 118 } 119 120 template <typename ErrT = ErrorInfoBase> std::unique_ptr<ErrT> takePayload() { 121 static_assert(std::is_base_of<ErrorInfoBase, ErrT>::value, 122 "ErrT is not an ErrorInfoBase subclass"); 123 std::unique_ptr<ErrT> Tmp(getPtr<ErrT>()); 124 setPtr(nullptr); 125 setChecked(true); 126 return Tmp; 127 } 128 129 uintptr_t ErrPtr = 0; 130 }; 131 132 /// Construct an error of ErrT with the given arguments. 133 template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&...Args) { 134 static_assert(std::is_base_of<ErrorInfoBase, ErrT>::value, 135 "ErrT is not an ErrorInfoBase subclass"); 136 return Error(std::make_unique<ErrT>(std::forward<ArgTs>(Args)...)); 137 } 138 139 /// Construct an error of ErrT using a std::unique_ptr<ErrorInfoBase>. The 140 /// primary use-case for this is 're-packaging' errors after inspecting them 141 /// using error_cast, hence the name. 142 inline Error repackage_error(std::unique_ptr<ErrorInfoBase> EIB) { 143 return Error(std::move(EIB)); 144 } 145 146 /// If the argument is an error of type ErrT then this function unpacks it 147 /// and returns a std::unique_ptr<ErrT>. Otherwise returns a nullptr and 148 /// leaves the error untouched. Common usage looks like: 149 /// 150 /// \code{.cpp} 151 /// if (Error E = foo()) { 152 /// if (auto EV1 = error_cast<ErrorType1>(E)) { 153 /// // use unwrapped EV1 value. 154 /// } else if (EV2 = error_cast<ErrorType2>(E)) { 155 /// // use unwrapped EV2 value. 156 /// } ... 157 /// } 158 /// \endcode 159 template <typename ErrT> std::unique_ptr<ErrT> error_cast(Error &Err) { 160 static_assert(std::is_base_of<ErrorInfoBase, ErrT>::value, 161 "ErrT is not an ErrorInfoBase subclass"); 162 if (Err.isA<ErrT>()) 163 return Err.takePayload<ErrT>(); 164 return nullptr; 165 } 166 167 /// Helper for Errors used as out-parameters. 168 /// Sets the 'checked' flag on construction, resets it on destruction. 169 class ErrorAsOutParameter { 170 public: 171 ErrorAsOutParameter(Error *Err) : Err(Err) { 172 // Raise the checked bit if Err is success. 173 if (Err) 174 (void)!!*Err; 175 } 176 177 ~ErrorAsOutParameter() { 178 // Clear the checked bit. 179 if (Err && !*Err) 180 *Err = Error::success(); 181 } 182 183 private: 184 Error *Err; 185 }; 186 187 template <typename T> class ORC_RT_NODISCARD Expected { 188 189 template <class OtherT> friend class Expected; 190 191 static constexpr bool IsRef = std::is_reference<T>::value; 192 using wrap = std::reference_wrapper<std::remove_reference_t<T>>; 193 using error_type = std::unique_ptr<ErrorInfoBase>; 194 using storage_type = std::conditional_t<IsRef, wrap, T>; 195 using value_type = T; 196 197 using reference = std::remove_reference_t<T> &; 198 using const_reference = const std::remove_reference_t<T> &; 199 using pointer = std::remove_reference_t<T> *; 200 using const_pointer = const std::remove_reference_t<T> *; 201 202 public: 203 /// Create an Expected from a failure value. 204 Expected(Error Err) : HasError(true), Unchecked(true) { 205 assert(Err && "Cannot create Expected<T> from Error success value"); 206 new (getErrorStorage()) error_type(Err.takePayload()); 207 } 208 209 /// Create an Expected from a T value. 210 template <typename OtherT> 211 Expected(OtherT &&Val, 212 std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) 213 : HasError(false), Unchecked(true) { 214 new (getStorage()) storage_type(std::forward<OtherT>(Val)); 215 } 216 217 /// Move-construct an Expected<T> from an Expected<OtherT>. 218 Expected(Expected &&Other) { moveConstruct(std::move(Other)); } 219 220 /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT 221 /// must be convertible to T. 222 template <class OtherT> 223 Expected( 224 Expected<OtherT> &&Other, 225 std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) { 226 moveConstruct(std::move(Other)); 227 } 228 229 /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT 230 /// isn't convertible to T. 231 template <class OtherT> 232 explicit Expected( 233 Expected<OtherT> &&Other, 234 std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) { 235 moveConstruct(std::move(Other)); 236 } 237 238 /// Move-assign from another Expected<T>. 239 Expected &operator=(Expected &&Other) { 240 moveAssign(std::move(Other)); 241 return *this; 242 } 243 244 /// Destroy an Expected<T>. 245 ~Expected() { 246 assertIsChecked(); 247 if (!HasError) 248 getStorage()->~storage_type(); 249 else 250 getErrorStorage()->~error_type(); 251 } 252 253 /// Returns true if this Expected value is in a success state (holding a T), 254 /// and false if this Expected value is in a failure state. 255 explicit operator bool() { 256 Unchecked = HasError; 257 return !HasError; 258 } 259 260 /// Returns true if this Expected value holds an Error of type error_type. 261 template <typename ErrT> bool isFailureOfType() const { 262 return HasError && (*getErrorStorage())->template isFailureOfType<ErrT>(); 263 } 264 265 /// Take ownership of the stored error. 266 /// 267 /// If this Expected value is in a success state (holding a T) then this 268 /// method is a no-op and returns Error::success. 269 /// 270 /// If thsi Expected value is in a failure state (holding an Error) then this 271 /// method returns the contained error and leaves this Expected in an 272 /// 'empty' state from which it may be safely destructed but not otherwise 273 /// accessed. 274 Error takeError() { 275 Unchecked = false; 276 return HasError ? Error(std::move(*getErrorStorage())) : Error::success(); 277 } 278 279 /// Returns a pointer to the stored T value. 280 pointer operator->() { 281 assertIsChecked(); 282 return toPointer(getStorage()); 283 } 284 285 /// Returns a pointer to the stored T value. 286 const_pointer operator->() const { 287 assertIsChecked(); 288 return toPointer(getStorage()); 289 } 290 291 /// Returns a reference to the stored T value. 292 reference operator*() { 293 assertIsChecked(); 294 return *getStorage(); 295 } 296 297 /// Returns a reference to the stored T value. 298 const_reference operator*() const { 299 assertIsChecked(); 300 return *getStorage(); 301 } 302 303 private: 304 template <class T1> 305 static bool compareThisIfSameType(const T1 &a, const T1 &b) { 306 return &a == &b; 307 } 308 309 template <class T1, class T2> 310 static bool compareThisIfSameType(const T1 &a, const T2 &b) { 311 return false; 312 } 313 314 template <class OtherT> void moveConstruct(Expected<OtherT> &&Other) { 315 HasError = Other.HasError; 316 Unchecked = true; 317 Other.Unchecked = false; 318 319 if (!HasError) 320 new (getStorage()) storage_type(std::move(*Other.getStorage())); 321 else 322 new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage())); 323 } 324 325 template <class OtherT> void moveAssign(Expected<OtherT> &&Other) { 326 assertIsChecked(); 327 328 if (compareThisIfSameType(*this, Other)) 329 return; 330 331 this->~Expected(); 332 new (this) Expected(std::move(Other)); 333 } 334 335 pointer toPointer(pointer Val) { return Val; } 336 337 const_pointer toPointer(const_pointer Val) const { return Val; } 338 339 pointer toPointer(wrap *Val) { return &Val->get(); } 340 341 const_pointer toPointer(const wrap *Val) const { return &Val->get(); } 342 343 storage_type *getStorage() { 344 assert(!HasError && "Cannot get value when an error exists!"); 345 return reinterpret_cast<storage_type *>(&TStorage); 346 } 347 348 const storage_type *getStorage() const { 349 assert(!HasError && "Cannot get value when an error exists!"); 350 return reinterpret_cast<const storage_type *>(&TStorage); 351 } 352 353 error_type *getErrorStorage() { 354 assert(HasError && "Cannot get error when a value exists!"); 355 return reinterpret_cast<error_type *>(&ErrorStorage); 356 } 357 358 const error_type *getErrorStorage() const { 359 assert(HasError && "Cannot get error when a value exists!"); 360 return reinterpret_cast<const error_type *>(&ErrorStorage); 361 } 362 363 void assertIsChecked() { 364 if (ORC_RT_UNLIKELY(Unchecked)) { 365 fprintf(stderr, 366 "Expected<T> must be checked before access or destruction.\n"); 367 abort(); 368 } 369 } 370 371 union { 372 std::aligned_union_t<1, storage_type> TStorage; 373 std::aligned_union_t<1, error_type> ErrorStorage; 374 }; 375 376 bool HasError : 1; 377 bool Unchecked : 1; 378 }; 379 380 /// Consume an error without doing anything. 381 inline void consumeError(Error Err) { 382 if (Err) 383 (void)error_cast<ErrorInfoBase>(Err); 384 } 385 386 /// Consumes success values. It is a programmatic error to call this function 387 /// on a failure value. 388 inline void cantFail(Error Err) { 389 assert(!Err && "cantFail called on failure value"); 390 consumeError(std::move(Err)); 391 } 392 393 /// Auto-unwrap an Expected<T> value in the success state. It is a programmatic 394 /// error to call this function on a failure value. 395 template <typename T> T cantFail(Expected<T> E) { 396 assert(E && "cantFail called on failure value"); 397 consumeError(E.takeError()); 398 return std::move(*E); 399 } 400 401 /// Auto-unwrap an Expected<T> value in the success state. It is a programmatic 402 /// error to call this function on a failure value. 403 template <typename T> T &cantFail(Expected<T &> E) { 404 assert(E && "cantFail called on failure value"); 405 consumeError(E.takeError()); 406 return *E; 407 } 408 409 /// Convert the given error to a string. The error value is consumed in the 410 /// process. 411 inline std::string toString(Error Err) { 412 if (auto EIB = error_cast<ErrorInfoBase>(Err)) 413 return EIB->toString(); 414 return {}; 415 } 416 417 class StringError : public RTTIExtends<StringError, ErrorInfoBase> { 418 public: 419 StringError(std::string ErrMsg) : ErrMsg(std::move(ErrMsg)) {} 420 std::string toString() const override { return ErrMsg; } 421 422 private: 423 std::string ErrMsg; 424 }; 425 426 } // end namespace __orc_rt 427 428 #endif // ORC_RT_ERROR_H 429