LCOV - code coverage report
Current view: top level - include/llvm/Support - ErrorOr.h (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 113 147 76.9 %
Date: 2018-10-20 13:21:21 Functions: 26 47 55.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13