Home | History | Annotate | Download | only in Support
      1 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
      2 //
      3 //                             The LLVM Linker
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 ///
     10 /// \file
     11 ///
     12 /// Provides ErrorOr<T> smart pointer.
     13 ///
     14 //===----------------------------------------------------------------------===//
     15 
     16 #ifndef LLVM_SUPPORT_ERROROR_H
     17 #define LLVM_SUPPORT_ERROROR_H
     18 
     19 #include "llvm/Support/AlignOf.h"
     20 #include <cassert>
     21 #include <system_error>
     22 #include <type_traits>
     23 #include <utility>
     24 
     25 namespace llvm {
     26 
     27 /// \brief Stores a reference that can be changed.
     28 template <typename T>
     29 class ReferenceStorage {
     30   T *Storage;
     31 
     32 public:
     33   ReferenceStorage(T &Ref) : Storage(&Ref) {}
     34 
     35   operator T &() const { return *Storage; }
     36   T &get() const { return *Storage; }
     37 };
     38 
     39 /// \brief Represents either an error or a value T.
     40 ///
     41 /// ErrorOr<T> is a pointer-like class that represents the result of an
     42 /// operation. The result is either an error, or a value of type T. This is
     43 /// designed to emulate the usage of returning a pointer where nullptr indicates
     44 /// failure. However instead of just knowing that the operation failed, we also
     45 /// have an error_code and optional user data that describes why it failed.
     46 ///
     47 /// It is used like the following.
     48 /// \code
     49 ///   ErrorOr<Buffer> getBuffer();
     50 ///
     51 ///   auto buffer = getBuffer();
     52 ///   if (error_code ec = buffer.getError())
     53 ///     return ec;
     54 ///   buffer->write("adena");
     55 /// \endcode
     56 ///
     57 ///
     58 /// Implicit conversion to bool returns true if there is a usable value. The
     59 /// unary * and -> operators provide pointer like access to the value. Accessing
     60 /// the value when there is an error has undefined behavior.
     61 ///
     62 /// When T is a reference type the behavior is slightly different. The reference
     63 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
     64 /// there is special handling to make operator -> work as if T was not a
     65 /// reference.
     66 ///
     67 /// T cannot be a rvalue reference.
     68 template<class T>
     69 class ErrorOr {
     70   template <class OtherT> friend class ErrorOr;
     71 
     72   static const bool isRef = std::is_reference<T>::value;
     73 
     74   using wrap = ReferenceStorage<typename std::remove_reference<T>::type>;
     75 
     76 public:
     77   using storage_type = typename std::conditional<isRef, wrap, T>::type;
     78 
     79 private:
     80   using reference = typename std::remove_reference<T>::type &;
     81   using const_reference = const typename std::remove_reference<T>::type &;
     82   using pointer = typename std::remove_reference<T>::type *;
     83   using const_pointer = const typename std::remove_reference<T>::type *;
     84 
     85 public:
     86   template <class E>
     87   ErrorOr(E ErrorCode,
     88           typename std::enable_if<std::is_error_code_enum<E>::value ||
     89                                       std::is_error_condition_enum<E>::value,
     90                                   void *>::type = nullptr)
     91       : HasError(true) {
     92     new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
     93   }
     94 
     95   ErrorOr(std::error_code EC) : HasError(true) {
     96     new (getErrorStorage()) std::error_code(EC);
     97   }
     98 
     99   template <class OtherT>
    100   ErrorOr(OtherT &&Val,
    101           typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
    102               * = nullptr)
    103       : HasError(false) {
    104     new (getStorage()) storage_type(std::forward<OtherT>(Val));
    105   }
    106 
    107   ErrorOr(const ErrorOr &Other) {
    108     copyConstruct(Other);
    109   }
    110 
    111   template <class OtherT>
    112   ErrorOr(
    113       const ErrorOr<OtherT> &Other,
    114       typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
    115           nullptr) {
    116     copyConstruct(Other);
    117   }
    118 
    119   template <class OtherT>
    120   explicit ErrorOr(
    121       const ErrorOr<OtherT> &Other,
    122       typename std::enable_if<
    123           !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
    124     copyConstruct(Other);
    125   }
    126 
    127   ErrorOr(ErrorOr &&Other) {
    128     moveConstruct(std::move(Other));
    129   }
    130 
    131   template <class OtherT>
    132   ErrorOr(
    133       ErrorOr<OtherT> &&Other,
    134       typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
    135           nullptr) {
    136     moveConstruct(std::move(Other));
    137   }
    138 
    139   // This might eventually need SFINAE but it's more complex than is_convertible
    140   // & I'm too lazy to write it right now.
    141   template <class OtherT>
    142   explicit ErrorOr(
    143       ErrorOr<OtherT> &&Other,
    144       typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
    145           nullptr) {
    146     moveConstruct(std::move(Other));
    147   }
    148 
    149   ErrorOr &operator=(const ErrorOr &Other) {
    150     copyAssign(Other);
    151     return *this;
    152   }
    153 
    154   ErrorOr &operator=(ErrorOr &&Other) {
    155     moveAssign(std::move(Other));
    156     return *this;
    157   }
    158 
    159   ~ErrorOr() {
    160     if (!HasError)
    161       getStorage()->~storage_type();
    162   }
    163 
    164   /// \brief Return false if there is an error.
    165   explicit operator bool() const {
    166     return !HasError;
    167   }
    168 
    169   reference get() { return *getStorage(); }
    170   const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
    171 
    172   std::error_code getError() const {
    173     return HasError ? *getErrorStorage() : std::error_code();
    174   }
    175 
    176   pointer operator ->() {
    177     return toPointer(getStorage());
    178   }
    179 
    180   const_pointer operator->() const { return toPointer(getStorage()); }
    181 
    182   reference operator *() {
    183     return *getStorage();
    184   }
    185 
    186   const_reference operator*() const { return *getStorage(); }
    187 
    188 private:
    189   template <class OtherT>
    190   void copyConstruct(const ErrorOr<OtherT> &Other) {
    191     if (!Other.HasError) {
    192       // Get the other value.
    193       HasError = false;
    194       new (getStorage()) storage_type(*Other.getStorage());
    195     } else {
    196       // Get other's error.
    197       HasError = true;
    198       new (getErrorStorage()) std::error_code(Other.getError());
    199     }
    200   }
    201 
    202   template <class T1>
    203   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
    204     return &a == &b;
    205   }
    206 
    207   template <class T1, class T2>
    208   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
    209     return false;
    210   }
    211 
    212   template <class OtherT>
    213   void copyAssign(const ErrorOr<OtherT> &Other) {
    214     if (compareThisIfSameType(*this, Other))
    215       return;
    216 
    217     this->~ErrorOr();
    218     new (this) ErrorOr(Other);
    219   }
    220 
    221   template <class OtherT>
    222   void moveConstruct(ErrorOr<OtherT> &&Other) {
    223     if (!Other.HasError) {
    224       // Get the other value.
    225       HasError = false;
    226       new (getStorage()) storage_type(std::move(*Other.getStorage()));
    227     } else {
    228       // Get other's error.
    229       HasError = true;
    230       new (getErrorStorage()) std::error_code(Other.getError());
    231     }
    232   }
    233 
    234   template <class OtherT>
    235   void moveAssign(ErrorOr<OtherT> &&Other) {
    236     if (compareThisIfSameType(*this, Other))
    237       return;
    238 
    239     this->~ErrorOr();
    240     new (this) ErrorOr(std::move(Other));
    241   }
    242 
    243   pointer toPointer(pointer Val) {
    244     return Val;
    245   }
    246 
    247   const_pointer toPointer(const_pointer Val) const { return Val; }
    248 
    249   pointer toPointer(wrap *Val) {
    250     return &Val->get();
    251   }
    252 
    253   const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
    254 
    255   storage_type *getStorage() {
    256     assert(!HasError && "Cannot get value when an error exists!");
    257     return reinterpret_cast<storage_type*>(TStorage.buffer);
    258   }
    259 
    260   const storage_type *getStorage() const {
    261     assert(!HasError && "Cannot get value when an error exists!");
    262     return reinterpret_cast<const storage_type*>(TStorage.buffer);
    263   }
    264 
    265   std::error_code *getErrorStorage() {
    266     assert(HasError && "Cannot get error when a value exists!");
    267     return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
    268   }
    269 
    270   const std::error_code *getErrorStorage() const {
    271     return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
    272   }
    273 
    274   union {
    275     AlignedCharArrayUnion<storage_type> TStorage;
    276     AlignedCharArrayUnion<std::error_code> ErrorStorage;
    277   };
    278   bool HasError : 1;
    279 };
    280 
    281 template <class T, class E>
    282 typename std::enable_if<std::is_error_code_enum<E>::value ||
    283                             std::is_error_condition_enum<E>::value,
    284                         bool>::type
    285 operator==(const ErrorOr<T> &Err, E Code) {
    286   return Err.getError() == Code;
    287 }
    288 
    289 } // end namespace llvm
    290 
    291 #endif // LLVM_SUPPORT_ERROROR_H
    292