Path: blob/main/contrib/llvm-project/compiler-rt/lib/orc/error.h
39566 views
//===-------- error.h - Enforced error checking for ORC RT ------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#ifndef ORC_RT_ERROR_H9#define ORC_RT_ERROR_H1011#include "compiler.h"12#include "extensible_rtti.h"13#include "stl_extras.h"1415#include <cassert>16#include <memory>17#include <string>18#include <type_traits>1920namespace __orc_rt {2122/// Base class for all errors.23class ErrorInfoBase : public RTTIExtends<ErrorInfoBase, RTTIRoot> {24public:25virtual std::string toString() const = 0;26};2728/// Represents an environmental error.29class ORC_RT_NODISCARD Error {3031template <typename ErrT, typename... ArgTs>32friend Error make_error(ArgTs &&...Args);3334friend Error repackage_error(std::unique_ptr<ErrorInfoBase>);3536template <typename ErrT> friend std::unique_ptr<ErrT> error_cast(Error &);3738template <typename T> friend class Expected;3940public:41/// Destroy this error. Aborts if error was not checked, or was checked but42/// not handled.43~Error() { assertIsChecked(); }4445Error(const Error &) = delete;46Error &operator=(const Error &) = delete;4748/// Move-construct an error. The newly constructed error is considered49/// unchecked, even if the source error had been checked. The original error50/// becomes a checked success value.51Error(Error &&Other) {52setChecked(true);53*this = std::move(Other);54}5556/// Move-assign an error value. The current error must represent success, you57/// you cannot overwrite an unhandled error. The current error is then58/// considered unchecked. The source error becomes a checked success value,59/// regardless of its original state.60Error &operator=(Error &&Other) {61// Don't allow overwriting of unchecked values.62assertIsChecked();63setPtr(Other.getPtr());6465// This Error is unchecked, even if the source error was checked.66setChecked(false);6768// Null out Other's payload and set its checked bit.69Other.setPtr(nullptr);70Other.setChecked(true);7172return *this;73}7475/// Create a success value.76static Error success() { return Error(); }7778/// Error values convert to true for failure values, false otherwise.79explicit operator bool() {80setChecked(getPtr() == nullptr);81return getPtr() != nullptr;82}8384/// Return true if this Error contains a failure value of the given type.85template <typename ErrT> bool isA() const {86return getPtr() && getPtr()->isA<ErrT>();87}8889private:90Error() = default;9192Error(std::unique_ptr<ErrorInfoBase> ErrInfo) {93auto RawErrPtr = reinterpret_cast<uintptr_t>(ErrInfo.release());94assert((RawErrPtr & 0x1) == 0 && "ErrorInfo is insufficiently aligned");95ErrPtr = RawErrPtr | 0x1;96}9798void assertIsChecked() {99if (ORC_RT_UNLIKELY(!isChecked() || getPtr())) {100fprintf(stderr, "Error must be checked prior to destruction.\n");101abort(); // Some sort of JIT program abort?102}103}104105template <typename ErrT = ErrorInfoBase> ErrT *getPtr() const {106return reinterpret_cast<ErrT *>(ErrPtr & ~uintptr_t(1));107}108109void setPtr(ErrorInfoBase *Ptr) {110ErrPtr = (reinterpret_cast<uintptr_t>(Ptr) & ~uintptr_t(1)) | (ErrPtr & 1);111}112113bool isChecked() const { return ErrPtr & 0x1; }114115void setChecked(bool Checked) { ErrPtr = (ErrPtr & ~uintptr_t(1)) | Checked; }116117template <typename ErrT = ErrorInfoBase> std::unique_ptr<ErrT> takePayload() {118static_assert(std::is_base_of<ErrorInfoBase, ErrT>::value,119"ErrT is not an ErrorInfoBase subclass");120std::unique_ptr<ErrT> Tmp(getPtr<ErrT>());121setPtr(nullptr);122setChecked(true);123return Tmp;124}125126uintptr_t ErrPtr = 0;127};128129/// Construct an error of ErrT with the given arguments.130template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&...Args) {131static_assert(std::is_base_of<ErrorInfoBase, ErrT>::value,132"ErrT is not an ErrorInfoBase subclass");133return Error(std::make_unique<ErrT>(std::forward<ArgTs>(Args)...));134}135136/// Construct an error of ErrT using a std::unique_ptr<ErrorInfoBase>. The137/// primary use-case for this is 're-packaging' errors after inspecting them138/// using error_cast, hence the name.139inline Error repackage_error(std::unique_ptr<ErrorInfoBase> EIB) {140return Error(std::move(EIB));141}142143/// If the argument is an error of type ErrT then this function unpacks it144/// and returns a std::unique_ptr<ErrT>. Otherwise returns a nullptr and145/// leaves the error untouched. Common usage looks like:146///147/// \code{.cpp}148/// if (Error E = foo()) {149/// if (auto EV1 = error_cast<ErrorType1>(E)) {150/// // use unwrapped EV1 value.151/// } else if (EV2 = error_cast<ErrorType2>(E)) {152/// // use unwrapped EV2 value.153/// } ...154/// }155/// \endcode156template <typename ErrT> std::unique_ptr<ErrT> error_cast(Error &Err) {157static_assert(std::is_base_of<ErrorInfoBase, ErrT>::value,158"ErrT is not an ErrorInfoBase subclass");159if (Err.isA<ErrT>())160return Err.takePayload<ErrT>();161return nullptr;162}163164/// Helper for Errors used as out-parameters.165/// Sets the 'checked' flag on construction, resets it on destruction.166class ErrorAsOutParameter {167public:168ErrorAsOutParameter(Error *Err) : Err(Err) {169// Raise the checked bit if Err is success.170if (Err)171(void)!!*Err;172}173174~ErrorAsOutParameter() {175// Clear the checked bit.176if (Err && !*Err)177*Err = Error::success();178}179180private:181Error *Err;182};183184template <typename T> class ORC_RT_NODISCARD Expected {185186template <class OtherT> friend class Expected;187188static constexpr bool IsRef = std::is_reference<T>::value;189using wrap = std::reference_wrapper<std::remove_reference_t<T>>;190using error_type = std::unique_ptr<ErrorInfoBase>;191using storage_type = std::conditional_t<IsRef, wrap, T>;192using value_type = T;193194using reference = std::remove_reference_t<T> &;195using const_reference = const std::remove_reference_t<T> &;196using pointer = std::remove_reference_t<T> *;197using const_pointer = const std::remove_reference_t<T> *;198199public:200/// Create an Expected from a failure value.201Expected(Error Err) : HasError(true), Unchecked(true) {202assert(Err && "Cannot create Expected<T> from Error success value");203new (getErrorStorage()) error_type(Err.takePayload());204}205206/// Create an Expected from a T value.207template <typename OtherT>208Expected(OtherT &&Val,209std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr)210: HasError(false), Unchecked(true) {211new (getStorage()) storage_type(std::forward<OtherT>(Val));212}213214/// Move-construct an Expected<T> from an Expected<OtherT>.215Expected(Expected &&Other) { moveConstruct(std::move(Other)); }216217/// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT218/// must be convertible to T.219template <class OtherT>220Expected(221Expected<OtherT> &&Other,222std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) {223moveConstruct(std::move(Other));224}225226/// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT227/// isn't convertible to T.228template <class OtherT>229explicit Expected(230Expected<OtherT> &&Other,231std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) {232moveConstruct(std::move(Other));233}234235/// Move-assign from another Expected<T>.236Expected &operator=(Expected &&Other) {237moveAssign(std::move(Other));238return *this;239}240241/// Destroy an Expected<T>.242~Expected() {243assertIsChecked();244if (!HasError)245getStorage()->~storage_type();246else247getErrorStorage()->~error_type();248}249250/// Returns true if this Expected value is in a success state (holding a T),251/// and false if this Expected value is in a failure state.252explicit operator bool() {253Unchecked = HasError;254return !HasError;255}256257/// Returns true if this Expected value holds an Error of type error_type.258template <typename ErrT> bool isFailureOfType() const {259return HasError && (*getErrorStorage())->template isFailureOfType<ErrT>();260}261262/// Take ownership of the stored error.263///264/// If this Expected value is in a success state (holding a T) then this265/// method is a no-op and returns Error::success.266///267/// If thsi Expected value is in a failure state (holding an Error) then this268/// method returns the contained error and leaves this Expected in an269/// 'empty' state from which it may be safely destructed but not otherwise270/// accessed.271Error takeError() {272Unchecked = false;273return HasError ? Error(std::move(*getErrorStorage())) : Error::success();274}275276/// Returns a pointer to the stored T value.277pointer operator->() {278assertIsChecked();279return toPointer(getStorage());280}281282/// Returns a pointer to the stored T value.283const_pointer operator->() const {284assertIsChecked();285return toPointer(getStorage());286}287288/// Returns a reference to the stored T value.289reference operator*() {290assertIsChecked();291return *getStorage();292}293294/// Returns a reference to the stored T value.295const_reference operator*() const {296assertIsChecked();297return *getStorage();298}299300private:301template <class T1>302static bool compareThisIfSameType(const T1 &a, const T1 &b) {303return &a == &b;304}305306template <class T1, class T2>307static bool compareThisIfSameType(const T1 &a, const T2 &b) {308return false;309}310311template <class OtherT> void moveConstruct(Expected<OtherT> &&Other) {312HasError = Other.HasError;313Unchecked = true;314Other.Unchecked = false;315316if (!HasError)317new (getStorage()) storage_type(std::move(*Other.getStorage()));318else319new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage()));320}321322template <class OtherT> void moveAssign(Expected<OtherT> &&Other) {323assertIsChecked();324325if (compareThisIfSameType(*this, Other))326return;327328this->~Expected();329new (this) Expected(std::move(Other));330}331332pointer toPointer(pointer Val) { return Val; }333334const_pointer toPointer(const_pointer Val) const { return Val; }335336pointer toPointer(wrap *Val) { return &Val->get(); }337338const_pointer toPointer(const wrap *Val) const { return &Val->get(); }339340storage_type *getStorage() {341assert(!HasError && "Cannot get value when an error exists!");342return reinterpret_cast<storage_type *>(&TStorage);343}344345const storage_type *getStorage() const {346assert(!HasError && "Cannot get value when an error exists!");347return reinterpret_cast<const storage_type *>(&TStorage);348}349350error_type *getErrorStorage() {351assert(HasError && "Cannot get error when a value exists!");352return reinterpret_cast<error_type *>(&ErrorStorage);353}354355const error_type *getErrorStorage() const {356assert(HasError && "Cannot get error when a value exists!");357return reinterpret_cast<const error_type *>(&ErrorStorage);358}359360void assertIsChecked() {361if (ORC_RT_UNLIKELY(Unchecked)) {362fprintf(stderr,363"Expected<T> must be checked before access or destruction.\n");364abort();365}366}367368union {369std::aligned_union_t<1, storage_type> TStorage;370std::aligned_union_t<1, error_type> ErrorStorage;371};372373bool HasError : 1;374bool Unchecked : 1;375};376377/// Consume an error without doing anything.378inline void consumeError(Error Err) {379if (Err)380(void)error_cast<ErrorInfoBase>(Err);381}382383/// Consumes success values. It is a programmatic error to call this function384/// on a failure value.385inline void cantFail(Error Err) {386assert(!Err && "cantFail called on failure value");387consumeError(std::move(Err));388}389390/// Auto-unwrap an Expected<T> value in the success state. It is a programmatic391/// error to call this function on a failure value.392template <typename T> T cantFail(Expected<T> E) {393assert(E && "cantFail called on failure value");394consumeError(E.takeError());395return std::move(*E);396}397398/// Auto-unwrap an Expected<T> value in the success state. It is a programmatic399/// error to call this function on a failure value.400template <typename T> T &cantFail(Expected<T &> E) {401assert(E && "cantFail called on failure value");402consumeError(E.takeError());403return *E;404}405406/// Convert the given error to a string. The error value is consumed in the407/// process.408inline std::string toString(Error Err) {409if (auto EIB = error_cast<ErrorInfoBase>(Err))410return EIB->toString();411return {};412}413414class StringError : public RTTIExtends<StringError, ErrorInfoBase> {415public:416StringError(std::string ErrMsg) : ErrMsg(std::move(ErrMsg)) {}417std::string toString() const override { return ErrMsg; }418419private:420std::string ErrMsg;421};422423} // end namespace __orc_rt424425#endif // ORC_RT_ERROR_H426427428