/*
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