LCOV - code coverage report
Current view: top level - include/llvm/Support - ErrorOr.h (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 52 55 94.5 %
Date: 2017-09-14 15:23:50 Functions: 22 26 84.6 %
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             : /// \brief Stores a reference that can be changed.
      28             : template <typename T>
      29             : class ReferenceStorage {
      30             :   T *Storage;
      31             : 
      32             : public:
      33       10246 :   ReferenceStorage(T &Ref) : Storage(&Ref) {}
      34             : 
      35        5722 :   operator T &() const { return *Storage; }
      36        2097 :   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           2 :   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      554444 :       : HasError(true) {
      92     1108888 :     new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
      93             :   }
      94             : 
      95      829303 :   ErrorOr(std::error_code EC) : HasError(true) {
      96      829303 :     new (getErrorStorage()) std::error_code(EC);
      97             :   }
      98             : 
      99             :   template <class OtherT>
     100          40 :   ErrorOr(OtherT &&Val,
     101             :           typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
     102             :               * = nullptr)
     103     1140217 :       : HasError(false) {
     104     3084687 :     new (getStorage()) storage_type(std::forward<OtherT>(Val));
     105             :   }
     106             : 
     107          23 :   ErrorOr(const ErrorOr &Other) {
     108          23 :     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           1 :   explicit ErrorOr(
     121             :       const ErrorOr<OtherT> &Other,
     122             :       typename std::enable_if<
     123             :           !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
     124           1 :     copyConstruct(Other);
     125             :   }
     126             : 
     127      305970 :   ErrorOr(ErrorOr &&Other) {
     128      305970 :     moveConstruct(std::move(Other));
     129             :   }
     130             : 
     131             :   template <class OtherT>
     132           4 :   ErrorOr(
     133             :       ErrorOr<OtherT> &&Other,
     134             :       typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
     135             :           nullptr) {
     136           4 :     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           1 :   explicit ErrorOr(
     143             :       ErrorOr<OtherT> &&Other,
     144             :       typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
     145             :           nullptr) {
     146           1 :     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       30392 :     moveAssign(std::move(Other));
     156             :     return *this;
     157             :   }
     158             : 
     159             :   ~ErrorOr() {
     160     1906812 :     if (!HasError)
     161     1316951 :       getStorage()->~storage_type();
     162      103819 :   }
     163             : 
     164             :   /// \brief Return false if there is an error.
     165             :   explicit operator bool() const {
     166     2369393 :     return !HasError;
     167             :   }
     168             : 
     169      282264 :   reference get() { return *getStorage(); }
     170         588 :   const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
     171             : 
     172             :   std::error_code getError() const {
     173     1203043 :     return HasError ? *getErrorStorage() : std::error_code();
     174             :   }
     175             : 
     176             :   pointer operator ->() {
     177      245512 :     return toPointer(getStorage());
     178             :   }
     179             : 
     180             :   const_pointer operator->() const { return toPointer(getStorage()); }
     181             : 
     182             :   reference operator *() {
     183      671565 :     return *getStorage();
     184             :   }
     185             : 
     186             :   const_reference operator*() const { return *getStorage(); }
     187             : 
     188             : private:
     189             :   template <class OtherT>
     190          24 :   void copyConstruct(const ErrorOr<OtherT> &Other) {
     191          24 :     if (!Other.HasError) {
     192             :       // Get the other value.
     193          24 :       HasError = false;
     194          26 :       new (getStorage()) storage_type(*Other.getStorage());
     195             :     } else {
     196             :       // Get other's error.
     197           0 :       HasError = true;
     198           0 :       new (getErrorStorage()) std::error_code(Other.getError());
     199             :     }
     200          24 :   }
     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      305975 :   void moveConstruct(ErrorOr<OtherT> &&Other) {
     223      305975 :     if (!Other.HasError) {
     224             :       // Get the other value.
     225      292194 :       HasError = false;
     226      866646 :       new (getStorage()) storage_type(std::move(*Other.getStorage()));
     227             :     } else {
     228             :       // Get other's error.
     229       13781 :       HasError = true;
     230       27562 :       new (getErrorStorage()) std::error_code(Other.getError());
     231             :     }
     232      305975 :   }
     233             : 
     234             :   template <class OtherT>
     235       30388 :   void moveAssign(ErrorOr<OtherT> &&Other) {
     236       30390 :     if (compareThisIfSameType(*this, Other))
     237             :       return;
     238             : 
     239       30390 :     this->~ErrorOr();
     240       60780 :     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          53 :     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     1605130 :     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           0 :     return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
     268             :   }
     269             : 
     270             :   const std::error_code *getErrorStorage() const {
     271      963944 :     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           1 : operator==(const ErrorOr<T> &Err, E Code) {
     286           3 :   return Err.getError() == Code;
     287             : }
     288             : 
     289             : } // end namespace llvm
     290             : 
     291             : #endif // LLVM_SUPPORT_ERROROR_H

Generated by: LCOV version 1.13