Home | History | Annotate | Download | only in Support
      1 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -----------------------===//
      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_ERROR_OR_H
     17 #define LLVM_SUPPORT_ERROR_OR_H
     18 
     19 #include "llvm/ADT/PointerIntPair.h"
     20 #include "llvm/Support/AlignOf.h"
     21 #include "llvm/Support/system_error.h"
     22 #include "llvm/Support/type_traits.h"
     23 
     24 #include <cassert>
     25 #if LLVM_HAS_CXX11_TYPETRAITS
     26 #include <type_traits>
     27 #endif
     28 
     29 namespace llvm {
     30 struct ErrorHolderBase {
     31   error_code Error;
     32   uint16_t RefCount;
     33   bool HasUserData;
     34 
     35   ErrorHolderBase() : RefCount(1) {}
     36 
     37   void aquire() {
     38     ++RefCount;
     39   }
     40 
     41   void release() {
     42     if (--RefCount == 0)
     43       delete this;
     44   }
     45 
     46 protected:
     47   virtual ~ErrorHolderBase() {}
     48 };
     49 
     50 template<class T>
     51 struct ErrorHolder : ErrorHolderBase {
     52 #if LLVM_HAS_RVALUE_REFERENCES
     53   ErrorHolder(T &&UD) : UserData(llvm_move(UD)) {}
     54 #else
     55   ErrorHolder(T &UD) : UserData(UD) {}
     56 #endif
     57   T UserData;
     58 };
     59 
     60 template<class Tp> struct ErrorOrUserDataTraits : llvm::false_type {};
     61 
     62 #if LLVM_HAS_CXX11_TYPETRAITS && LLVM_HAS_RVALUE_REFERENCES
     63 template<class T, class V>
     64 typename std::enable_if< std::is_constructible<T, V>::value
     65                        , typename std::remove_reference<V>::type>::type &&
     66  moveIfMoveConstructible(V &Val) {
     67   return std::move(Val);
     68 }
     69 
     70 template<class T, class V>
     71 typename std::enable_if< !std::is_constructible<T, V>::value
     72                        , typename std::remove_reference<V>::type>::type &
     73 moveIfMoveConstructible(V &Val) {
     74   return Val;
     75 }
     76 #else
     77 template<class T, class V>
     78 V &moveIfMoveConstructible(V &Val) {
     79   return Val;
     80 }
     81 #endif
     82 
     83 /// \brief Stores a reference that can be changed.
     84 template <typename T>
     85 class ReferenceStorage {
     86   T *Storage;
     87 
     88 public:
     89   ReferenceStorage(T &Ref) : Storage(&Ref) {}
     90 
     91   operator T &() const { return *Storage; }
     92   T &get() const { return *Storage; }
     93 };
     94 
     95 /// \brief Represents either an error or a value T.
     96 ///
     97 /// ErrorOr<T> is a pointer-like class that represents the result of an
     98 /// operation. The result is either an error, or a value of type T. This is
     99 /// designed to emulate the usage of returning a pointer where nullptr indicates
    100 /// failure. However instead of just knowing that the operation failed, we also
    101 /// have an error_code and optional user data that describes why it failed.
    102 ///
    103 /// It is used like the following.
    104 /// \code
    105 ///   ErrorOr<Buffer> getBuffer();
    106 ///   void handleError(error_code ec);
    107 ///
    108 ///   auto buffer = getBuffer();
    109 ///   if (!buffer)
    110 ///     handleError(buffer);
    111 ///   buffer->write("adena");
    112 /// \endcode
    113 ///
    114 /// ErrorOr<T> also supports user defined data for specific error_codes. To use
    115 /// this feature you must first add a template specialization of
    116 /// ErrorOrUserDataTraits derived from std::true_type for your type in the lld
    117 /// namespace. This specialization must have a static error_code error()
    118 /// function that returns the error_code this data is used with.
    119 ///
    120 /// getError<UserData>() may be called to get either the stored user data, or
    121 /// a default constructed UserData if none was stored.
    122 ///
    123 /// Example:
    124 /// \code
    125 ///   struct InvalidArgError {
    126 ///     InvalidArgError() {}
    127 ///     InvalidArgError(std::string S) : ArgName(S) {}
    128 ///     std::string ArgName;
    129 ///   };
    130 ///
    131 ///   namespace llvm {
    132 ///   template<>
    133 ///   struct ErrorOrUserDataTraits<InvalidArgError> : std::true_type {
    134 ///     static error_code error() {
    135 ///       return make_error_code(errc::invalid_argument);
    136 ///     }
    137 ///   };
    138 ///   } // end namespace llvm
    139 ///
    140 ///   using namespace llvm;
    141 ///
    142 ///   ErrorOr<int> foo() {
    143 ///     return InvalidArgError("adena");
    144 ///   }
    145 ///
    146 ///   int main() {
    147 ///     auto a = foo();
    148 ///     if (!a && error_code(a) == errc::invalid_argument)
    149 ///       llvm::errs() << a.getError<InvalidArgError>().ArgName << "\n";
    150 ///   }
    151 /// \endcode
    152 ///
    153 /// An implicit conversion to bool provides a way to check if there was an
    154 /// error. The unary * and -> operators provide pointer like access to the
    155 /// value. Accessing the value when there is an error has undefined behavior.
    156 ///
    157 /// When T is a reference type the behaivor is slightly different. The reference
    158 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
    159 /// there is special handling to make operator -> work as if T was not a
    160 /// reference.
    161 ///
    162 /// T cannot be a rvalue reference.
    163 template<class T>
    164 class ErrorOr {
    165   template <class OtherT> friend class ErrorOr;
    166   static const bool isRef = is_reference<T>::value;
    167   typedef ReferenceStorage<typename remove_reference<T>::type> wrap;
    168 
    169 public:
    170   typedef typename
    171     conditional< isRef
    172                , wrap
    173                , T
    174                >::type storage_type;
    175 
    176 private:
    177   typedef typename remove_reference<T>::type &reference;
    178   typedef typename remove_reference<T>::type *pointer;
    179 
    180 public:
    181   ErrorOr() : IsValid(false) {}
    182 
    183   template <class E>
    184   ErrorOr(E ErrorCode, typename enable_if_c<is_error_code_enum<E>::value ||
    185                                             is_error_condition_enum<E>::value,
    186                                             void *>::type = 0)
    187       : HasError(true), IsValid(true) {
    188     Error = new ErrorHolderBase;
    189     Error->Error = make_error_code(ErrorCode);
    190     Error->HasUserData = false;
    191   }
    192 
    193   ErrorOr(llvm::error_code EC) : HasError(true), IsValid(true) {
    194     Error = new ErrorHolderBase;
    195     Error->Error = EC;
    196     Error->HasUserData = false;
    197   }
    198 
    199   template<class UserDataT>
    200   ErrorOr(UserDataT UD, typename
    201           enable_if_c<ErrorOrUserDataTraits<UserDataT>::value>::type* = 0)
    202     : HasError(true), IsValid(true) {
    203     Error = new ErrorHolder<UserDataT>(llvm_move(UD));
    204     Error->Error = ErrorOrUserDataTraits<UserDataT>::error();
    205     Error->HasUserData = true;
    206   }
    207 
    208   ErrorOr(T Val) : HasError(false), IsValid(true) {
    209     new (get()) storage_type(moveIfMoveConstructible<storage_type>(Val));
    210   }
    211 
    212   ErrorOr(const ErrorOr &Other) : IsValid(false) {
    213     copyConstruct(Other);
    214   }
    215 
    216   template <class OtherT>
    217   ErrorOr(const ErrorOr<OtherT> &Other) : IsValid(false) {
    218     copyConstruct(Other);
    219   }
    220 
    221   ErrorOr &operator =(const ErrorOr &Other) {
    222     copyAssign(Other);
    223     return *this;
    224   }
    225 
    226   template <class OtherT>
    227   ErrorOr &operator =(const ErrorOr<OtherT> &Other) {
    228     copyAssign(Other);
    229     return *this;
    230   }
    231 
    232 #if LLVM_HAS_RVALUE_REFERENCES
    233   ErrorOr(ErrorOr &&Other) : IsValid(false) {
    234     moveConstruct(std::move(Other));
    235   }
    236 
    237   template <class OtherT>
    238   ErrorOr(ErrorOr<OtherT> &&Other) : IsValid(false) {
    239     moveConstruct(std::move(Other));
    240   }
    241 
    242   ErrorOr &operator =(ErrorOr &&Other) {
    243     moveAssign(std::move(Other));
    244     return *this;
    245   }
    246 
    247   template <class OtherT>
    248   ErrorOr &operator =(ErrorOr<OtherT> &&Other) {
    249     moveAssign(std::move(Other));
    250     return *this;
    251   }
    252 #endif
    253 
    254   ~ErrorOr() {
    255     if (!IsValid)
    256       return;
    257     if (HasError)
    258       Error->release();
    259     else
    260       get()->~storage_type();
    261   }
    262 
    263   template<class ET>
    264   ET getError() const {
    265     assert(IsValid && "Cannot get the error of a default constructed ErrorOr!");
    266     assert(HasError && "Cannot get an error if none exists!");
    267     assert(ErrorOrUserDataTraits<ET>::error() == Error->Error &&
    268            "Incorrect user error data type for error!");
    269     if (!Error->HasUserData)
    270       return ET();
    271     return reinterpret_cast<const ErrorHolder<ET>*>(Error)->UserData;
    272   }
    273 
    274   typedef void (*unspecified_bool_type)();
    275   static void unspecified_bool_true() {}
    276 
    277   /// \brief Return false if there is an error.
    278   operator unspecified_bool_type() const {
    279     assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
    280     return HasError ? 0 : unspecified_bool_true;
    281   }
    282 
    283   operator llvm::error_code() const {
    284     assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
    285     return HasError ? Error->Error : llvm::error_code::success();
    286   }
    287 
    288   pointer operator ->() {
    289     return toPointer(get());
    290   }
    291 
    292   reference operator *() {
    293     return *get();
    294   }
    295 
    296 private:
    297   template <class OtherT>
    298   void copyConstruct(const ErrorOr<OtherT> &Other) {
    299     // Construct an invalid ErrorOr if other is invalid.
    300     if (!Other.IsValid)
    301       return;
    302     IsValid = true;
    303     if (!Other.HasError) {
    304       // Get the other value.
    305       HasError = false;
    306       new (get()) storage_type(*Other.get());
    307     } else {
    308       // Get other's error.
    309       Error = Other.Error;
    310       HasError = true;
    311       Error->aquire();
    312     }
    313   }
    314 
    315   template <class T1>
    316   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
    317     return &a == &b;
    318   }
    319 
    320   template <class T1, class T2>
    321   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
    322     return false;
    323   }
    324 
    325   template <class OtherT>
    326   void copyAssign(const ErrorOr<OtherT> &Other) {
    327     if (compareThisIfSameType(*this, Other))
    328       return;
    329 
    330     this->~ErrorOr();
    331     new (this) ErrorOr(Other);
    332   }
    333 
    334 #if LLVM_HAS_RVALUE_REFERENCES
    335   template <class OtherT>
    336   void moveConstruct(ErrorOr<OtherT> &&Other) {
    337     // Construct an invalid ErrorOr if other is invalid.
    338     if (!Other.IsValid)
    339       return;
    340     IsValid = true;
    341     if (!Other.HasError) {
    342       // Get the other value.
    343       HasError = false;
    344       new (get()) storage_type(std::move(*Other.get()));
    345       // Tell other not to do any destruction.
    346       Other.IsValid = false;
    347     } else {
    348       // Get other's error.
    349       Error = Other.Error;
    350       HasError = true;
    351       // Tell other not to do any destruction.
    352       Other.IsValid = false;
    353     }
    354   }
    355 
    356   template <class OtherT>
    357   void moveAssign(ErrorOr<OtherT> &&Other) {
    358     if (compareThisIfSameType(*this, Other))
    359       return;
    360 
    361     this->~ErrorOr();
    362     new (this) ErrorOr(std::move(Other));
    363   }
    364 #endif
    365 
    366   pointer toPointer(pointer Val) {
    367     return Val;
    368   }
    369 
    370   pointer toPointer(wrap *Val) {
    371     return &Val->get();
    372   }
    373 
    374   storage_type *get() {
    375     assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
    376     assert(!HasError && "Cannot get value when an error exists!");
    377     return reinterpret_cast<storage_type*>(TStorage.buffer);
    378   }
    379 
    380   const storage_type *get() const {
    381     assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
    382     assert(!HasError && "Cannot get value when an error exists!");
    383     return reinterpret_cast<const storage_type*>(TStorage.buffer);
    384   }
    385 
    386   union {
    387     AlignedCharArrayUnion<storage_type> TStorage;
    388     ErrorHolderBase *Error;
    389   };
    390   bool HasError : 1;
    391   bool IsValid : 1;
    392 };
    393 
    394 // ErrorOr specialization for void.
    395 template <>
    396 class ErrorOr<void> {
    397 public:
    398   ErrorOr() : Error(0, 0) {}
    399 
    400   template <class E>
    401   ErrorOr(E ErrorCode, typename enable_if_c<is_error_code_enum<E>::value ||
    402                                             is_error_condition_enum<E>::value,
    403                                             void *> ::type = 0)
    404       : Error(0, 0) {
    405     error_code EC = make_error_code(ErrorCode);
    406     if (EC == errc::success) {
    407       Error.setInt(1);
    408       return;
    409     }
    410     ErrorHolderBase *EHB = new ErrorHolderBase;
    411     EHB->Error = EC;
    412     EHB->HasUserData = false;
    413     Error.setPointer(EHB);
    414   }
    415 
    416   ErrorOr(llvm::error_code EC) : Error(0, 0) {
    417     if (EC == errc::success) {
    418       Error.setInt(1);
    419       return;
    420     }
    421     ErrorHolderBase *E = new ErrorHolderBase;
    422     E->Error = EC;
    423     E->HasUserData = false;
    424     Error.setPointer(E);
    425   }
    426 
    427   template<class UserDataT>
    428   ErrorOr(UserDataT UD, typename
    429           enable_if_c<ErrorOrUserDataTraits<UserDataT>::value>::type* = 0)
    430       : Error(0, 0) {
    431     ErrorHolderBase *E = new ErrorHolder<UserDataT>(llvm_move(UD));
    432     E->Error = ErrorOrUserDataTraits<UserDataT>::error();
    433     E->HasUserData = true;
    434     Error.setPointer(E);
    435   }
    436 
    437   ErrorOr(const ErrorOr &Other) : Error(0, 0) {
    438     Error = Other.Error;
    439     if (Other.Error.getPointer()->Error) {
    440       Error.getPointer()->aquire();
    441     }
    442   }
    443 
    444   ErrorOr &operator =(const ErrorOr &Other) {
    445     if (this == &Other)
    446       return *this;
    447 
    448     this->~ErrorOr();
    449     new (this) ErrorOr(Other);
    450 
    451     return *this;
    452   }
    453 
    454 #if LLVM_HAS_RVALUE_REFERENCES
    455   ErrorOr(ErrorOr &&Other) : Error(0) {
    456     // Get other's error.
    457     Error = Other.Error;
    458     // Tell other not to do any destruction.
    459     Other.Error.setPointer(0);
    460   }
    461 
    462   ErrorOr &operator =(ErrorOr &&Other) {
    463     if (this == &Other)
    464       return *this;
    465 
    466     this->~ErrorOr();
    467     new (this) ErrorOr(std::move(Other));
    468 
    469     return *this;
    470   }
    471 #endif
    472 
    473   ~ErrorOr() {
    474     if (Error.getPointer())
    475       Error.getPointer()->release();
    476   }
    477 
    478   template<class ET>
    479   ET getError() const {
    480     assert(ErrorOrUserDataTraits<ET>::error() == *this &&
    481            "Incorrect user error data type for error!");
    482     if (!Error.getPointer()->HasUserData)
    483       return ET();
    484     return reinterpret_cast<const ErrorHolder<ET> *>(
    485         Error.getPointer())->UserData;
    486   }
    487 
    488   typedef void (*unspecified_bool_type)();
    489   static void unspecified_bool_true() {}
    490 
    491   /// \brief Return false if there is an error.
    492   operator unspecified_bool_type() const {
    493     return Error.getInt() ? unspecified_bool_true : 0;
    494   }
    495 
    496   operator llvm::error_code() const {
    497     return Error.getInt() ? make_error_code(errc::success)
    498                           : Error.getPointer()->Error;
    499   }
    500 
    501 private:
    502   // If the bit is 1, the error is success.
    503   llvm::PointerIntPair<ErrorHolderBase *, 1> Error;
    504 };
    505 
    506 template<class T, class E>
    507 typename enable_if_c<is_error_code_enum<E>::value ||
    508                      is_error_condition_enum<E>::value, bool>::type
    509 operator ==(ErrorOr<T> &Err, E Code) {
    510   return error_code(Err) == Code;
    511 }
    512 } // end namespace llvm
    513 
    514 #endif
    515