Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Blame | Last modification | View Log | Download | RSS feed

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