xref: /freebsd-src/contrib/llvm-project/llvm/include/llvm/Support/ErrorOr.h (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// \file
100b57cec5SDimitry Andric ///
110b57cec5SDimitry Andric /// Provides ErrorOr<T> smart pointer.
120b57cec5SDimitry Andric ///
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #ifndef LLVM_SUPPORT_ERROROR_H
160b57cec5SDimitry Andric #define LLVM_SUPPORT_ERROROR_H
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric #include "llvm/Support/AlignOf.h"
190b57cec5SDimitry Andric #include <cassert>
200b57cec5SDimitry Andric #include <system_error>
210b57cec5SDimitry Andric #include <type_traits>
220b57cec5SDimitry Andric #include <utility>
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace llvm {
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric /// Represents either an error or a value T.
270b57cec5SDimitry Andric ///
280b57cec5SDimitry Andric /// ErrorOr<T> is a pointer-like class that represents the result of an
290b57cec5SDimitry Andric /// operation. The result is either an error, or a value of type T. This is
300b57cec5SDimitry Andric /// designed to emulate the usage of returning a pointer where nullptr indicates
310b57cec5SDimitry Andric /// failure. However instead of just knowing that the operation failed, we also
320b57cec5SDimitry Andric /// have an error_code and optional user data that describes why it failed.
330b57cec5SDimitry Andric ///
340b57cec5SDimitry Andric /// It is used like the following.
350b57cec5SDimitry Andric /// \code
360b57cec5SDimitry Andric ///   ErrorOr<Buffer> getBuffer();
370b57cec5SDimitry Andric ///
380b57cec5SDimitry Andric ///   auto buffer = getBuffer();
390b57cec5SDimitry Andric ///   if (error_code ec = buffer.getError())
400b57cec5SDimitry Andric ///     return ec;
410b57cec5SDimitry Andric ///   buffer->write("adena");
420b57cec5SDimitry Andric /// \endcode
430b57cec5SDimitry Andric ///
440b57cec5SDimitry Andric ///
450b57cec5SDimitry Andric /// Implicit conversion to bool returns true if there is a usable value. The
460b57cec5SDimitry Andric /// unary * and -> operators provide pointer like access to the value. Accessing
470b57cec5SDimitry Andric /// the value when there is an error has undefined behavior.
480b57cec5SDimitry Andric ///
490b57cec5SDimitry Andric /// When T is a reference type the behavior is slightly different. The reference
500b57cec5SDimitry Andric /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
510b57cec5SDimitry Andric /// there is special handling to make operator -> work as if T was not a
520b57cec5SDimitry Andric /// reference.
530b57cec5SDimitry Andric ///
540b57cec5SDimitry Andric /// T cannot be a rvalue reference.
550b57cec5SDimitry Andric template<class T>
560b57cec5SDimitry Andric class ErrorOr {
570b57cec5SDimitry Andric   template <class OtherT> friend class ErrorOr;
580b57cec5SDimitry Andric 
59*06c3fb27SDimitry Andric   static constexpr bool isRef = std::is_reference_v<T>;
600b57cec5SDimitry Andric 
615ffd83dbSDimitry Andric   using wrap = std::reference_wrapper<std::remove_reference_t<T>>;
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric public:
645ffd83dbSDimitry Andric   using storage_type = std::conditional_t<isRef, wrap, T>;
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric private:
675ffd83dbSDimitry Andric   using reference = std::remove_reference_t<T> &;
685ffd83dbSDimitry Andric   using const_reference = const std::remove_reference_t<T> &;
695ffd83dbSDimitry Andric   using pointer = std::remove_reference_t<T> *;
705ffd83dbSDimitry Andric   using const_pointer = const std::remove_reference_t<T> *;
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric public:
730b57cec5SDimitry Andric   template <class E>
740b57cec5SDimitry Andric   ErrorOr(E ErrorCode,
755ffd83dbSDimitry Andric           std::enable_if_t<std::is_error_code_enum<E>::value ||
760b57cec5SDimitry Andric                                std::is_error_condition_enum<E>::value,
775ffd83dbSDimitry Andric                            void *> = nullptr)
HasError(true)780b57cec5SDimitry Andric       : HasError(true) {
790b57cec5SDimitry Andric     new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
800b57cec5SDimitry Andric   }
810b57cec5SDimitry Andric 
ErrorOr(std::error_code EC)820b57cec5SDimitry Andric   ErrorOr(std::error_code EC) : HasError(true) {
830b57cec5SDimitry Andric     new (getErrorStorage()) std::error_code(EC);
840b57cec5SDimitry Andric   }
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric   template <class OtherT>
870b57cec5SDimitry Andric   ErrorOr(OtherT &&Val,
88*06c3fb27SDimitry Andric           std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr)
HasError(false)890b57cec5SDimitry Andric       : HasError(false) {
900b57cec5SDimitry Andric     new (getStorage()) storage_type(std::forward<OtherT>(Val));
910b57cec5SDimitry Andric   }
920b57cec5SDimitry Andric 
ErrorOr(const ErrorOr & Other)930b57cec5SDimitry Andric   ErrorOr(const ErrorOr &Other) {
940b57cec5SDimitry Andric     copyConstruct(Other);
950b57cec5SDimitry Andric   }
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric   template <class OtherT>
985ffd83dbSDimitry Andric   ErrorOr(const ErrorOr<OtherT> &Other,
99*06c3fb27SDimitry Andric           std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) {
1000b57cec5SDimitry Andric     copyConstruct(Other);
1010b57cec5SDimitry Andric   }
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric   template <class OtherT>
1040b57cec5SDimitry Andric   explicit ErrorOr(
1050b57cec5SDimitry Andric       const ErrorOr<OtherT> &Other,
106*06c3fb27SDimitry Andric       std::enable_if_t<!std::is_convertible_v<OtherT, const T &>> * = nullptr) {
1070b57cec5SDimitry Andric     copyConstruct(Other);
1080b57cec5SDimitry Andric   }
1090b57cec5SDimitry Andric 
ErrorOr(ErrorOr && Other)1100b57cec5SDimitry Andric   ErrorOr(ErrorOr &&Other) {
1110b57cec5SDimitry Andric     moveConstruct(std::move(Other));
1120b57cec5SDimitry Andric   }
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric   template <class OtherT>
1155ffd83dbSDimitry Andric   ErrorOr(ErrorOr<OtherT> &&Other,
116*06c3fb27SDimitry Andric           std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) {
1170b57cec5SDimitry Andric     moveConstruct(std::move(Other));
1180b57cec5SDimitry Andric   }
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric   // This might eventually need SFINAE but it's more complex than is_convertible
1210b57cec5SDimitry Andric   // & I'm too lazy to write it right now.
1220b57cec5SDimitry Andric   template <class OtherT>
1230b57cec5SDimitry Andric   explicit ErrorOr(
1240b57cec5SDimitry Andric       ErrorOr<OtherT> &&Other,
125*06c3fb27SDimitry Andric       std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = nullptr) {
1260b57cec5SDimitry Andric     moveConstruct(std::move(Other));
1270b57cec5SDimitry Andric   }
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   ErrorOr &operator=(const ErrorOr &Other) {
1300b57cec5SDimitry Andric     copyAssign(Other);
1310b57cec5SDimitry Andric     return *this;
1320b57cec5SDimitry Andric   }
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric   ErrorOr &operator=(ErrorOr &&Other) {
1350b57cec5SDimitry Andric     moveAssign(std::move(Other));
1360b57cec5SDimitry Andric     return *this;
1370b57cec5SDimitry Andric   }
1380b57cec5SDimitry Andric 
~ErrorOr()1390b57cec5SDimitry Andric   ~ErrorOr() {
1400b57cec5SDimitry Andric     if (!HasError)
1410b57cec5SDimitry Andric       getStorage()->~storage_type();
1420b57cec5SDimitry Andric   }
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric   /// Return false if there is an error.
1450b57cec5SDimitry Andric   explicit operator bool() const {
1460b57cec5SDimitry Andric     return !HasError;
1470b57cec5SDimitry Andric   }
1480b57cec5SDimitry Andric 
get()1490b57cec5SDimitry Andric   reference get() { return *getStorage(); }
get()1500b57cec5SDimitry Andric   const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
1510b57cec5SDimitry Andric 
getError()1520b57cec5SDimitry Andric   std::error_code getError() const {
1530b57cec5SDimitry Andric     return HasError ? *getErrorStorage() : std::error_code();
1540b57cec5SDimitry Andric   }
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric   pointer operator ->() {
1570b57cec5SDimitry Andric     return toPointer(getStorage());
1580b57cec5SDimitry Andric   }
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric   const_pointer operator->() const { return toPointer(getStorage()); }
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric   reference operator *() {
1630b57cec5SDimitry Andric     return *getStorage();
1640b57cec5SDimitry Andric   }
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   const_reference operator*() const { return *getStorage(); }
1670b57cec5SDimitry Andric 
1680b57cec5SDimitry Andric private:
1690b57cec5SDimitry Andric   template <class OtherT>
copyConstruct(const ErrorOr<OtherT> & Other)1700b57cec5SDimitry Andric   void copyConstruct(const ErrorOr<OtherT> &Other) {
1710b57cec5SDimitry Andric     if (!Other.HasError) {
1720b57cec5SDimitry Andric       // Get the other value.
1730b57cec5SDimitry Andric       HasError = false;
1740b57cec5SDimitry Andric       new (getStorage()) storage_type(*Other.getStorage());
1750b57cec5SDimitry Andric     } else {
1760b57cec5SDimitry Andric       // Get other's error.
1770b57cec5SDimitry Andric       HasError = true;
1780b57cec5SDimitry Andric       new (getErrorStorage()) std::error_code(Other.getError());
1790b57cec5SDimitry Andric     }
1800b57cec5SDimitry Andric   }
1810b57cec5SDimitry Andric 
1820b57cec5SDimitry Andric   template <class T1>
compareThisIfSameType(const T1 & a,const T1 & b)1830b57cec5SDimitry Andric   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
1840b57cec5SDimitry Andric     return &a == &b;
1850b57cec5SDimitry Andric   }
1860b57cec5SDimitry Andric 
1870b57cec5SDimitry Andric   template <class T1, class T2>
compareThisIfSameType(const T1 & a,const T2 & b)1880b57cec5SDimitry Andric   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
1890b57cec5SDimitry Andric     return false;
1900b57cec5SDimitry Andric   }
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   template <class OtherT>
copyAssign(const ErrorOr<OtherT> & Other)1930b57cec5SDimitry Andric   void copyAssign(const ErrorOr<OtherT> &Other) {
1940b57cec5SDimitry Andric     if (compareThisIfSameType(*this, Other))
1950b57cec5SDimitry Andric       return;
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric     this->~ErrorOr();
1980b57cec5SDimitry Andric     new (this) ErrorOr(Other);
1990b57cec5SDimitry Andric   }
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric   template <class OtherT>
moveConstruct(ErrorOr<OtherT> && Other)2020b57cec5SDimitry Andric   void moveConstruct(ErrorOr<OtherT> &&Other) {
2030b57cec5SDimitry Andric     if (!Other.HasError) {
2040b57cec5SDimitry Andric       // Get the other value.
2050b57cec5SDimitry Andric       HasError = false;
2060b57cec5SDimitry Andric       new (getStorage()) storage_type(std::move(*Other.getStorage()));
2070b57cec5SDimitry Andric     } else {
2080b57cec5SDimitry Andric       // Get other's error.
2090b57cec5SDimitry Andric       HasError = true;
2100b57cec5SDimitry Andric       new (getErrorStorage()) std::error_code(Other.getError());
2110b57cec5SDimitry Andric     }
2120b57cec5SDimitry Andric   }
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric   template <class OtherT>
moveAssign(ErrorOr<OtherT> && Other)2150b57cec5SDimitry Andric   void moveAssign(ErrorOr<OtherT> &&Other) {
2160b57cec5SDimitry Andric     if (compareThisIfSameType(*this, Other))
2170b57cec5SDimitry Andric       return;
2180b57cec5SDimitry Andric 
2190b57cec5SDimitry Andric     this->~ErrorOr();
2200b57cec5SDimitry Andric     new (this) ErrorOr(std::move(Other));
2210b57cec5SDimitry Andric   }
2220b57cec5SDimitry Andric 
toPointer(pointer Val)2230b57cec5SDimitry Andric   pointer toPointer(pointer Val) {
2240b57cec5SDimitry Andric     return Val;
2250b57cec5SDimitry Andric   }
2260b57cec5SDimitry Andric 
toPointer(const_pointer Val)2270b57cec5SDimitry Andric   const_pointer toPointer(const_pointer Val) const { return Val; }
2280b57cec5SDimitry Andric 
toPointer(wrap * Val)2290b57cec5SDimitry Andric   pointer toPointer(wrap *Val) {
2300b57cec5SDimitry Andric     return &Val->get();
2310b57cec5SDimitry Andric   }
2320b57cec5SDimitry Andric 
toPointer(const wrap * Val)2330b57cec5SDimitry Andric   const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
2340b57cec5SDimitry Andric 
getStorage()2350b57cec5SDimitry Andric   storage_type *getStorage() {
2360b57cec5SDimitry Andric     assert(!HasError && "Cannot get value when an error exists!");
237e8d8bef9SDimitry Andric     return reinterpret_cast<storage_type *>(&TStorage);
2380b57cec5SDimitry Andric   }
2390b57cec5SDimitry Andric 
getStorage()2400b57cec5SDimitry Andric   const storage_type *getStorage() const {
2410b57cec5SDimitry Andric     assert(!HasError && "Cannot get value when an error exists!");
242e8d8bef9SDimitry Andric     return reinterpret_cast<const storage_type *>(&TStorage);
2430b57cec5SDimitry Andric   }
2440b57cec5SDimitry Andric 
getErrorStorage()2450b57cec5SDimitry Andric   std::error_code *getErrorStorage() {
2460b57cec5SDimitry Andric     assert(HasError && "Cannot get error when a value exists!");
247e8d8bef9SDimitry Andric     return reinterpret_cast<std::error_code *>(&ErrorStorage);
2480b57cec5SDimitry Andric   }
2490b57cec5SDimitry Andric 
getErrorStorage()2500b57cec5SDimitry Andric   const std::error_code *getErrorStorage() const {
2510b57cec5SDimitry Andric     return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
2520b57cec5SDimitry Andric   }
2530b57cec5SDimitry Andric 
2540b57cec5SDimitry Andric   union {
2550b57cec5SDimitry Andric     AlignedCharArrayUnion<storage_type> TStorage;
2560b57cec5SDimitry Andric     AlignedCharArrayUnion<std::error_code> ErrorStorage;
2570b57cec5SDimitry Andric   };
2580b57cec5SDimitry Andric   bool HasError : 1;
2590b57cec5SDimitry Andric };
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric template <class T, class E>
2625ffd83dbSDimitry Andric std::enable_if_t<std::is_error_code_enum<E>::value ||
2630b57cec5SDimitry Andric                      std::is_error_condition_enum<E>::value,
2645ffd83dbSDimitry Andric                  bool>
2650b57cec5SDimitry Andric operator==(const ErrorOr<T> &Err, E Code) {
2660b57cec5SDimitry Andric   return Err.getError() == Code;
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric 
2690b57cec5SDimitry Andric } // end namespace llvm
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric #endif // LLVM_SUPPORT_ERROROR_H
272