brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.6 KiB · 2803509 Raw
427 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef ORC_RT_ERROR_H10#define ORC_RT_ERROR_H11 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 21namespace orc_rt {22 23/// Base class for all errors.24class ErrorInfoBase : public RTTIExtends<ErrorInfoBase, RTTIRoot> {25public:26  virtual std::string toString() const = 0;27};28 29/// Represents an environmental error.30class 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 41public:42  /// Destroy this error. Aborts if error was not checked, or was checked but43  /// 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 considered50  /// unchecked, even if the source error had been checked. The original error51  /// 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, you58  /// you cannot overwrite an unhandled error. The current error is then59  /// 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 90private: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) { 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.131template <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>. The138/// primary use-case for this is 're-packaging' errors after inspecting them139/// using error_cast, hence the name.140inline Error repackage_error(std::unique_ptr<ErrorInfoBase> EIB) {141  return Error(std::move(EIB));142}143 144/// If the argument is an error of type ErrT then this function unpacks it145/// and returns a std::unique_ptr<ErrT>. Otherwise returns a nullptr and146/// 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/// \endcode157template <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.167class ErrorAsOutParameter {168public: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 181private:182  Error *Err;183};184 185template <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 200public: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 OtherT219  /// 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 OtherT228  /// 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    else248      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 this266  /// method is a no-op and returns Error::success.267  ///268  /// If thsi Expected value is in a failure state (holding an Error) then this269  /// method returns the contained error and leaves this Expected in an270  /// 'empty' state from which it may be safely destructed but not otherwise271  /// 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 301private: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    else320      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              "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.379inline 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 function385/// on a failure value.386inline void cantFail(Error Err) {387  assert(!Err && "cantFail called on failure value");388  consumeError(std::move(Err));389}390 391/// Auto-unwrap an Expected<T> value in the success state. It is a programmatic392/// error to call this function on a failure value.393template <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 programmatic400/// error to call this function on a failure value.401template <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 the408/// process.409inline std::string toString(Error Err) {410  if (auto EIB = error_cast<ErrorInfoBase>(Err))411    return EIB->toString();412  return {};413}414 415class StringError : public RTTIExtends<StringError, ErrorInfoBase> {416public:417  StringError(std::string ErrMsg) : ErrMsg(std::move(ErrMsg)) {}418  std::string toString() const override { return ErrMsg; }419 420private:421  std::string ErrMsg;422};423 424} // namespace orc_rt425 426#endif // ORC_RT_ERROR_H427