mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-27 02:01:42 +00:00
Update LLVM to latest upstream. (#1080)
Also change header guards to WPI header guards. Remove StringRef::c_str() customization, replacing the handful of uses with Twine or SmallString. TCPStream: Include errno.h and make Windows includes lowercase for consistency. Upstream LLVM version: eb4186cca7924fb1706357545311a2fa3de40c59
This commit is contained in:
291
wpiutil/src/main/native/include/wpi/ErrorOr.h
Normal file
291
wpiutil/src/main/native/include/wpi/ErrorOr.h
Normal file
@@ -0,0 +1,291 @@
|
||||
//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
///
|
||||
/// Provides ErrorOr<T> smart pointer.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_ERROROR_H
|
||||
#define WPIUTIL_WPI_ERROROR_H
|
||||
|
||||
#include "wpi/AlignOf.h"
|
||||
#include <cassert>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// Stores a reference that can be changed.
|
||||
template <typename T>
|
||||
class ReferenceStorage {
|
||||
T *Storage;
|
||||
|
||||
public:
|
||||
ReferenceStorage(T &Ref) : Storage(&Ref) {}
|
||||
|
||||
operator T &() const { return *Storage; }
|
||||
T &get() const { return *Storage; }
|
||||
};
|
||||
|
||||
/// Represents either an error or a value T.
|
||||
///
|
||||
/// ErrorOr<T> 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 and optional user data that describes why it failed.
|
||||
///
|
||||
/// It is used like the following.
|
||||
/// \code
|
||||
/// ErrorOr<Buffer> 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<std::remove_reference<T>::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 T>
|
||||
class ErrorOr {
|
||||
template <class OtherT> friend class ErrorOr;
|
||||
|
||||
static const bool isRef = std::is_reference<T>::value;
|
||||
|
||||
using wrap = ReferenceStorage<typename std::remove_reference<T>::type>;
|
||||
|
||||
public:
|
||||
using storage_type = typename std::conditional<isRef, wrap, T>::type;
|
||||
|
||||
private:
|
||||
using reference = typename std::remove_reference<T>::type &;
|
||||
using const_reference = const typename std::remove_reference<T>::type &;
|
||||
using pointer = typename std::remove_reference<T>::type *;
|
||||
using const_pointer = const typename std::remove_reference<T>::type *;
|
||||
|
||||
public:
|
||||
template <class E>
|
||||
ErrorOr(E ErrorCode,
|
||||
typename std::enable_if<std::is_error_code_enum<E>::value ||
|
||||
std::is_error_condition_enum<E>::value,
|
||||
void *>::type = 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 <class OtherT>
|
||||
ErrorOr(OtherT &&Val,
|
||||
typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
|
||||
* = nullptr)
|
||||
: HasError(false) {
|
||||
new (getStorage()) storage_type(std::forward<OtherT>(Val));
|
||||
}
|
||||
|
||||
ErrorOr(const ErrorOr &Other) {
|
||||
copyConstruct(Other);
|
||||
}
|
||||
|
||||
template <class OtherT>
|
||||
ErrorOr(
|
||||
const ErrorOr<OtherT> &Other,
|
||||
typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
|
||||
nullptr) {
|
||||
copyConstruct(Other);
|
||||
}
|
||||
|
||||
template <class OtherT>
|
||||
explicit ErrorOr(
|
||||
const ErrorOr<OtherT> &Other,
|
||||
typename std::enable_if<
|
||||
!std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
|
||||
copyConstruct(Other);
|
||||
}
|
||||
|
||||
ErrorOr(ErrorOr &&Other) {
|
||||
moveConstruct(std::move(Other));
|
||||
}
|
||||
|
||||
template <class OtherT>
|
||||
ErrorOr(
|
||||
ErrorOr<OtherT> &&Other,
|
||||
typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
|
||||
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 <class OtherT>
|
||||
explicit ErrorOr(
|
||||
ErrorOr<OtherT> &&Other,
|
||||
typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
|
||||
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<ErrorOr<T> *>(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 <class OtherT>
|
||||
void copyConstruct(const ErrorOr<OtherT> &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 <class T1>
|
||||
static bool compareThisIfSameType(const T1 &a, const T1 &b) {
|
||||
return &a == &b;
|
||||
}
|
||||
|
||||
template <class T1, class T2>
|
||||
static bool compareThisIfSameType(const T1 &a, const T2 &b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class OtherT>
|
||||
void copyAssign(const ErrorOr<OtherT> &Other) {
|
||||
if (compareThisIfSameType(*this, Other))
|
||||
return;
|
||||
|
||||
this->~ErrorOr();
|
||||
new (this) ErrorOr(Other);
|
||||
}
|
||||
|
||||
template <class OtherT>
|
||||
void moveConstruct(ErrorOr<OtherT> &&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 <class OtherT>
|
||||
void moveAssign(ErrorOr<OtherT> &&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 reinterpret_cast<storage_type*>(TStorage.buffer);
|
||||
}
|
||||
|
||||
const storage_type *getStorage() const {
|
||||
assert(!HasError && "Cannot get value when an error exists!");
|
||||
return reinterpret_cast<const storage_type*>(TStorage.buffer);
|
||||
}
|
||||
|
||||
std::error_code *getErrorStorage() {
|
||||
assert(HasError && "Cannot get error when a value exists!");
|
||||
return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
|
||||
}
|
||||
|
||||
const std::error_code *getErrorStorage() const {
|
||||
return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
|
||||
}
|
||||
|
||||
union {
|
||||
AlignedCharArrayUnion<storage_type> TStorage;
|
||||
AlignedCharArrayUnion<std::error_code> ErrorStorage;
|
||||
};
|
||||
bool HasError : 1;
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
typename std::enable_if<std::is_error_code_enum<E>::value ||
|
||||
std::is_error_condition_enum<E>::value,
|
||||
bool>::type
|
||||
operator==(const ErrorOr<T> &Err, E Code) {
|
||||
return Err.getError() == Code;
|
||||
}
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // LLVM_SUPPORT_ERROROR_H
|
||||
Reference in New Issue
Block a user