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