Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:30

0001 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 ///
0009 /// \file
0010 ///
0011 /// Provides ErrorOr<T> smart pointer.
0012 ///
0013 //===----------------------------------------------------------------------===//
0014 
0015 #ifndef LLVM_SUPPORT_ERROROR_H
0016 #define LLVM_SUPPORT_ERROROR_H
0017 
0018 #include "llvm/Support/AlignOf.h"
0019 #include <cassert>
0020 #include <system_error>
0021 #include <type_traits>
0022 #include <utility>
0023 
0024 namespace llvm {
0025 
0026 /// Represents either an error or a value T.
0027 ///
0028 /// ErrorOr<T> is a pointer-like class that represents the result of an
0029 /// operation. The result is either an error, or a value of type T. This is
0030 /// designed to emulate the usage of returning a pointer where nullptr indicates
0031 /// failure. However instead of just knowing that the operation failed, we also
0032 /// have an error_code and optional user data that describes why it failed.
0033 ///
0034 /// It is used like the following.
0035 /// \code
0036 ///   ErrorOr<Buffer> getBuffer();
0037 ///
0038 ///   auto buffer = getBuffer();
0039 ///   if (error_code ec = buffer.getError())
0040 ///     return ec;
0041 ///   buffer->write("adena");
0042 /// \endcode
0043 ///
0044 ///
0045 /// Implicit conversion to bool returns true if there is a usable value. The
0046 /// unary * and -> operators provide pointer like access to the value. Accessing
0047 /// the value when there is an error has undefined behavior.
0048 ///
0049 /// When T is a reference type the behavior is slightly different. The reference
0050 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
0051 /// there is special handling to make operator -> work as if T was not a
0052 /// reference.
0053 ///
0054 /// T cannot be a rvalue reference.
0055 template<class T>
0056 class ErrorOr {
0057   template <class OtherT> friend class ErrorOr;
0058 
0059   static constexpr bool isRef = std::is_reference_v<T>;
0060 
0061   using wrap = std::reference_wrapper<std::remove_reference_t<T>>;
0062 
0063 public:
0064   using storage_type = std::conditional_t<isRef, wrap, T>;
0065 
0066 private:
0067   using reference = std::remove_reference_t<T> &;
0068   using const_reference = const std::remove_reference_t<T> &;
0069   using pointer = std::remove_reference_t<T> *;
0070   using const_pointer = const std::remove_reference_t<T> *;
0071 
0072 public:
0073   template <class E>
0074   ErrorOr(E ErrorCode,
0075           std::enable_if_t<std::is_error_code_enum<E>::value ||
0076                                std::is_error_condition_enum<E>::value,
0077                            void *> = nullptr)
0078       : HasError(true) {
0079     new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
0080   }
0081 
0082   ErrorOr(std::error_code EC) : HasError(true) {
0083     new (getErrorStorage()) std::error_code(EC);
0084   }
0085 
0086   template <class OtherT>
0087   ErrorOr(OtherT &&Val,
0088           std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr)
0089       : HasError(false) {
0090     new (getStorage()) storage_type(std::forward<OtherT>(Val));
0091   }
0092 
0093   ErrorOr(const ErrorOr &Other) {
0094     copyConstruct(Other);
0095   }
0096 
0097   template <class OtherT>
0098   ErrorOr(const ErrorOr<OtherT> &Other,
0099           std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) {
0100     copyConstruct(Other);
0101   }
0102 
0103   template <class OtherT>
0104   explicit ErrorOr(
0105       const ErrorOr<OtherT> &Other,
0106       std::enable_if_t<!std::is_convertible_v<OtherT, const T &>> * = nullptr) {
0107     copyConstruct(Other);
0108   }
0109 
0110   ErrorOr(ErrorOr &&Other) {
0111     moveConstruct(std::move(Other));
0112   }
0113 
0114   template <class OtherT>
0115   ErrorOr(ErrorOr<OtherT> &&Other,
0116           std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) {
0117     moveConstruct(std::move(Other));
0118   }
0119 
0120   // This might eventually need SFINAE but it's more complex than is_convertible
0121   // & I'm too lazy to write it right now.
0122   template <class OtherT>
0123   explicit ErrorOr(
0124       ErrorOr<OtherT> &&Other,
0125       std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = nullptr) {
0126     moveConstruct(std::move(Other));
0127   }
0128 
0129   ErrorOr &operator=(const ErrorOr &Other) {
0130     copyAssign(Other);
0131     return *this;
0132   }
0133 
0134   ErrorOr &operator=(ErrorOr &&Other) {
0135     moveAssign(std::move(Other));
0136     return *this;
0137   }
0138 
0139   ~ErrorOr() {
0140     if (!HasError)
0141       getStorage()->~storage_type();
0142   }
0143 
0144   /// Return false if there is an error.
0145   explicit operator bool() const {
0146     return !HasError;
0147   }
0148 
0149   reference get() { return *getStorage(); }
0150   const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
0151 
0152   std::error_code getError() const {
0153     return HasError ? *getErrorStorage() : std::error_code();
0154   }
0155 
0156   pointer operator ->() {
0157     return toPointer(getStorage());
0158   }
0159 
0160   const_pointer operator->() const { return toPointer(getStorage()); }
0161 
0162   reference operator *() {
0163     return *getStorage();
0164   }
0165 
0166   const_reference operator*() const { return *getStorage(); }
0167 
0168 private:
0169   template <class OtherT>
0170   void copyConstruct(const ErrorOr<OtherT> &Other) {
0171     if (!Other.HasError) {
0172       // Get the other value.
0173       HasError = false;
0174       new (getStorage()) storage_type(*Other.getStorage());
0175     } else {
0176       // Get other's error.
0177       HasError = true;
0178       new (getErrorStorage()) std::error_code(Other.getError());
0179     }
0180   }
0181 
0182   template <class T1>
0183   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
0184     return &a == &b;
0185   }
0186 
0187   template <class T1, class T2>
0188   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
0189     return false;
0190   }
0191 
0192   template <class OtherT>
0193   void copyAssign(const ErrorOr<OtherT> &Other) {
0194     if (compareThisIfSameType(*this, Other))
0195       return;
0196 
0197     this->~ErrorOr();
0198     new (this) ErrorOr(Other);
0199   }
0200 
0201   template <class OtherT>
0202   void moveConstruct(ErrorOr<OtherT> &&Other) {
0203     if (!Other.HasError) {
0204       // Get the other value.
0205       HasError = false;
0206       new (getStorage()) storage_type(std::move(*Other.getStorage()));
0207     } else {
0208       // Get other's error.
0209       HasError = true;
0210       new (getErrorStorage()) std::error_code(Other.getError());
0211     }
0212   }
0213 
0214   template <class OtherT>
0215   void moveAssign(ErrorOr<OtherT> &&Other) {
0216     if (compareThisIfSameType(*this, Other))
0217       return;
0218 
0219     this->~ErrorOr();
0220     new (this) ErrorOr(std::move(Other));
0221   }
0222 
0223   pointer toPointer(pointer Val) {
0224     return Val;
0225   }
0226 
0227   const_pointer toPointer(const_pointer Val) const { return Val; }
0228 
0229   pointer toPointer(wrap *Val) {
0230     return &Val->get();
0231   }
0232 
0233   const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
0234 
0235   storage_type *getStorage() {
0236     assert(!HasError && "Cannot get value when an error exists!");
0237     return reinterpret_cast<storage_type *>(&TStorage);
0238   }
0239 
0240   const storage_type *getStorage() const {
0241     assert(!HasError && "Cannot get value when an error exists!");
0242     return reinterpret_cast<const storage_type *>(&TStorage);
0243   }
0244 
0245   std::error_code *getErrorStorage() {
0246     assert(HasError && "Cannot get error when a value exists!");
0247     return reinterpret_cast<std::error_code *>(&ErrorStorage);
0248   }
0249 
0250   const std::error_code *getErrorStorage() const {
0251     return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
0252   }
0253 
0254   union {
0255     AlignedCharArrayUnion<storage_type> TStorage;
0256     AlignedCharArrayUnion<std::error_code> ErrorStorage;
0257   };
0258   bool HasError : 1;
0259 };
0260 
0261 template <class T, class E>
0262 std::enable_if_t<std::is_error_code_enum<E>::value ||
0263                      std::is_error_condition_enum<E>::value,
0264                  bool>
0265 operator==(const ErrorOr<T> &Err, E Code) {
0266   return Err.getError() == Code;
0267 }
0268 
0269 } // end namespace llvm
0270 
0271 #endif // LLVM_SUPPORT_ERROROR_H