/* RCC-Project Copyright (C) 2026 Richard Qin This file is part of RCC-Project. RCC-Project is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RCC-Project; see the file COPYING. If not see . */ //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// /// Provides ErrorOr smart pointer. /// //===----------------------------------------------------------------------===// #ifndef RCC_SUPPORT_ERROROR_H #define RCC_SUPPORT_ERROROR_H #include #include // for std::reference_wrapper #include #include #include namespace rcc { /// Represents either an error or a value T. /// /// ErrorOr is a pointer-like class that represents the result of an /// operation. The result is either an error, or a value of type T. This is /// designed to emulate the usage of returning a pointer where nullptr indicates /// failure. However instead of just knowing that the operation failed, we also /// have an error_code that describes why it failed. /// /// It is used like the following. /// \code /// ErrorOr getBuffer(); /// /// auto buffer = getBuffer(); /// if (error_code ec = buffer.getError()) /// return ec; /// buffer->write("adena"); /// \endcode /// /// /// Implicit conversion to bool returns true if there is a usable value. The /// unary * and -> operators provide pointer like access to the value. Accessing /// the value when there is an error has undefined behavior. /// /// When T is a reference type the behavior is slightly different. The reference /// is held in a std::reference_wrapper::type>, and /// there is special handling to make operator -> work as if T was not a /// reference. /// /// T cannot be a rvalue reference. template class ErrorOr { template friend class ErrorOr; static constexpr bool isRef = std::is_reference_v; using wrap = std::reference_wrapper>; public: using storage_type = std::conditional_t; private: using reference = std::remove_reference_t &; using const_reference = const std::remove_reference_t &; using pointer = std::remove_reference_t *; using const_pointer = const std::remove_reference_t *; public: template ErrorOr (E ErrorCode, std::enable_if_t::value || std::is_error_condition_enum::value, void *> = nullptr) : HasError (true) { new (getErrorStorage ()) std::error_code (make_error_code (ErrorCode)); } ErrorOr (std::error_code EC) : HasError (true) { new (getErrorStorage ()) std::error_code (EC); } template ErrorOr (OtherT &&Val, std::enable_if_t> * = nullptr) : HasError (false) { new (getStorage ()) storage_type (std::forward (Val)); } ErrorOr (const ErrorOr &Other) { copyConstruct (Other); } template ErrorOr (const ErrorOr &Other, std::enable_if_t> * = nullptr) { copyConstruct (Other); } template explicit ErrorOr ( const ErrorOr &Other, std::enable_if_t> * = nullptr) { copyConstruct (Other); } ErrorOr (ErrorOr &&Other) { moveConstruct (std::move (Other)); } template ErrorOr (ErrorOr &&Other, std::enable_if_t> * = nullptr) { moveConstruct (std::move (Other)); } // This might eventually need SFINAE but it's more complex than is_convertible // & I'm too lazy to write it right now. template explicit ErrorOr ( ErrorOr &&Other, std::enable_if_t> * = nullptr) { moveConstruct (std::move (Other)); } ErrorOr & operator= (const ErrorOr &Other) { copyAssign (Other); return *this; } ErrorOr & operator= (ErrorOr &&Other) { moveAssign (std::move (Other)); return *this; } ~ErrorOr () { if (!HasError) { getStorage ()->~storage_type (); } } /// Return false if there is an error. explicit operator bool () const { return !HasError; } reference get () { return *getStorage (); } const_reference get () const { return const_cast *> (this)->get (); } std::error_code getError () const { return HasError ? *getErrorStorage () : std::error_code (); } pointer operator->() { return toPointer (getStorage ()); } const_pointer operator->() const { return toPointer (getStorage ()); } reference operator* () { return *getStorage (); } const_reference operator* () const { return *getStorage (); } private: template void copyConstruct (const ErrorOr &Other) { if (!Other.HasError) { // Get the other value. HasError = false; new (getStorage ()) storage_type (*Other.getStorage ()); } else { // Get other's error. HasError = true; new (getErrorStorage ()) std::error_code (Other.getError ()); } } template static bool compareThisIfSameType (const T1 &a, const T1 &b) { return &a == &b; } template static bool compareThisIfSameType (const T1 &, const T2 &) { return false; } template void copyAssign (const ErrorOr &Other) { if (compareThisIfSameType (*this, Other)) { return; } this->~ErrorOr (); new (this) ErrorOr (Other); } template void moveConstruct (ErrorOr &&Other) { if (!Other.HasError) { // Get the other value. HasError = false; new (getStorage ()) storage_type (std::move (*Other.getStorage ())); } else { // Get other's error. HasError = true; new (getErrorStorage ()) std::error_code (Other.getError ()); } } template void moveAssign (ErrorOr &&Other) { if (compareThisIfSameType (*this, Other)) { return; } this->~ErrorOr (); new (this) ErrorOr (std::move (Other)); } pointer toPointer (pointer Val) { return Val; } const_pointer toPointer (const_pointer Val) const { return Val; } pointer toPointer (wrap *Val) { return &Val->get (); } const_pointer toPointer (const wrap *Val) const { return &Val->get (); } storage_type * getStorage () { assert (!HasError && "Cannot get value when an error exists!"); return &TStorage; } const storage_type * getStorage () const { assert (!HasError && "Cannot get value when an error exists!"); return &TStorage; } std::error_code * getErrorStorage () { assert (HasError && "Cannot get error when a value exists!"); return &ErrorStorage; } const std::error_code * getErrorStorage () const { assert (HasError && "Cannot get error when a value exists!"); return &ErrorStorage; } union { storage_type TStorage; std::error_code ErrorStorage; }; bool HasError : 1; }; template std::enable_if_t::value || std::is_error_condition_enum::value, bool> operator== (const ErrorOr &Err, E Code) { return Err.getError () == Code; } } // end namespace rcc #endif // RCC_SUPPORT_ERROROR_H