[upstream_utils] Upgrade to LLVM 22.1.6 (#8919)

This commit is contained in:
Tyler Veness
2026-05-26 16:25:29 -07:00
committed by GitHub
parent 1392db4529
commit 254ca64106
86 changed files with 3236 additions and 2328 deletions

View File

@@ -84,7 +84,7 @@
# define ConvertUTF_RESTORE_WARNINGS \
_Pragma("clang diagnostic pop")
# endif
#elif defined(__GNUC__) && __GNUC__ > 6
#elif defined(__GNUC__)
# define ConvertUTF_DISABLE_WARNINGS \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")

View File

@@ -305,5 +305,15 @@ bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result
}
}
bool IsSingleCodeUnitUTF8Codepoint(unsigned V) { return V <= 0x7F; }
bool IsSingleCodeUnitUTF16Codepoint(unsigned V) {
return V <= 0xD7FF || (V >= 0xE000 && V <= 0xFFFF);
}
bool IsSingleCodeUnitUTF32Codepoint(unsigned V) {
return V <= 0xD7FF || (V >= 0xE000 && V <= 0x10FFFF);
}
} // end namespace wpi::util

View File

@@ -14,6 +14,7 @@
#include "wpi/util/ErrorHandling.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/Errc.hpp"
#include "wpi/util/Errno.hpp"
#include "wpi/util/WindowsError.hpp"
#include "wpi/util/print.hpp"
#include <cassert>
@@ -50,6 +51,21 @@ static void *BadAllocErrorHandlerUserData = nullptr;
static std::mutex ErrorHandlerMutex;
static std::mutex BadAllocErrorHandlerMutex;
static bool write_retry(int fd, const char *buf, size_t count) {
while (count > 0) {
#ifdef _WIN32
int written = sys::RetryAfterSignal(-1, ::_write, fd, buf, count);
#else
ssize_t written = sys::RetryAfterSignal(-1, ::write, fd, buf, count);
#endif
if (written <= 0)
return false;
buf += written;
count -= written;
}
return true;
}
void wpi::util::install_fatal_error_handler(fatal_error_handler_t handler,
void *user_data) {
std::scoped_lock Lock(ErrorHandlerMutex);
@@ -92,6 +108,25 @@ void wpi::util::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
exit(1);
}
void wpi::util::reportFatalInternalError(const char *reason) {
report_fatal_error(reason, /*GenCrashDiag=*/true);
}
void wpi::util::reportFatalInternalError(const std::string& reason) {
report_fatal_error(reason, /*GenCrashDiag=*/true);
}
void wpi::util::reportFatalInternalError(std::string_view reason) {
report_fatal_error(reason, /*GenCrashDiag=*/true);
}
void wpi::util::reportFatalUsageError(const char *reason) {
report_fatal_error(reason, /*GenCrashDiag=*/false);
}
void wpi::util::reportFatalUsageError(const std::string& reason) {
report_fatal_error(reason, /*GenCrashDiag=*/false);
}
void wpi::util::reportFatalUsageError(std::string_view reason) {
report_fatal_error(reason, /*GenCrashDiag=*/false);
}
void wpi::util::install_bad_alloc_error_handler(fatal_error_handler_t handler,
void *user_data) {
std::scoped_lock Lock(BadAllocErrorHandlerMutex);
@@ -127,15 +162,9 @@ void wpi::util::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
// an OOM to stderr and abort.
const char *OOMMessage = "LLVM ERROR: out of memory\n";
const char *Newline = "\n";
#ifdef _WIN32
(void)!::_write(2, OOMMessage, strlen(OOMMessage));
(void)!::_write(2, Reason, strlen(Reason));
(void)!::_write(2, Newline, strlen(Newline));
#else
(void)!::write(2, OOMMessage, strlen(OOMMessage));
(void)!::write(2, Reason, strlen(Reason));
(void)!::write(2, Newline, strlen(Newline));
#endif
write_retry(2, OOMMessage, strlen(OOMMessage));
write_retry(2, Reason, strlen(Reason));
write_retry(2, Newline, strlen(Newline));
abort();
}

View File

@@ -28,7 +28,7 @@ void SmallPtrSetImplBase::shrink_and_clear() {
// Reduce the number of buckets.
unsigned Size = size();
CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
NumNonEmpty = NumTombstones = 0;
NumEntries = NumTombstones = 0;
// Install the new array. Clear all the buckets to empty.
CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
@@ -41,7 +41,8 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
// If more than 3/4 of the array is full, grow.
Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
} else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
} else if (LLVM_UNLIKELY(CurArraySize - NumEntries - NumTombstones <
CurArraySize / 8)) {
// If fewer of 1/8 of the array is empty (meaning that many are filled with
// tombstones), rehash.
Grow(CurArraySize);
@@ -50,16 +51,15 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
// Okay, we know we have space. Find a hash bucket.
const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
if (*Bucket == Ptr)
return std::make_pair(Bucket, false); // Already inserted, good.
return {Bucket, false}; // Already inserted, good.
// Otherwise, insert it!
if (*Bucket == getTombstoneMarker())
--NumTombstones;
else
++NumNonEmpty; // Track density.
++NumEntries;
*Bucket = Ptr;
incrementEpoch();
return std::make_pair(Bucket, true);
return {Bucket, true};
}
const void *const *SmallPtrSetImplBase::doFind(const void *Ptr) const {
@@ -110,8 +110,7 @@ const void *const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
/// Grow - Allocate a larger backing store for the buckets and move it over.
///
void SmallPtrSetImplBase::Grow(unsigned NewSize) {
const void **OldBuckets = CurArray;
const void **OldEnd = EndPointer();
auto OldBuckets = buckets();
bool WasSmall = isSmall();
// Install the new array. Clear all the buckets to empty.
@@ -123,16 +122,14 @@ void SmallPtrSetImplBase::Grow(unsigned NewSize) {
memset(CurArray, -1, NewSize*sizeof(void*));
// Copy over all valid entries.
for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
for (const void *&Bucket : OldBuckets) {
// Copy over the element if it is valid.
const void *Elt = *BucketPtr;
if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
*const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
if (Bucket != getTombstoneMarker() && Bucket != getEmptyMarker())
*const_cast<void **>(FindBucketFor(Bucket)) = const_cast<void *>(Bucket);
}
if (!WasSmall)
free(OldBuckets);
NumNonEmpty -= NumTombstones;
free(OldBuckets.begin());
NumTombstones = 0;
IsSmall = false;
}
@@ -193,9 +190,9 @@ void SmallPtrSetImplBase::copyHelper(const SmallPtrSetImplBase &RHS) {
CurArraySize = RHS.CurArraySize;
// Copy over the contents from the other set
std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
std::ranges::copy(RHS.buckets(), CurArray);
NumNonEmpty = RHS.NumNonEmpty;
NumEntries = RHS.NumEntries;
NumTombstones = RHS.NumTombstones;
}
@@ -217,7 +214,7 @@ void SmallPtrSetImplBase::moveHelper(const void **SmallStorage,
if (RHS.isSmall()) {
// Copy a small RHS rather than moving.
CurArray = SmallStorage;
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
std::ranges::copy(RHS.small_buckets(), CurArray);
} else {
CurArray = RHS.CurArray;
RHS.CurArray = RHSSmallStorage;
@@ -225,13 +222,13 @@ void SmallPtrSetImplBase::moveHelper(const void **SmallStorage,
// Copy the rest of the trivial members.
CurArraySize = RHS.CurArraySize;
NumNonEmpty = RHS.NumNonEmpty;
NumEntries = RHS.NumEntries;
NumTombstones = RHS.NumTombstones;
IsSmall = RHS.IsSmall;
// Make the RHS small and empty.
RHS.CurArraySize = SmallSize;
RHS.NumNonEmpty = 0;
RHS.NumEntries = 0;
RHS.NumTombstones = 0;
RHS.IsSmall = true;
}
@@ -245,54 +242,42 @@ void SmallPtrSetImplBase::swap(const void **SmallStorage,
if (!this->isSmall() && !RHS.isSmall()) {
std::swap(this->CurArray, RHS.CurArray);
std::swap(this->CurArraySize, RHS.CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap(this->NumEntries, RHS.NumEntries);
std::swap(this->NumTombstones, RHS.NumTombstones);
return;
}
// FIXME: From here on we assume that both sets have the same small size.
// If only RHS is small, copy the small elements into LHS and move the pointer
// from LHS to RHS.
if (!this->isSmall() && RHS.isSmall()) {
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, SmallStorage);
std::swap(RHS.CurArraySize, this->CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap(this->NumTombstones, RHS.NumTombstones);
RHS.CurArray = this->CurArray;
RHS.IsSmall = false;
this->CurArray = SmallStorage;
this->IsSmall = true;
return;
}
// If only LHS is small, copy the small elements into RHS and move the pointer
// from RHS to LHS.
if (this->isSmall() && !RHS.isSmall()) {
std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
RHSSmallStorage);
std::swap(RHS.CurArraySize, this->CurArraySize);
std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
std::swap(RHS.NumTombstones, this->NumTombstones);
this->CurArray = RHS.CurArray;
this->IsSmall = false;
RHS.CurArray = RHSSmallStorage;
RHS.IsSmall = true;
return;
}
// Both a small, just swap the small elements.
assert(this->isSmall() && RHS.isSmall());
unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap_ranges(this->CurArray, this->CurArray + MinNonEmpty, RHS.CurArray);
if (this->NumNonEmpty > MinNonEmpty) {
std::copy(this->CurArray + MinNonEmpty, this->CurArray + this->NumNonEmpty,
RHS.CurArray + MinNonEmpty);
} else {
std::copy(RHS.CurArray + MinNonEmpty, RHS.CurArray + RHS.NumNonEmpty,
this->CurArray + MinNonEmpty);
if (this->isSmall() && RHS.isSmall()) {
unsigned MinEntries = std::min(this->NumEntries, RHS.NumEntries);
std::swap_ranges(this->CurArray, this->CurArray + MinEntries, RHS.CurArray);
if (this->NumEntries > MinEntries) {
std::copy(this->CurArray + MinEntries, this->CurArray + this->NumEntries,
RHS.CurArray + MinEntries);
} else {
std::copy(RHS.CurArray + MinEntries, RHS.CurArray + RHS.NumEntries,
this->CurArray + MinEntries);
}
assert(this->CurArraySize == RHS.CurArraySize);
std::swap(this->NumEntries, RHS.NumEntries);
std::swap(this->NumTombstones, RHS.NumTombstones);
return;
}
assert(this->CurArraySize == RHS.CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap(this->NumTombstones, RHS.NumTombstones);
// If only one side is small, copy the small elements into the large side and
// move the pointer from the large side to the small side.
SmallPtrSetImplBase &SmallSide = this->isSmall() ? *this : RHS;
SmallPtrSetImplBase &LargeSide = this->isSmall() ? RHS : *this;
const void **LargeSideInlineStorage =
this->isSmall() ? RHSSmallStorage : SmallStorage;
std::ranges::copy(SmallSide.small_buckets(), LargeSideInlineStorage);
std::swap(LargeSide.CurArraySize, SmallSide.CurArraySize);
std::swap(LargeSide.NumEntries, SmallSide.NumEntries);
std::swap(LargeSide.NumTombstones, SmallSide.NumTombstones);
SmallSide.CurArray = LargeSide.CurArray;
SmallSide.IsSmall = false;
LargeSide.CurArray = LargeSideInlineStorage;
LargeSide.IsSmall = true;
}

View File

@@ -82,15 +82,15 @@ inline bool RunningWindows8OrGreater() {
}
/// Determines if the program is running on Windows 11 or Windows Server 2022.
bool RunningWindows11OrGreater();
LLVM_ABI bool RunningWindows11OrGreater();
/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
/// GetVersionEx is deprecated, but this API exposes the build number which can
/// be useful for working around certain kernel bugs.
wpi::util::VersionTuple GetWindowsOSVersion();
LLVM_ABI wpi::util::VersionTuple GetWindowsOSVersion();
bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
LLVM_ABI bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
// Include GetLastError() in a fatal error message.
[[noreturn]] inline void ReportLastErrorFatal(const char *Msg) {

View File

@@ -21,6 +21,7 @@
#include "wpi/util/Compiler.hpp"
#include "wpi/util/ErrorHandling.hpp"
#include "wpi/util/fs.hpp"
#include "wpi/util/IOSandbox.hpp"
#include "wpi/util/MathExtras.hpp"
#include <algorithm>
#include <cerrno>
@@ -59,17 +60,6 @@
using namespace wpi::util;
constexpr raw_ostream::Colors raw_ostream::BLACK;
constexpr raw_ostream::Colors raw_ostream::RED;
constexpr raw_ostream::Colors raw_ostream::GREEN;
constexpr raw_ostream::Colors raw_ostream::YELLOW;
constexpr raw_ostream::Colors raw_ostream::BLUE;
constexpr raw_ostream::Colors raw_ostream::MAGENTA;
constexpr raw_ostream::Colors raw_ostream::CYAN;
constexpr raw_ostream::Colors raw_ostream::WHITE;
constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR;
constexpr raw_ostream::Colors raw_ostream::RESET;
raw_ostream::~raw_ostream() {
// raw_ostream's subclasses should take care to flush the buffer
// in their destructors.
@@ -288,6 +278,9 @@ void raw_ostream::anchor() {}
static int getFD(std::string_view Filename, std::error_code &EC,
fs::CreationDisposition Disp, fs::FileAccess Access,
fs::OpenFlags Flags) {
// FIXME(sandboxing): Remove this by adopting `wpi::util::vfs::OutputBackend`.
auto BypassSandbox = sys::sandbox::scopedDisable();
assert((Access & fs::FA_Write) &&
"Cannot make a raw_ostream from a read-only descriptor!");
@@ -347,6 +340,9 @@ raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
OStreamKind K)
: raw_pwrite_stream(unbuffered, K), FD(fd), ShouldClose(shouldClose) {
// FIXME(sandboxing): Remove this by adopting `wpi::util::vfs::OutputBackend`.
auto BypassSandbox = sys::sandbox::scopedDisable();
if (FD < 0 ) {
ShouldClose = false;
return;
@@ -402,8 +398,7 @@ raw_fd_ostream::~raw_fd_ostream() {
// has_error() and clear the error flag with clear_error() before
// destructing raw_ostream objects which may have errors.
if (has_error())
report_fatal_error("IO failure on output stream: " + error().message(),
/*gen_crash_diag=*/false);
reportFatalUsageError("IO failure on output stream: " + error().message());
}
#if defined(_WIN32)
@@ -596,6 +591,7 @@ void raw_fd_ostream::anchor() {}
raw_fd_ostream &wpi::util::outs() {
// Set buffer settings to model stdout behavior.
std::error_code EC;
static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::OF_None);
assert(!EC);
return *S;

View File

@@ -14,15 +14,16 @@
#define WPIUTIL_WPI_ALIGNOF_H
#include <algorithm>
#include <cstddef>
namespace wpi::util {
/// A suitably aligned and sized character array member which can hold elements
/// of any type.
template <typename... Ts> struct AlignedCharArrayUnion {
alignas((std::max)({alignof(Ts)...}))
std::byte buffer[(std::max)({static_cast<size_t>(1), sizeof(Ts)...})];
template <typename T, typename... Ts> struct AlignedCharArrayUnion {
// Work around "internal compiler error: Segmentation fault" with GCC 7.5,
// apparently caused by alignas(Ts...).
static constexpr std::size_t Align = (std::max)({alignof(T), alignof(Ts)...});
alignas(Align) char buffer[(std::max)({sizeof(T), sizeof(Ts)...})];
};
} // end namespace wpi::util

View File

@@ -28,6 +28,7 @@
#include "wpi/util/Compiler.hpp"
#include "wpi/util/MemAlloc.hpp"
#include <type_traits>
#include <utility>
namespace wpi::util {
@@ -111,7 +112,7 @@ template <typename Alloc> class AllocatorHolder : Alloc {
public:
AllocatorHolder() = default;
AllocatorHolder(const Alloc &A) : Alloc(A) {}
AllocatorHolder(Alloc &&A) : Alloc(static_cast<Alloc &&>(A)) {}
AllocatorHolder(Alloc &&A) : Alloc(std::move(A)) {}
Alloc &getAllocator() { return *this; }
const Alloc &getAllocator() const { return *this; }
};

View File

@@ -340,7 +340,7 @@ struct ValueFromPointerCast
/// during the cast. It's also a good example of how to implement a move-only
/// cast.
template <typename To, typename From, typename Derived = void>
struct UniquePtrCast : public CastIsPossible<To, From *> {
struct UniquePtrCast : CastIsPossible<To, From *> {
using Self = detail::SelfType<Derived, UniquePtrCast<To, From>>;
using CastResultType = std::unique_ptr<
std::remove_reference_t<typename cast_retty<To, From>::ret_type>>;
@@ -473,7 +473,7 @@ struct ForwardToPointerCast {
// take advantage of the cast traits whenever possible!
template <typename To, typename From, typename Enable = void>
struct CastInfo : public CastIsPossible<To, From> {
struct CastInfo : CastIsPossible<To, From> {
using Self = CastInfo<To, From, Enable>;
using CastReturnType = typename cast_retty<To, From>::ret_type;
@@ -536,22 +536,16 @@ struct CastInfo<To, std::unique_ptr<From>> : public UniquePtrCast<To, From> {};
/// the input is std::optional<From> that the output can be std::optional<To>.
/// If that's not the case, specialize CastInfo for your use case.
template <typename To, typename From>
struct CastInfo<To, std::optional<From>> : public OptionalValueCast<To, From> {
};
struct CastInfo<To, std::optional<From>> : OptionalValueCast<To, From> {};
/// isa<X> - Return true if the parameter to the template is an instance of one
/// of the template type arguments. Used like this:
///
/// if (isa<Type>(myVal)) { ... }
/// if (isa<Type0, Type1, Type2>(myVal)) { ... }
template <typename To, typename From>
template <typename... To, typename From>
[[nodiscard]] inline bool isa(const From &Val) {
return CastInfo<To, const From>::isPossible(Val);
}
template <typename First, typename Second, typename... Rest, typename From>
[[nodiscard]] inline bool isa(const From &Val) {
return isa<First>(Val) || isa<Second, Rest...>(Val);
return (CastInfo<To, const From>::isPossible(Val) || ...);
}
/// cast<X> - Return the argument parameter cast to the specified type. This
@@ -822,6 +816,42 @@ template <typename... Types> struct IsaAndPresentCheckPredicate {
return isa_and_present<Types...>(Val);
}
};
//===----------------------------------------------------------------------===//
// Casting Function Objects
//===----------------------------------------------------------------------===//
/// Usable in generic algorithms like map_range
template <typename U> struct StaticCastFunc {
template <typename T> decltype(auto) operator()(T &&Val) const {
return static_cast<U>(Val);
}
};
template <typename U> struct DynCastFunc {
template <typename T> decltype(auto) operator()(T &&Val) const {
return dyn_cast<U>(Val);
}
};
template <typename U> struct CastFunc {
template <typename T> decltype(auto) operator()(T &&Val) const {
return cast<U>(Val);
}
};
template <typename U> struct CastIfPresentFunc {
template <typename T> decltype(auto) operator()(T &&Val) const {
return cast_if_present<U>(Val);
}
};
template <typename U> struct DynCastIfPresentFunc {
template <typename T> decltype(auto) operator()(T &&Val) const {
return dyn_cast_if_present<U>(Val);
}
};
} // namespace detail
/// Function object wrapper for the `wpi::util::isa` type check. The function call
@@ -847,6 +877,20 @@ template <typename... Types>
inline constexpr detail::IsaAndPresentCheckPredicate<Types...>
IsaAndPresentPred{};
/// Function objects corresponding to the Cast types defined above.
template <typename To>
inline constexpr detail::StaticCastFunc<To> StaticCastTo{};
template <typename To> inline constexpr detail::CastFunc<To> CastTo{};
template <typename To>
inline constexpr detail::CastIfPresentFunc<To> CastIfPresentTo{};
template <typename To>
inline constexpr detail::DynCastIfPresentFunc<To> DynCastIfPresentTo{};
template <typename To> inline constexpr detail::DynCastFunc<To> DynCastTo{};
} // end namespace wpi::util
#endif // WPIUTIL_WPI_CASTING_H

View File

@@ -76,8 +76,8 @@ toTimePoint(std::time_t T, uint32_t nsec) {
} // namespace sys
raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
raw_ostream &operator<<(raw_ostream &OS, sys::UtcTime<> TP);
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, sys::UtcTime<> TP);
} // namespace wpi::util

View File

@@ -38,6 +38,10 @@
# define __has_builtin(x) 0
#endif
#ifndef __has_warning
# define __has_warning(x) 0
#endif
// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in
// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid.
#ifndef LLVM_HAS_CPP_ATTRIBUTE
@@ -130,7 +134,7 @@
#endif
#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \
(defined(__MINGW32__) && defined(__clang__)))
((defined(__MINGW32__) || defined(__CYGWIN__)) && defined(__clang__)))
#define LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_VISIBILITY_HIDDEN
// Clang compilers older then 15 do not support gnu style attributes on
// namespaces.
@@ -168,26 +172,19 @@
/// for both functions and classes. On windows its turned in to dllimport for
/// library consumers, for other platforms its a default visibility attribute.
///
/// LLVM_C_ABI is used to annotated functions and data that need to be exported
/// for the libllvm-c API. This used both for the llvm-c headers and for the
/// functions declared in the different Target's c++ source files that don't
/// include the header forward declaring them.
/// LLVM_ABI_FOR_TEST is for annotating symbols that are only exported because
/// they are imported from a test. These symbols are not technically part of the
/// LLVM public interface and could be conditionally excluded when not building
/// tests in the future.
///
#ifndef LLVM_ABI_GENERATING_ANNOTATIONS
// Marker to add to classes or functions in public headers that should not have
// export macros added to them by the clang tool
#define LLVM_ABI_NOT_EXPORTED
#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) || \
defined(LLVM_ENABLE_PLUGINS)
// Some libraries like those for tablegen are linked in to tools that used
// in the build so can't depend on the llvm shared library. If export macros
// were left enabled when building these we would get duplicate or
// missing symbol linker errors on windows.
#if defined(LLVM_BUILD_STATIC)
#define LLVM_ABI
#define LLVM_TEMPLATE_ABI
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT
#elif defined(_WIN32) && !defined(__MINGW32__)
// TODO(https://github.com/llvm/llvm-project/issues/145406): eliminate need for
// two preprocessor definitions to gate LLVM_ABI macro definitions.
#if defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) && !defined(LLVM_BUILD_STATIC)
#if defined(_WIN32) && !defined(__MINGW32__)
#if defined(LLVM_EXPORTS)
#define LLVM_ABI __declspec(dllexport)
#define LLVM_TEMPLATE_ABI
@@ -198,25 +195,28 @@
#define LLVM_EXPORT_TEMPLATE
#endif
#define LLVM_ABI_EXPORT __declspec(dllexport)
#elif defined(__ELF__) || defined(__MINGW32__) || defined(_AIX) || \
defined(__MVS__)
#define LLVM_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#define LLVM_TEMPLATE_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#elif __has_attribute(visibility)
#if defined(__ELF__) || defined(__MINGW32__) || defined(_AIX) || \
defined(__MVS__) || defined(__CYGWIN__)
#define LLVM_ABI __attribute__((visibility("default")))
#define LLVM_TEMPLATE_ABI LLVM_ABI
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#define LLVM_ABI_EXPORT LLVM_ABI
#elif defined(__MACH__) || defined(__WASM__) || defined(__EMSCRIPTEN__)
#define LLVM_ABI LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#define LLVM_ABI __attribute__((visibility("default")))
#define LLVM_TEMPLATE_ABI
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT LLVM_ATTRIBUTE_VISIBILITY_DEFAULT
#define LLVM_ABI_EXPORT LLVM_ABI
#endif
#else
#endif
#endif
#if !defined(LLVM_ABI)
#define LLVM_ABI
#define LLVM_TEMPLATE_ABI
#define LLVM_EXPORT_TEMPLATE
#define LLVM_ABI_EXPORT
#endif
#define LLVM_C_ABI LLVM_ABI
#define LLVM_ABI_FOR_TEST LLVM_ABI
#endif
#ifndef LLVM_PREFETCH
@@ -227,6 +227,12 @@
#endif
#endif
#if __has_attribute(uninitialized)
#define LLVM_ATTRIBUTE_UNINITIALIZED __attribute__((uninitialized))
#else
#define LLVM_ATTRIBUTE_UNINITIALIZED
#endif
#ifndef LLVM_ATTRIBUTE_USED
#if __has_attribute(used)
#define LLVM_ATTRIBUTE_USED __attribute__((__used__))
@@ -235,6 +241,16 @@
#endif
#endif
// Only enabled for clang:
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99587
// GCC may produce "warning: 'retain' attribute ignored" (despite
// __has_attribute(retain) being 1).
#if defined(__clang__) && __has_attribute(retain)
#define LLVM_ATTRIBUTE_RETAIN __attribute__((__retain__))
#else
#define LLVM_ATTRIBUTE_RETAIN
#endif
#if defined(__clang__)
#define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX)))
#else
@@ -657,7 +673,8 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
// FIXME: Move this to a private config.h as it's not usable in public headers.
#ifndef LLVM_DUMP_METHOD
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
#define LLVM_DUMP_METHOD \
LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED LLVM_ATTRIBUTE_RETAIN
#else
#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE
#endif
@@ -724,4 +741,37 @@ void AnnotateIgnoreWritesEnd(const char *file, int line);
#define LLVM_PREFERRED_TYPE(T)
#endif
#if LLVM_HAS_CPP_ATTRIBUTE(clang::ptrauth_vtable_pointer) && \
(defined(__PTRAUTH__) || __has_feature(ptrauth_calls))
#define LLVM_MOVABLE_POLYMORPHIC_TYPE \
[[clang::ptrauth_vtable_pointer(default_key, no_address_discrimination, \
default_extra_discrimination)]]
#else
#define LLVM_MOVABLE_POLYMORPHIC_TYPE
#endif
/// \macro LLVM_VIRTUAL_ANCHOR_FUNCTION
/// This macro is used to adhere to LLVM's policy that each class with a vtable
/// must have at least one out-of-line virtual function. This macro allows us
/// to declare such a function in `final` classes without triggering a warning.
// clang-format off
// Autoformatting makes this look awful.
#if defined(__clang__)
// Make sure this is only parsed if __clang__ is defined
#if __has_warning("-Wunnecessary-virtual-specifier")
#define LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunnecessary-virtual-specifier\"") \
virtual void anchor() \
_Pragma("clang diagnostic pop")
#else // __has_warning
#define LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \
virtual void anchor()
#endif
#else // defined(__clang__)
#define LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \
virtual void anchor()
#endif
// clang-format on
#endif

View File

@@ -47,7 +47,7 @@
Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several funtions are included here, forming a complete set of
Several functions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.
@@ -105,6 +105,7 @@
#ifndef WPIUTIL_WPI_CONVERTUTF_H
#define WPIUTIL_WPI_CONVERTUTF_H
#include "wpi/util/Compiler.hpp"
#include <cstddef>
#include <string>
#include <span>
@@ -124,10 +125,10 @@ namespace wpi::util {
bit mask & shift operations.
------------------------------------------------------------------------ */
typedef unsigned int UTF32; /* at least 32 bits */
typedef unsigned short UTF16; /* at least 16 bits */
typedef unsigned char UTF8; /* typically 8 bits */
typedef bool Boolean; /* 0 or 1 */
using UTF32 = unsigned int; /* at least 32 bits */
using UTF16 = unsigned short; /* at least 16 bits */
using UTF8 = unsigned char; /* typically 8 bits */
using Boolean = bool; /* 0 or 1 */
/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
@@ -144,61 +145,73 @@ typedef bool Boolean; /* 0 or 1 */
#define UNI_UTF32_BYTE_ORDER_MARK_NATIVE 0x0000FEFF
#define UNI_UTF32_BYTE_ORDER_MARK_SWAPPED 0xFFFE0000
typedef enum {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
} ConversionResult;
enum ConversionResult {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
};
typedef enum {
strictConversion = 0,
lenientConversion
} ConversionFlags;
enum ConversionFlags { strictConversion = 0, lenientConversion };
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
LLVM_ABI ConversionResult ConvertUTF8toUTF16(const UTF8 **sourceStart,
const UTF8 *sourceEnd,
UTF16 **targetStart,
UTF16 *targetEnd,
ConversionFlags flags);
/**
* Convert a partial UTF8 sequence to UTF32. If the sequence ends in an
* incomplete code unit sequence, returns \c sourceExhausted.
*/
ConversionResult ConvertUTF8toUTF32Partial(
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
LLVM_ABI ConversionResult ConvertUTF8toUTF32Partial(const UTF8 **sourceStart,
const UTF8 *sourceEnd,
UTF32 **targetStart,
UTF32 *targetEnd,
ConversionFlags flags);
/**
* Convert a partial UTF8 sequence to UTF32. If the sequence ends in an
* incomplete code unit sequence, returns \c sourceIllegal.
*/
ConversionResult ConvertUTF8toUTF32(
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
LLVM_ABI ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
const UTF8 *sourceEnd,
UTF32 **targetStart,
UTF32 *targetEnd,
ConversionFlags flags);
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
LLVM_ABI ConversionResult ConvertUTF16toUTF8(const UTF16 **sourceStart,
const UTF16 *sourceEnd,
UTF8 **targetStart,
UTF8 *targetEnd,
ConversionFlags flags);
ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
LLVM_ABI ConversionResult ConvertUTF32toUTF8(const UTF32 **sourceStart,
const UTF32 *sourceEnd,
UTF8 **targetStart,
UTF8 *targetEnd,
ConversionFlags flags);
ConversionResult ConvertUTF16toUTF32 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
LLVM_ABI ConversionResult ConvertUTF16toUTF32(const UTF16 **sourceStart,
const UTF16 *sourceEnd,
UTF32 **targetStart,
UTF32 *targetEnd,
ConversionFlags flags);
ConversionResult ConvertUTF32toUTF16 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
LLVM_ABI ConversionResult ConvertUTF32toUTF16(const UTF32 **sourceStart,
const UTF32 *sourceEnd,
UTF16 **targetStart,
UTF16 *targetEnd,
ConversionFlags flags);
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
LLVM_ABI Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd);
LLVM_ABI Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd);
unsigned getUTF8SequenceSize(const UTF8 *source, const UTF8 *sourceEnd);
LLVM_ABI unsigned getUTF8SequenceSize(const UTF8 *source,
const UTF8 *sourceEnd);
unsigned getNumBytesForUTF8(UTF8 firstByte);
LLVM_ABI unsigned getNumBytesForUTF8(UTF8 firstByte);
/*************************************************************************/
/* Below are LLVM-specific wrappers of the functions above. */
@@ -214,27 +227,27 @@ template <typename T> class SmallVectorImpl;
* the first character which could not be converted.
* \return true on success.
*/
bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
char *&ResultPtr, const UTF8 *&ErrorPtr);
LLVM_ABI bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
char *&ResultPtr, const UTF8 *&ErrorPtr);
/**
* Converts a UTF-8 string_view to a std::wstring.
* \return true on success.
*/
bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result);
LLVM_ABI bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result);
/**
* Converts a UTF-8 C-string to a std::wstring.
* \return true on success.
*/
bool ConvertUTF8toWide(const char *Source, std::wstring &Result);
LLVM_ABI bool ConvertUTF8toWide(const char *Source, std::wstring &Result);
/**
* Converts a std::wstring to a UTF-8 encoded std::string.
* \return true on success.
*/
bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result);
LLVM_ABI bool convertWideToUTF8(const std::wstring &Source,
SmallVectorImpl<char> &Result);
/**
* Convert an Unicode code point to UTF8 sequence.
@@ -246,7 +259,7 @@ bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result
*
* \returns true on success.
*/
bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr);
LLVM_ABI bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr);
/**
* Convert the first UTF8 sequence in the given source buffer to a UTF32
@@ -279,7 +292,7 @@ inline ConversionResult convertUTF8Sequence(const UTF8 **source,
* Returns true if a blob of text starts with a UTF-16 big or little endian byte
* order mark.
*/
bool hasUTF16ByteOrderMark(std::span<const char> SrcBytes);
LLVM_ABI bool hasUTF16ByteOrderMark(std::span<const char> SrcBytes);
/**
* Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string.
@@ -288,7 +301,8 @@ bool hasUTF16ByteOrderMark(std::span<const char> SrcBytes);
* \param [out] Out Converted UTF-8 is stored here on success.
* \returns true on success
*/
bool convertUTF16ToUTF8String(std::span<const char> SrcBytes, SmallVectorImpl<char> &Out);
LLVM_ABI bool convertUTF16ToUTF8String(std::span<const char> SrcBytes,
SmallVectorImpl<char> &Out);
/**
* Converts a UTF16 string into a UTF8 std::string.
@@ -297,7 +311,8 @@ bool convertUTF16ToUTF8String(std::span<const char> SrcBytes, SmallVectorImpl<ch
* \param [out] Out Converted UTF-8 is stored here on success.
* \returns true on success
*/
bool convertUTF16ToUTF8String(std::span<const UTF16> Src, SmallVectorImpl<char> &Out);
LLVM_ABI bool convertUTF16ToUTF8String(std::span<const UTF16> Src,
SmallVectorImpl<char> &Out);
/**
* Converts a stream of raw bytes assumed to be UTF32 into a UTF8 std::string.
@@ -306,7 +321,8 @@ bool convertUTF16ToUTF8String(std::span<const UTF16> Src, SmallVectorImpl<char>
* \param [out] Out Converted UTF-8 is stored here on success.
* \returns true on success
*/
bool convertUTF32ToUTF8String(std::span<const char> SrcBytes, std::string &Out);
LLVM_ABI bool convertUTF32ToUTF8String(std::span<const char> SrcBytes,
std::string &Out);
/**
* Converts a UTF32 string into a UTF8 std::string.
@@ -315,27 +331,33 @@ bool convertUTF32ToUTF8String(std::span<const char> SrcBytes, std::string &Out);
* \param [out] Out Converted UTF-8 is stored here on success.
* \returns true on success
*/
bool convertUTF32ToUTF8String(std::span<const UTF32> Src, std::string &Out);
LLVM_ABI bool convertUTF32ToUTF8String(std::span<const UTF32> Src, std::string &Out);
/**
* Converts a UTF-8 string into a UTF-16 string with native endianness.
*
* \returns true on success
*/
bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
SmallVectorImpl<UTF16> &DstUTF16);
LLVM_ABI bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
SmallVectorImpl<UTF16> &DstUTF16);
LLVM_ABI bool IsSingleCodeUnitUTF8Codepoint(unsigned);
LLVM_ABI bool IsSingleCodeUnitUTF16Codepoint(unsigned);
LLVM_ABI bool IsSingleCodeUnitUTF32Codepoint(unsigned);
#if defined(_WIN32)
namespace sys {
namespace windows {
std::error_code UTF8ToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
LLVM_ABI std::error_code UTF8ToUTF16(std::string_view utf8,
SmallVectorImpl<wchar_t> &utf16);
/// Convert to UTF16 from the current code page used in the system
std::error_code CurCPToUTF16(std::string_view utf8, SmallVectorImpl<wchar_t> &utf16);
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
LLVM_ABI std::error_code CurCPToUTF16(std::string_view utf8,
SmallVectorImpl<wchar_t> &utf16);
LLVM_ABI std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
/// Convert from UTF16 to the current code page used in the system
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
LLVM_ABI std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
} // namespace windows
} // namespace sys
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,8 @@
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <optional>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -50,10 +52,10 @@ inline unsigned combineHashValue(unsigned a, unsigned b) {
/// just be `void`.
template<typename T, typename Enable = void>
struct DenseMapInfo {
//static inline T getEmptyKey();
//static inline T getTombstoneKey();
//static unsigned getHashValue(const T &Val);
//static bool isEqual(const T &LHS, const T &RHS);
// static constexpr T getEmptyKey();
// static constexpr T getTombstoneKey();
// static unsigned getHashValue(const T &Val);
// static bool isEqual(const T &LHS, const T &RHS);
};
// Provide DenseMapInfo for all pointers. Come up with sentinel pointer values
@@ -69,13 +71,13 @@ struct DenseMapInfo<T*> {
// "Log2MaxAlign bits of alignment");
static constexpr uintptr_t Log2MaxAlign = 12;
static inline T* getEmptyKey() {
static constexpr T *getEmptyKey() {
uintptr_t Val = static_cast<uintptr_t>(-1);
Val <<= Log2MaxAlign;
return reinterpret_cast<T*>(Val);
}
static inline T* getTombstoneKey() {
static constexpr T *getTombstoneKey() {
uintptr_t Val = static_cast<uintptr_t>(-2);
Val <<= Log2MaxAlign;
return reinterpret_cast<T*>(Val);
@@ -91,8 +93,8 @@ struct DenseMapInfo<T*> {
// Provide DenseMapInfo for chars.
template<> struct DenseMapInfo<char> {
static inline char getEmptyKey() { return ~0; }
static inline char getTombstoneKey() { return ~0 - 1; }
static constexpr char getEmptyKey() { return ~0; }
static constexpr char getTombstoneKey() { return ~0 - 1; }
static unsigned getHashValue(const char& Val) { return Val * 37U; }
static bool isEqual(const char &LHS, const char &RHS) {
@@ -100,120 +102,33 @@ template<> struct DenseMapInfo<char> {
}
};
// Provide DenseMapInfo for unsigned chars.
template <> struct DenseMapInfo<unsigned char> {
static inline unsigned char getEmptyKey() { return ~0; }
static inline unsigned char getTombstoneKey() { return ~0 - 1; }
static unsigned getHashValue(const unsigned char &Val) { return Val * 37U; }
// Provide DenseMapInfo for all integral types except char.
//
// The "char" case is excluded because it uses ~0 as the empty key despite
// "char" being a signed type. "std::is_same_v<T, char>" is included below
// for clarity; technically, we do not need it because the explicit
// specialization above "wins",
template <typename T>
struct DenseMapInfo<
T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, char>>> {
static constexpr T getEmptyKey() { return (std::numeric_limits<T>::max)(); }
static bool isEqual(const unsigned char &LHS, const unsigned char &RHS) {
return LHS == RHS;
}
};
// Provide DenseMapInfo for unsigned shorts.
template <> struct DenseMapInfo<unsigned short> {
static inline unsigned short getEmptyKey() { return 0xFFFF; }
static inline unsigned short getTombstoneKey() { return 0xFFFF - 1; }
static unsigned getHashValue(const unsigned short &Val) { return Val * 37U; }
static bool isEqual(const unsigned short &LHS, const unsigned short &RHS) {
return LHS == RHS;
}
};
// Provide DenseMapInfo for unsigned ints.
template<> struct DenseMapInfo<unsigned> {
static inline unsigned getEmptyKey() { return ~0U; }
static inline unsigned getTombstoneKey() { return ~0U - 1; }
static unsigned getHashValue(const unsigned& Val) { return Val * 37U; }
static bool isEqual(const unsigned& LHS, const unsigned& RHS) {
return LHS == RHS;
}
};
// Provide DenseMapInfo for unsigned longs.
template<> struct DenseMapInfo<unsigned long> {
static inline unsigned long getEmptyKey() { return ~0UL; }
static inline unsigned long getTombstoneKey() { return ~0UL - 1L; }
static unsigned getHashValue(const unsigned long& Val) {
if constexpr (sizeof(Val) == 4)
return DenseMapInfo<unsigned>::getHashValue(Val);
static constexpr T getTombstoneKey() {
if constexpr (std::is_unsigned_v<T> || std::is_same_v<T, long>)
return (std::numeric_limits<T>::max)() - 1;
else
return (std::numeric_limits<T>::min)();
}
static unsigned getHashValue(const T &Val) {
if constexpr (std::is_unsigned_v<T> && sizeof(T) > sizeof(unsigned))
return densemap::detail::mix(Val);
else
return static_cast<unsigned>(Val *
static_cast<std::make_unsigned_t<T>>(37U));
}
static bool isEqual(const unsigned long& LHS, const unsigned long& RHS) {
return LHS == RHS;
}
};
// Provide DenseMapInfo for unsigned long longs.
template<> struct DenseMapInfo<unsigned long long> {
static inline unsigned long long getEmptyKey() { return ~0ULL; }
static inline unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; }
static unsigned getHashValue(const unsigned long long& Val) {
return densemap::detail::mix(Val);
}
static bool isEqual(const unsigned long long& LHS,
const unsigned long long& RHS) {
return LHS == RHS;
}
};
// Provide DenseMapInfo for shorts.
template <> struct DenseMapInfo<short> {
static inline short getEmptyKey() { return 0x7FFF; }
static inline short getTombstoneKey() { return -0x7FFF - 1; }
static unsigned getHashValue(const short &Val) { return Val * 37U; }
static bool isEqual(const short &LHS, const short &RHS) { return LHS == RHS; }
};
// Provide DenseMapInfo for ints.
template<> struct DenseMapInfo<int> {
static inline int getEmptyKey() { return 0x7fffffff; }
static inline int getTombstoneKey() { return -0x7fffffff - 1; }
static unsigned getHashValue(const int& Val) { return (unsigned)(Val * 37U); }
static bool isEqual(const int& LHS, const int& RHS) {
return LHS == RHS;
}
};
// Provide DenseMapInfo for longs.
template<> struct DenseMapInfo<long> {
static inline long getEmptyKey() {
return (1UL << (sizeof(long) * 8 - 1)) - 1UL;
}
static inline long getTombstoneKey() { return getEmptyKey() - 1L; }
static unsigned getHashValue(const long& Val) {
return (unsigned)(Val * 37UL);
}
static bool isEqual(const long& LHS, const long& RHS) {
return LHS == RHS;
}
};
// Provide DenseMapInfo for long longs.
template<> struct DenseMapInfo<long long> {
static inline long long getEmptyKey() { return 0x7fffffffffffffffLL; }
static inline long long getTombstoneKey() { return -0x7fffffffffffffffLL-1; }
static unsigned getHashValue(const long long& Val) {
return (unsigned)(Val * 37ULL);
}
static bool isEqual(const long long& LHS,
const long long& RHS) {
return LHS == RHS;
}
static bool isEqual(const T &LHS, const T &RHS) { return LHS == RHS; }
};
// Provide DenseMapInfo for all pairs whose members have info.
@@ -223,14 +138,12 @@ struct DenseMapInfo<std::pair<T, U>> {
using FirstInfo = DenseMapInfo<T>;
using SecondInfo = DenseMapInfo<U>;
static inline Pair getEmptyKey() {
return std::make_pair(FirstInfo::getEmptyKey(),
SecondInfo::getEmptyKey());
static constexpr Pair getEmptyKey() {
return {FirstInfo::getEmptyKey(), SecondInfo::getEmptyKey()};
}
static inline Pair getTombstoneKey() {
return std::make_pair(FirstInfo::getTombstoneKey(),
SecondInfo::getTombstoneKey());
static constexpr Pair getTombstoneKey() {
return {FirstInfo::getTombstoneKey(), SecondInfo::getTombstoneKey()};
}
static unsigned getHashValue(const Pair& PairVal) {
@@ -256,49 +169,39 @@ struct DenseMapInfo<std::pair<T, U>> {
template <typename... Ts> struct DenseMapInfo<std::tuple<Ts...>> {
using Tuple = std::tuple<Ts...>;
static inline Tuple getEmptyKey() {
static constexpr Tuple getEmptyKey() {
return Tuple(DenseMapInfo<Ts>::getEmptyKey()...);
}
static inline Tuple getTombstoneKey() {
static constexpr Tuple getTombstoneKey() {
return Tuple(DenseMapInfo<Ts>::getTombstoneKey()...);
}
template <unsigned I>
static unsigned getHashValueImpl(const Tuple &values, std::false_type) {
using EltType = std::tuple_element_t<I, Tuple>;
std::integral_constant<bool, I + 1 == sizeof...(Ts)> atEnd;
return detail::combineHashValue(
DenseMapInfo<EltType>::getHashValue(std::get<I>(values)),
getHashValueImpl<I + 1>(values, atEnd));
}
template <unsigned I>
static unsigned getHashValueImpl(const Tuple &, std::true_type) {
return 0;
template <unsigned I> static unsigned getHashValueImpl(const Tuple &values) {
if constexpr (I == sizeof...(Ts)) {
return 0;
} else {
using EltType = std::tuple_element_t<I, Tuple>;
return detail::combineHashValue(
DenseMapInfo<EltType>::getHashValue(std::get<I>(values)),
getHashValueImpl<I + 1>(values));
}
}
static unsigned getHashValue(const std::tuple<Ts...> &values) {
std::integral_constant<bool, 0 == sizeof...(Ts)> atEnd;
return getHashValueImpl<0>(values, atEnd);
return getHashValueImpl<0>(values);
}
template <unsigned I>
static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs, std::false_type) {
using EltType = std::tuple_element_t<I, Tuple>;
std::integral_constant<bool, I + 1 == sizeof...(Ts)> atEnd;
return DenseMapInfo<EltType>::isEqual(std::get<I>(lhs), std::get<I>(rhs)) &&
isEqualImpl<I + 1>(lhs, rhs, atEnd);
}
template <unsigned I>
static bool isEqualImpl(const Tuple &, const Tuple &, std::true_type) {
return true;
template <std::size_t... Is>
static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs,
std::index_sequence<Is...>) {
return (DenseMapInfo<std::tuple_element_t<Is, Tuple>>::isEqual(
std::get<Is>(lhs), std::get<Is>(rhs)) &&
...);
}
static bool isEqual(const Tuple &lhs, const Tuple &rhs) {
std::integral_constant<bool, 0 == sizeof...(Ts)> atEnd;
return isEqualImpl<0>(lhs, rhs, atEnd);
return isEqualImpl(lhs, rhs, std::index_sequence_for<Ts...>{});
}
};
@@ -308,10 +211,22 @@ struct DenseMapInfo<Enum, std::enable_if_t<std::is_enum_v<Enum>>> {
using UnderlyingType = std::underlying_type_t<Enum>;
using Info = DenseMapInfo<UnderlyingType>;
static Enum getEmptyKey() { return static_cast<Enum>(Info::getEmptyKey()); }
// If an enum does not have a "fixed" underlying type, it may be UB to cast
// some values of the underlying type to the enum. We use an "extra" constexpr
// local to ensure that such UB would trigger "static assertion expression is
// not an integral constant expression", rather than runtime UB.
//
// If you hit this error, you can fix by switching to `enum class`, or adding
// an explicit underlying type (e.g. `enum X : int`) to the enum's definition.
static Enum getTombstoneKey() {
return static_cast<Enum>(Info::getTombstoneKey());
static constexpr Enum getEmptyKey() {
constexpr Enum V = static_cast<Enum>(Info::getEmptyKey());
return V;
}
static constexpr Enum getTombstoneKey() {
constexpr Enum V = static_cast<Enum>(Info::getTombstoneKey());
return V;
}
static unsigned getHashValue(const Enum &Val) {
@@ -320,6 +235,30 @@ struct DenseMapInfo<Enum, std::enable_if_t<std::is_enum_v<Enum>>> {
static bool isEqual(const Enum &LHS, const Enum &RHS) { return LHS == RHS; }
};
template <typename T> struct DenseMapInfo<std::optional<T>> {
using Optional = std::optional<T>;
using Info = DenseMapInfo<T>;
static constexpr Optional getEmptyKey() { return {Info::getEmptyKey()}; }
static constexpr Optional getTombstoneKey() {
return {Info::getTombstoneKey()};
}
static unsigned getHashValue(const Optional &OptionalVal) {
return detail::combineHashValue(
OptionalVal.has_value(),
Info::getHashValue(OptionalVal.value_or(Info::getEmptyKey())));
}
static bool isEqual(const Optional &LHS, const Optional &RHS) {
if (LHS && RHS) {
return Info::isEqual(LHS.value(), RHS.value());
}
return !LHS && !RHS;
}
};
} // end namespace wpi::util
#endif // WPIUTIL_WPI_DENSEMAPINFO_H

View File

@@ -49,7 +49,9 @@ template <typename value_type>
/// Swap the bytes of value to match the given endianness.
template <typename value_type, endianness endian>
[[nodiscard]] inline value_type byte_swap(value_type value) {
[[nodiscard]]
LLVM_DEPRECATED("Pass endian as a function argument instead",
"byte_swap") inline value_type byte_swap(value_type value) {
if constexpr (endian != wpi::util::endianness::native)
sys::swapByteOrder(value);
return value;
@@ -68,7 +70,9 @@ template <typename value_type, std::size_t alignment = unaligned>
}
template <typename value_type, endianness endian, std::size_t alignment>
[[nodiscard]] inline value_type read(const void *memory) {
[[nodiscard]] LLVM_DEPRECATED("Pass endian as a function argument instead",
"read") inline value_type
read(const void *memory) {
return read<value_type, alignment>(memory, endian);
}
@@ -98,9 +102,8 @@ inline void write(void *memory, value_type value, endianness endian) {
&value, sizeof(value_type));
}
template<typename value_type,
endianness endian,
std::size_t alignment>
template <typename value_type, endianness endian, std::size_t alignment>
LLVM_DEPRECATED("Pass endian as a function argument instead", "write")
inline void write(void *memory, value_type value) {
write<value_type, alignment>(memory, value, endian);
}
@@ -130,7 +133,7 @@ template <typename value_type, endianness endian, std::size_t alignment>
uint64_t startBit) {
assert(startBit < 8);
if (startBit == 0)
return read<value_type, endian, alignment>(memory);
return read<value_type, alignment>(memory, endian);
else {
// Read two values and compose the result from them.
value_type val[2];
@@ -138,8 +141,8 @@ template <typename value_type, endianness endian, std::size_t alignment>
LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
sizeof(value_type) * 2);
val[0] = byte_swap<value_type, endian>(val[0]);
val[1] = byte_swap<value_type, endian>(val[1]);
val[0] = byte_swap<value_type>(val[0], endian);
val[1] = byte_swap<value_type>(val[1], endian);
// Shift bits from the lower value into place.
make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
@@ -165,7 +168,7 @@ inline void writeAtBitAlignment(void *memory, value_type value,
uint64_t startBit) {
assert(startBit < 8);
if (startBit == 0)
write<value_type, endian, alignment>(memory, value);
write<value_type, alignment>(memory, value, endian);
else {
// Read two values and shift the result into them.
value_type val[2];
@@ -173,8 +176,8 @@ inline void writeAtBitAlignment(void *memory, value_type value,
LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
sizeof(value_type) * 2);
val[0] = byte_swap<value_type, endian>(val[0]);
val[1] = byte_swap<value_type, endian>(val[1]);
val[0] = byte_swap<value_type>(val[0], endian);
val[1] = byte_swap<value_type>(val[1], endian);
// Mask off any existing bits in the upper part of the lower value that
// we want to replace.
@@ -202,8 +205,8 @@ inline void writeAtBitAlignment(void *memory, value_type value,
val[1] |= upperVal;
// Finally, rewrite values.
val[0] = byte_swap<value_type, endian>(val[0]);
val[1] = byte_swap<value_type, endian>(val[1]);
val[0] = byte_swap<value_type>(val[0], endian);
val[1] = byte_swap<value_type>(val[1], endian);
memcpy(LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
&val[0], sizeof(value_type) * 2);
@@ -225,14 +228,15 @@ struct packed_endian_specific_integral {
explicit packed_endian_specific_integral(value_type val) { *this = val; }
operator value_type() const {
return endian::read<value_type, endian, alignment>(
(const void*)Value.buffer);
value_type value() const {
return endian::read<value_type, alignment>((const void *)Value.buffer,
endian);
}
operator value_type() const { return value(); }
void operator=(value_type newValue) {
endian::write<value_type, endian, alignment>(
(void*)Value.buffer, newValue);
endian::write<value_type, alignment>((void *)Value.buffer, newValue,
endian);
}
packed_endian_specific_integral &operator+=(value_type newValue) {
@@ -265,11 +269,11 @@ public:
explicit ref(void *Ptr) : Ptr(Ptr) {}
operator value_type() const {
return endian::read<value_type, endian, alignment>(Ptr);
return endian::read<value_type, alignment>(Ptr, endian);
}
void operator=(value_type NewValue) {
endian::write<value_type, endian, alignment>(Ptr, NewValue);
endian::write<value_type, alignment>(Ptr, NewValue, endian);
}
private:
@@ -279,6 +283,9 @@ public:
} // end namespace detail
using ulittle8_t =
detail::packed_endian_specific_integral<uint8_t, wpi::util::endianness::little,
unaligned>;
using ulittle16_t =
detail::packed_endian_specific_integral<uint16_t, wpi::util::endianness::little,
unaligned>;

View File

@@ -13,6 +13,7 @@
#ifndef WPIUTIL_WPI_ERRNO_H
#define WPIUTIL_WPI_ERRNO_H
#include "wpi/util/Compiler.hpp"
#include <cerrno>
#include <string>

View File

@@ -20,59 +20,81 @@
namespace wpi::util {
/// An error handler callback.
typedef void (*fatal_error_handler_t)(void *user_data,
const char *reason,
bool gen_crash_diag);
/// An error handler callback.
using fatal_error_handler_t = void (*)(void *user_data, const char *reason,
bool gen_crash_diag);
/// install_fatal_error_handler - Installs a new error handler to be used
/// whenever a serious (non-recoverable) error is encountered by LLVM.
///
/// If no error handler is installed the default is to print the error message
/// to stderr, and call exit(1). If an error handler is installed then it is
/// the handler's responsibility to log the message, it will no longer be
/// printed to stderr. If the error handler returns, then exit(1) will be
/// called.
///
/// It is dangerous to naively use an error handler which throws an exception.
/// Even though some applications desire to gracefully recover from arbitrary
/// faults, blindly throwing exceptions through unfamiliar code isn't a way to
/// achieve this.
///
/// \param user_data - An argument which will be passed to the install error
/// handler.
void install_fatal_error_handler(fatal_error_handler_t handler,
void *user_data = nullptr);
/// Restores default error handling behaviour.
void remove_fatal_error_handler();
/// ScopedFatalErrorHandler - This is a simple helper class which just
/// calls install_fatal_error_handler in its constructor and
/// remove_fatal_error_handler in its destructor.
struct ScopedFatalErrorHandler {
explicit ScopedFatalErrorHandler(fatal_error_handler_t handler,
void *user_data = nullptr) {
install_fatal_error_handler(handler, user_data);
}
~ScopedFatalErrorHandler() { remove_fatal_error_handler(); }
};
/// Reports a serious error, calling any installed error handler. These
/// functions are intended to be used for error conditions which are outside
/// the control of the compiler (I/O errors, invalid user input, etc.)
/// install_fatal_error_handler - Installs a new error handler to be used
/// whenever a serious (non-recoverable) error is encountered by LLVM.
///
/// If no error handler is installed the default is to print the message to
/// standard error, followed by a newline.
/// After the error handler is called this function will call abort(), it
/// does not return.
[[noreturn]] void report_fatal_error(const char *reason,
bool gen_crash_diag = true);
[[noreturn]] void report_fatal_error(const std::string &reason,
bool gen_crash_diag = true);
[[noreturn]] void report_fatal_error(std::string_view reason,
bool gen_crash_diag = true);
/// If no error handler is installed the default is to print the error message
/// to stderr, and call exit(1). If an error handler is installed then it is
/// the handler's responsibility to log the message, it will no longer be
/// printed to stderr. If the error handler returns, then exit(1) will be
/// called.
///
/// It is dangerous to naively use an error handler which throws an exception.
/// Even though some applications desire to gracefully recover from arbitrary
/// faults, blindly throwing exceptions through unfamiliar code isn't a way to
/// achieve this.
///
/// \param user_data - An argument which will be passed to the install error
/// handler.
LLVM_ABI void install_fatal_error_handler(fatal_error_handler_t handler,
void *user_data = nullptr);
/// Restores default error handling behaviour.
LLVM_ABI void remove_fatal_error_handler();
/// ScopedFatalErrorHandler - This is a simple helper class which just
/// calls install_fatal_error_handler in its constructor and
/// remove_fatal_error_handler in its destructor.
struct ScopedFatalErrorHandler {
explicit ScopedFatalErrorHandler(fatal_error_handler_t handler,
void *user_data = nullptr) {
install_fatal_error_handler(handler, user_data);
}
~ScopedFatalErrorHandler() { remove_fatal_error_handler(); }
};
/// @deprecated Use reportFatalInternalError() or reportFatalUsageError()
/// instead.
[[noreturn]] LLVM_ABI void report_fatal_error(const char *reason,
bool gen_crash_diag = true);
[[noreturn]] LLVM_ABI void report_fatal_error(const std::string& reason,
bool gen_crash_diag = true);
[[noreturn]] LLVM_ABI void report_fatal_error(std::string_view reason,
bool gen_crash_diag = true);
/// Report a fatal error that likely indicates a bug in LLVM. It serves a
/// similar purpose as an assertion, but is always enabled, regardless of the
/// value of NDEBUG.
///
/// This will call installed error handlers (or print the message by default)
/// and then abort. This will produce a crash trace and *will* ask users to
/// report an LLVM bug.
[[noreturn]] LLVM_ABI void reportFatalInternalError(const char *reason);
[[noreturn]] LLVM_ABI void reportFatalInternalError(const std::string& reason);
[[noreturn]] LLVM_ABI void reportFatalInternalError(std::string_view reason);
/// Report a fatal error that does not indicate a bug in LLVM.
///
/// This can be used in contexts where a proper error reporting mechanism
/// (such as Error/Expected or DiagnosticInfo) is currently not supported, and
/// would be too involved to introduce at the moment.
///
/// Examples where this function should be used instead of
/// reportFatalInternalError() include invalid inputs or options, but also
/// environment error conditions outside LLVM's control. It should also be used
/// for known unsupported/unimplemented functionality.
///
/// This will call installed error handlers (or print the message by default)
/// and then exit with code 1. It will not produce a crash trace and will
/// *not* ask users to report an LLVM bug.
[[noreturn]] LLVM_ABI void reportFatalUsageError(const char *reason);
[[noreturn]] LLVM_ABI void reportFatalUsageError(const std::string& reason);
[[noreturn]] LLVM_ABI void reportFatalUsageError(std::string_view reason);
/// Installs a new bad alloc error handler that should be used whenever a
/// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM.
@@ -90,13 +112,13 @@ namespace wpi::util {
///
/// \param user_data - An argument which will be passed to the installed error
/// handler.
void install_bad_alloc_error_handler(fatal_error_handler_t handler,
void *user_data = nullptr);
LLVM_ABI void install_bad_alloc_error_handler(fatal_error_handler_t handler,
void *user_data = nullptr);
/// Restores default bad alloc error handling behavior.
void remove_bad_alloc_error_handler();
LLVM_ABI void remove_bad_alloc_error_handler();
void install_out_of_memory_new_handler();
LLVM_ABI void install_out_of_memory_new_handler();
/// Reports a bad alloc error, calling any user defined bad alloc
/// error handler. In contrast to the generic 'report_fatal_error'
@@ -110,16 +132,16 @@ void install_out_of_memory_new_handler();
/// If no error handler is installed (default), throws a bad_alloc exception
/// if LLVM is compiled with exception support. Otherwise prints the error
/// to standard error and calls abort().
[[noreturn]] void report_bad_alloc_error(const char *Reason,
bool GenCrashDiag = true);
[[noreturn]] LLVM_ABI void report_bad_alloc_error(const char *Reason,
bool GenCrashDiag = true);
/// This function calls abort(), and prints the optional message to stderr.
/// Use the wpi_unreachable macro (that adds location info), instead of
/// calling this function directly.
[[noreturn]] void
wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr,
unsigned line = 0);
}
[[noreturn]] LLVM_ABI void wpi_unreachable_internal(const char *msg = nullptr,
const char *file = nullptr,
unsigned line = 0);
} // namespace wpi::util
/// Marks that the current location is not supposed to be reachable.
/// In !NDEBUG builds, prints the message and location info to stderr.
@@ -139,7 +161,7 @@ wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr,
/// diagnostics for unreachable code paths, and allows compilers to omit
/// unnecessary code.
#ifndef NDEBUG
#define wpi_unreachable(msg) \
#define wpi_unreachable(msg) \
::wpi::util::wpi_unreachable_internal(msg, __FILE__, __LINE__)
#elif !defined(LLVM_BUILTIN_UNREACHABLE)
#define wpi_unreachable(msg) ::wpi::util::wpi_unreachable_internal()

View File

@@ -39,7 +39,6 @@
#include "wpi/util/MemAlloc.hpp"
#include "wpi/util/type_traits.hpp"
#include <cstring>
#include <memory>
#include <type_traits>
namespace wpi::util {
@@ -65,10 +64,6 @@ template <typename FunctionT> class unique_function;
namespace detail {
template <typename T>
using EnableIfTrivial =
std::enable_if_t<std::is_trivially_move_constructible<T>::value &&
std::is_trivially_destructible<T>::value>;
template <typename CallableT, typename ThisT>
using EnableUnlessSameType =
std::enable_if_t<!std::is_same<remove_cvref_t<CallableT>, ThisT>::value>;
@@ -89,13 +84,6 @@ protected:
static constexpr size_t InlineStorageSize = sizeof(void *) * 4;
static constexpr size_t InlineStorageAlign = alignof(void *);
template <typename T, class = void>
struct IsSizeLessThanThresholdT : std::false_type {};
template <typename T>
struct IsSizeLessThanThresholdT<
T, std::enable_if_t<sizeof(T) <= 2 * sizeof(void *)>> : std::true_type {};
// Provide a type function to map parameters that won't observe extra copies
// or moves and which are small enough to likely pass in register to values
// and all other types to l-value reference types. We use this to compute the
@@ -108,10 +96,12 @@ protected:
template <typename T> struct AdjustedParamTBase {
static_assert(!std::is_reference<T>::value,
"references should be handled by template specialization");
static constexpr bool IsSizeLessThanThreshold =
sizeof(T) <= 2 * sizeof(void *);
using type =
std::conditional_t<std::is_trivially_copy_constructible<T>::value &&
std::is_trivially_move_constructible<T>::value &&
IsSizeLessThanThresholdT<T>::value,
IsSizeLessThanThreshold,
T, T &>;
};
@@ -242,22 +232,22 @@ protected:
// The pointers to call/move/destroy functions are determined for each
// callable type (and called-as type, which determines the overload chosen).
// (definitions are out-of-line).
// By default, we need an object that contains all the different
// type erased behaviors needed. Create a static instance of the struct type
// here and each instance will contain a pointer to it.
// Wrap in a struct to avoid https://gcc.gnu.org/PR71954
template <typename CallableT, typename CalledAs, typename Enable = void>
struct CallbacksHolder {
static NonTrivialCallbacks Callbacks;
};
// See if we can create a trivial callback. We need the callable to be
// trivially moved and trivially destroyed so that we don't have to store
// type erased callbacks for those operations.
template <typename CallableT, typename CalledAs>
struct CallbacksHolder<CallableT, CalledAs, EnableIfTrivial<CallableT>> {
static TrivialCallback Callbacks;
template <typename CallableT, typename CalledAs> struct CallbacksHolder {
inline static auto Callbacks = []() constexpr {
// For trivial callables, we don't need to store move and destroy
// callbacks.
if constexpr (std::is_trivially_move_constructible_v<CallableT> &&
std::is_trivially_destructible_v<CallableT>)
return TrivialCallback{&CallImpl<CalledAs>};
else
return NonTrivialCallbacks{&CallImpl<CalledAs>, &MoveImpl<CallableT>,
&DestroyImpl<CallableT>};
}();
};
// A simple tag type so the call-as type to be passed to the constructor.
@@ -355,19 +345,6 @@ public:
}
};
template <typename R, typename... P>
template <typename CallableT, typename CalledAsT, typename Enable>
typename UniqueFunctionBase<R, P...>::NonTrivialCallbacks UniqueFunctionBase<
R, P...>::CallbacksHolder<CallableT, CalledAsT, Enable>::Callbacks = {
&CallImpl<CalledAsT>, &MoveImpl<CallableT>, &DestroyImpl<CallableT>};
template <typename R, typename... P>
template <typename CallableT, typename CalledAsT>
typename UniqueFunctionBase<R, P...>::TrivialCallback
UniqueFunctionBase<R, P...>::CallbacksHolder<
CallableT, CalledAsT, EnableIfTrivial<CallableT>>::Callbacks{
&CallImpl<CalledAsT>};
} // namespace detail
template <typename R, typename... P>

View File

@@ -0,0 +1,42 @@
//===- IOSandbox.h ----------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_IOSANDBOX_H
#define WPIUTIL_WPI_IOSANDBOX_H
#if defined(LLVM_ENABLE_IO_SANDBOX) && LLVM_ENABLE_IO_SANDBOX
#include "wpi/util/Compiler.hpp"
#include "wpi/util/ErrorHandling.hpp"
#include "wpi/util/SaveAndRestore.hpp"
namespace wpi::util::sys::sandbox {
inline LLVM_THREAD_LOCAL bool Enabled = false;
struct ScopedSetting {
SaveAndRestore<bool> Impl;
};
inline ScopedSetting scopedEnable() { return {{Enabled, true}}; }
inline ScopedSetting scopedDisable() { return {{Enabled, false}}; }
inline void violationIfEnabled() {
if (Enabled)
reportFatalInternalError("IO sandbox violation");
}
} // namespace wpi::util::sys::sandbox
#else
namespace wpi::util::sys::sandbox {
struct [[maybe_unused]] ScopedSetting {};
inline ScopedSetting scopedEnable() { return {}; }
inline ScopedSetting scopedDisable() { return {}; }
inline void violationIfEnabled() {}
} // namespace wpi::util::sys::sandbox
#endif
#endif

View File

@@ -13,6 +13,7 @@
#ifndef WPIUTIL_WPI_MATHEXTRAS_H
#define WPIUTIL_WPI_MATHEXTRAS_H
#include "wpi/util/STLForwardCompat.hpp"
#include "wpi/util/bit.hpp"
#include "wpi/util/Compiler.hpp"
#include <bit>
@@ -43,7 +44,7 @@ using common_sint =
/// Create a bitmask with the N right-most bits set to 1, and all other
/// bits set to 0. Only unsigned types are allowed.
template <typename T> T maskTrailingOnes(unsigned N) {
template <typename T> constexpr T maskTrailingOnes(unsigned N) {
static_assert(std::is_unsigned_v<T>, "Invalid type!");
const unsigned Bits = CHAR_BIT * sizeof(T);
assert(N <= Bits && "Invalid bit index");
@@ -54,19 +55,19 @@ template <typename T> T maskTrailingOnes(unsigned N) {
/// Create a bitmask with the N left-most bits set to 1, and all other
/// bits set to 0. Only unsigned types are allowed.
template <typename T> T maskLeadingOnes(unsigned N) {
template <typename T> constexpr T maskLeadingOnes(unsigned N) {
return ~maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Create a bitmask with the N right-most bits set to 0, and all other
/// bits set to 1. Only unsigned types are allowed.
template <typename T> T maskTrailingZeros(unsigned N) {
template <typename T> constexpr T maskTrailingZeros(unsigned N) {
return maskLeadingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Create a bitmask with the N left-most bits set to 0, and all other
/// bits set to 1. Only unsigned types are allowed.
template <typename T> T maskLeadingZeros(unsigned N) {
template <typename T> constexpr T maskLeadingZeros(unsigned N) {
return maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
@@ -84,7 +85,7 @@ static const unsigned char BitReverseTable256[256] = {
};
/// Reverse the bits in \p Val.
template <typename T> T reverseBits(T Val) {
template <typename T> constexpr T reverseBits(T Val) {
#if __has_builtin(__builtin_bitreverse8)
if constexpr (std::is_same_v<T, uint8_t>)
return __builtin_bitreverse8(Val);
@@ -156,16 +157,8 @@ constexpr bool isShiftedInt(int64_t x) {
/// Checks if an unsigned integer fits into the given bit width.
template <unsigned N> constexpr bool isUInt(uint64_t x) {
if constexpr (N == 0)
return 0 == x;
if constexpr (N == 8)
return static_cast<uint8_t>(x) == x;
if constexpr (N == 16)
return static_cast<uint16_t>(x) == x;
if constexpr (N == 32)
return static_cast<uint32_t>(x) == x;
if constexpr (N < 64)
return x < (UINT64_C(1) << (N));
return (x >> N) == 0;
(void)x; // MSVC v19.25 warns that x is unused.
return true;
}
@@ -181,7 +174,7 @@ constexpr bool isShiftedUInt(uint64_t x) {
}
/// Gets the maximum value for a N-bit unsigned integer.
inline uint64_t maxUIntN(uint64_t N) {
inline constexpr uint64_t maxUIntN(uint64_t N) {
assert(N <= 64 && "integer width out of range");
// uint64_t(1) << 64 is undefined behavior, so we can't do
@@ -202,12 +195,12 @@ inline uint64_t maxUIntN(uint64_t N) {
#endif
/// Gets the minimum value for a N-bit signed integer.
inline int64_t minIntN(int64_t N) {
inline constexpr int64_t minIntN(int64_t N) {
assert(N >= 0 && N <= 64 && "integer width out of range");
if (N == 0)
return 0;
return UINT64_C(1) + ~(UINT64_C(1) << (N - 1));
return UINT64_MAX << (N - 1);
}
#ifdef _WIN32
@@ -215,7 +208,7 @@ inline int64_t minIntN(int64_t N) {
#endif
/// Gets the maximum value for a N-bit signed integer.
inline int64_t maxIntN(int64_t N) {
inline constexpr int64_t maxIntN(int64_t N) {
assert(N >= 0 && N <= 64 && "integer width out of range");
// This relies on two's complement wraparound when N == 64, so we convert to
@@ -226,12 +219,12 @@ inline int64_t maxIntN(int64_t N) {
}
/// Checks if an unsigned integer fits into the given (dynamic) bit width.
inline bool isUIntN(unsigned N, uint64_t x) {
return N >= 64 || x <= maxUIntN(N);
inline constexpr bool isUIntN(unsigned N, uint64_t x) {
return N >= 64 || (x >> N) == 0;
}
/// Checks if an signed integer fits into the given (dynamic) bit width.
inline bool isIntN(unsigned N, int64_t x) {
inline constexpr bool isIntN(unsigned N, int64_t x) {
return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N));
}
@@ -300,13 +293,16 @@ inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
/// Compile time Log2.
/// Valid only for positive powers of two.
template <size_t kValue> constexpr size_t CTLog2() {
static_assert(kValue > 0 && wpi::util::isPowerOf2_64(kValue),
"Value is not a valid power of 2");
return 1 + CTLog2<kValue / 2>();
template <size_t kValue> constexpr size_t ConstantLog2() {
static_assert(wpi::util::isPowerOf2_64(kValue), "Value is not a valid power of 2");
return std::countr_zero(kValue);
}
template <> constexpr size_t CTLog2<1>() { return 0; }
template <size_t kValue>
LLVM_DEPRECATED("Use ConstantLog2 instead", "ConstantLog2")
constexpr size_t CTLog2() {
return ConstantLog2<kValue>();
}
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (32 bit edition.)
@@ -568,6 +564,15 @@ inline int64_t SignExtend64(uint64_t X, unsigned B) {
return int64_t(X << (64 - B)) >> (64 - B);
}
/// Return the absolute value of a signed integer, converted to the
/// corresponding unsigned integer type. Avoids undefined behavior in std::abs
/// when you pass it INT_MIN or similar.
template <typename T, typename U = std::make_unsigned_t<T>>
constexpr U AbsoluteValue(T X) {
// If X is negative, cast it to the unsigned type _before_ negating it.
return X < 0 ? -static_cast<U>(X) : X;
}
/// Subtract two unsigned integers, X and Y, of type T and return the absolute
/// value of the result.
template <typename U, typename V, typename T = common_uint<U, V>>
@@ -667,7 +672,7 @@ SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
}
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
extern const float huge_valf;
LLVM_ABI extern const float huge_valf;
/// Add two signed integers, computing the two's complement truncated result,
/// returning true if overflow occurred.
@@ -696,7 +701,7 @@ std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
}
/// Subtract two signed integers, computing the two's complement truncated
/// result, returning true if an overflow ocurred.
/// result, returning true if an overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_sub_overflow)
@@ -722,7 +727,7 @@ std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
}
/// Multiply two signed integers, computing the two's complement truncated
/// result, returning true if an overflow ocurred.
/// result, returning true if an overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_mul_overflow)

View File

@@ -79,7 +79,7 @@ LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_realloc(void *Ptr, size_t Sz) {
/// like posix_memalign due to portability. It is mostly intended to allow
/// compatibility with platforms that, after aligned allocation was added, use
/// reduced default alignment.
LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void *
LLVM_ABI LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void *
allocate_buffer(size_t Size, size_t Alignment);
/// Deallocate a buffer of memory with the given size and alignment.
@@ -89,7 +89,7 @@ allocate_buffer(size_t Size, size_t Alignment);
///
/// The pointer must have been allocated with the corresponding new operator,
/// most likely using the above helper.
void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment);
LLVM_ABI void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment);
} // namespace wpi::util

View File

@@ -173,15 +173,14 @@ struct PointerIntPairInfo {
"PointerIntPair with integer size too large for pointer");
enum MaskAndShiftConstants : uintptr_t {
/// PointerBitMask - The bits that come from the pointer.
PointerBitMask =
~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1),
PointerBitMask = (~(uintptr_t)0) << PtrTraits::NumLowBitsAvailable,
/// IntShift - The number of low bits that we reserve for other uses, and
/// keep zero.
IntShift = (uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
/// IntMask - This is the unshifted mask for valid bits of the int type.
IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1),
IntMask = ((uintptr_t)1 << IntBits) - 1,
// ShiftedIntMask - This is the bits for the integer shifted in place.
ShiftedIntMask = (uintptr_t)(IntMask << IntShift)
@@ -206,11 +205,10 @@ struct PointerIntPairInfo {
}
static intptr_t updateInt(intptr_t OrigValue, intptr_t Int) {
intptr_t IntWord = static_cast<intptr_t>(Int);
assert((IntWord & ~IntMask) == 0 && "Integer too large for field");
assert((Int & ~IntMask) == 0 && "Integer too large for field");
// Preserve all bits other than the ones we are updating.
return (OrigValue & ~ShiftedIntMask) | IntWord << IntShift;
return (OrigValue & ~ShiftedIntMask) | Int << IntShift;
}
};

View File

@@ -14,9 +14,9 @@
#ifndef WPIUTIL_WPI_POINTERLIKETYPETRAITS_H
#define WPIUTIL_WPI_POINTERLIKETYPETRAITS_H
#include "wpi/util/MathExtras.hpp"
#include <cassert>
#include <cstdint>
#include <type_traits>
namespace wpi::util {
@@ -25,12 +25,6 @@ namespace wpi::util {
template <typename T> struct PointerLikeTypeTraits;
namespace detail {
/// A tiny meta function to compute the log2 of a compile time constant.
template <size_t N>
struct ConstantLog2
: std::integral_constant<size_t, ConstantLog2<N / 2>::value + 1> {};
template <> struct ConstantLog2<1> : std::integral_constant<size_t, 0> {};
// Provide a trait to check if T is pointer-like.
template <typename T, typename U = void> struct HasPointerLikeTypeTraits {
static const bool value = false;
@@ -57,8 +51,7 @@ template <typename T> struct PointerLikeTypeTraits<T *> {
static inline void *getAsVoidPointer(T *P) { return P; }
static inline T *getFromVoidPointer(void *P) { return static_cast<T *>(P); }
static constexpr int NumLowBitsAvailable =
detail::ConstantLog2<alignof(T)>::value;
static constexpr int NumLowBitsAvailable = ConstantLog2<alignof(T)>();
};
template <> struct PointerLikeTypeTraits<void *> {
@@ -77,7 +70,7 @@ template <> struct PointerLikeTypeTraits<void *> {
// Provide PointerLikeTypeTraits for const things.
template <typename T> struct PointerLikeTypeTraits<const T> {
typedef PointerLikeTypeTraits<T> NonConst;
using NonConst = PointerLikeTypeTraits<T>;
static inline const void *getAsVoidPointer(const T P) {
return NonConst::getAsVoidPointer(P);
@@ -90,7 +83,7 @@ template <typename T> struct PointerLikeTypeTraits<const T> {
// Provide PointerLikeTypeTraits for const pointers.
template <typename T> struct PointerLikeTypeTraits<const T *> {
typedef PointerLikeTypeTraits<T *> NonConst;
using NonConst = PointerLikeTypeTraits<T *>;
static inline const void *getAsVoidPointer(const T *P) {
return NonConst::getAsVoidPointer(const_cast<T *>(P));
@@ -123,8 +116,7 @@ template <> struct PointerLikeTypeTraits<uintptr_t> {
/// potentially use alignment attributes on functions to satisfy that.
template <int Alignment, typename FunctionPointerT>
struct FunctionPointerLikeTypeTraits {
static constexpr int NumLowBitsAvailable =
detail::ConstantLog2<Alignment>::value;
static constexpr int NumLowBitsAvailable = ConstantLog2<Alignment>();
static inline void *getAsVoidPointer(FunctionPointerT P) {
assert((reinterpret_cast<uintptr_t>(P) &
~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 &&

View File

@@ -76,18 +76,13 @@ namespace pointer_union_detail {
/// Determine the number of bits required to store integers with values < n.
/// This is ceil(log2(n)).
constexpr int bitsRequired(unsigned n) {
return n > 1 ? 1 + bitsRequired((n + 1) / 2) : 0;
return n == 0 ? 0 : std::bit_width(n - 1);
}
template <typename... Ts> constexpr int lowBitsAvailable() {
return std::min<int>({PointerLikeTypeTraits<Ts>::NumLowBitsAvailable...});
}
/// Find the first type in a list of types.
template <typename T, typename...> struct GetFirstType {
using type = T;
};
/// Provide PointerLikeTypeTraits for void* that is used by PointerUnion
/// for the template arguments.
template <typename ...PTs> class PointerUnionUIntTraits {
@@ -133,9 +128,6 @@ namespace pointer_union_detail {
};
}
// This is a forward declaration of CastInfoPointerUnionImpl
// Refer to its definition below for further details
template <typename... PTs> struct CastInfoPointerUnionImpl;
/// A discriminated union of two or more pointer types, with the discriminator
/// in the low bit of the pointer.
///
@@ -171,10 +163,12 @@ class PointerUnion
using First = TypeAtIndex<0, PTs...>;
using Base = typename PointerUnion::PointerUnionMembers;
/// This is needed to give the CastInfo implementation below access
/// to protected members.
/// Refer to its definition for further details.
friend struct CastInfoPointerUnionImpl<PTs...>;
// Give the CastInfo specialization below access to protected members.
//
// This makes all of CastInfo a friend, which is more than strictly
// necessary. It's a workaround for C++'s inability to friend a
// partial template specialization.
template <typename To, typename From, typename Enable> friend struct CastInfo;
public:
PointerUnion() = default;
@@ -264,42 +258,21 @@ bool operator<(PointerUnion<PTs...> lhs, PointerUnion<PTs...> rhs) {
return lhs.getOpaqueValue() < rhs.getOpaqueValue();
}
/// We can't (at least, at this moment with C++14) declare CastInfo
/// as a friend of PointerUnion like this:
/// ```
/// template<typename To>
/// friend struct CastInfo<To, PointerUnion<PTs...>>;
/// ```
/// The compiler complains 'Partial specialization cannot be declared as a
/// friend'.
/// So we define this struct to be a bridge between CastInfo and
/// PointerUnion.
template <typename... PTs> struct CastInfoPointerUnionImpl {
using From = PointerUnion<PTs...>;
template <typename To> static inline bool isPossible(From &F) {
return F.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
}
template <typename To> static To doCast(From &F) {
assert(isPossible<To>(F) && "cast to an incompatible type!");
return PointerLikeTypeTraits<To>::getFromVoidPointer(F.Val.getPointer());
}
};
// Specialization of CastInfo for PointerUnion
template <typename To, typename... PTs>
struct CastInfo<To, PointerUnion<PTs...>>
: public DefaultDoCastIfPossible<To, PointerUnion<PTs...>,
CastInfo<To, PointerUnion<PTs...>>> {
using From = PointerUnion<PTs...>;
using Impl = CastInfoPointerUnionImpl<PTs...>;
static inline bool isPossible(From &f) {
return Impl::template isPossible<To>(f);
return f.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
}
static To doCast(From &f) { return Impl::template doCast<To>(f); }
static To doCast(From &f) {
assert(isPossible(f) && "cast to an incompatible type!");
return PointerLikeTypeTraits<To>::getFromVoidPointer(f.Val.getPointer());
}
static inline To castFailed() { return To(); }
};
@@ -331,8 +304,7 @@ struct PointerLikeTypeTraits<PointerUnion<PTs...>> {
// Teach DenseMap how to use PointerUnions as keys.
template <typename ...PTs> struct DenseMapInfo<PointerUnion<PTs...>> {
using Union = PointerUnion<PTs...>;
using FirstInfo =
DenseMapInfo<typename pointer_union_detail::GetFirstType<PTs...>::type>;
using FirstInfo = DenseMapInfo<TypeAtIndex<0, PTs...>>;
static inline Union getEmptyKey() { return Union(FirstInfo::getEmptyKey()); }

View File

@@ -5,8 +5,7 @@
namespace wpi::util {
template<class T = void *>
bool shouldReverseIterate() {
template <class T = void *> constexpr bool shouldReverseIterate() {
#if LLVM_ENABLE_REVERSE_ITERATION
return detail::IsPointerLike<T>::value;
#else

View File

@@ -19,6 +19,7 @@
#include <optional>
#include <type_traits>
#include <utility>
namespace wpi::util {
@@ -26,6 +27,54 @@ namespace wpi::util {
// Features from C++20
//===----------------------------------------------------------------------===//
namespace numbers {
// clang-format off
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T e_v = T(0x1.5bf0a8b145769P+1); // (2.7182818284590452354) https://oeis.org/A001113
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T egamma_v = T(0x1.2788cfc6fb619P-1); // (.57721566490153286061) https://oeis.org/A001620
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T ln2_v = T(0x1.62e42fefa39efP-1); // (.69314718055994530942) https://oeis.org/A002162
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T ln10_v = T(0x1.26bb1bbb55516P+1); // (2.3025850929940456840) https://oeis.org/A002392
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T log2e_v = T(0x1.71547652b82feP+0); // (1.4426950408889634074)
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T log10e_v = T(0x1.bcb7b1526e50eP-2); // (.43429448190325182765)
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T pi_v = T(0x1.921fb54442d18P+1); // (3.1415926535897932385) https://oeis.org/A000796
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T inv_pi_v = T(0x1.45f306dc9c883P-2); // (.31830988618379067154) https://oeis.org/A049541
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T inv_sqrtpi_v = T(0x1.20dd750429b6dP-1); // (.56418958354775628695) https://oeis.org/A087197
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T sqrt2_v = T(0x1.6a09e667f3bcdP+0); // (1.4142135623730950488) https://oeis.org/A00219
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T inv_sqrt2_v = T(0x1.6a09e667f3bcdP-1); // (.70710678118654752440)
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T sqrt3_v = T(0x1.bb67ae8584caaP+0); // (1.7320508075688772935) https://oeis.org/A002194
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T inv_sqrt3_v = T(0x1.279a74590331cP-1); // (.57735026918962576451)
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
inline constexpr T phi_v = T(0x1.9e3779b97f4a8P+0); // (1.6180339887498948482) https://oeis.org/A001622
inline constexpr double e = e_v<double>;
inline constexpr double egamma = egamma_v<double>;
inline constexpr double ln2 = ln2_v<double>;
inline constexpr double ln10 = ln10_v<double>;
inline constexpr double log2e = log2e_v<double>;
inline constexpr double log10e = log10e_v<double>;
inline constexpr double pi = pi_v<double>;
inline constexpr double inv_pi = inv_pi_v<double>;
inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
inline constexpr double sqrt2 = sqrt2_v<double>;
inline constexpr double inv_sqrt2 = inv_sqrt2_v<double>;
inline constexpr double sqrt3 = sqrt3_v<double>;
inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
inline constexpr double phi = phi_v<double>;
// clang-format on
} // namespace numbers
template <typename T>
struct remove_cvref // NOLINT(readability-identifier-naming)
{
@@ -36,27 +85,81 @@ template <typename T>
using remove_cvref_t // NOLINT(readability-identifier-naming)
= typename wpi::util::remove_cvref<T>::type;
// TODO: Remove this in favor of std::type_identity<T> once we switch to C++20.
template <typename T>
struct type_identity // NOLINT(readability-identifier-naming)
{
using type = T;
};
// TODO: Remove this in favor of std::type_identity_t<T> once we switch to
// C++20.
template <typename T>
using type_identity_t // NOLINT(readability-identifier-naming)
= typename wpi::util::type_identity<T>::type;
namespace detail {
template <class, template <class...> class Op, class... Args> struct detector {
using value_t = std::false_type;
};
template <template <class...> class Op, class... Args>
struct detector<std::void_t<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
};
} // end namespace detail
/// Detects if a given trait holds for some set of arguments 'Args'.
/// For example, the given trait could be used to detect if a given type
/// has a copy assignment operator:
/// template<class T>
/// using has_copy_assign_t = decltype(std::declval<T&>()
/// = std::declval<const T&>());
/// bool fooHasCopyAssign = is_detected<has_copy_assign_t, FooClass>::value;
///
/// NOTE: The C++20 standard has adopted concepts and requires clauses as a
/// superior alternative to std::is_detected.
///
/// This utility is placed in STLForwardCompat.h as a reminder
/// to migrate usages of wpi::util::is_detected to concepts and 'requires'
/// clauses when the codebase adopts C++20.
template <template <class...> class Op, class... Args>
using is_detected = typename detail::detector<void, Op, Args...>::value_t;
struct identity // NOLINT(readability-identifier-naming)
{
using is_transparent = void;
template <typename T> constexpr T &&operator()(T &&self) const noexcept {
return std::forward<T>(self);
}
};
/// Returns a raw pointer that represents the same address as the argument.
///
/// This implementation can be removed once we move to C++20 where it's defined
/// as std::to_address().
///
/// The std::pointer_traits<>::to_address(p) variations of these overloads has
/// not been implemented.
template <class Ptr> auto to_address(const Ptr &P) { return P.operator->(); }
template <class T> constexpr T *to_address(T *P) {
static_assert(!std::is_function_v<T>);
return P;
}
//===----------------------------------------------------------------------===//
// Features from C++23
//===----------------------------------------------------------------------===//
// TODO: Remove this in favor of std::optional<T>::transform once we switch to
// C++23.
template <typename T, typename Function>
auto transformOptional(const std::optional<T> &O, const Function &F)
-> std::optional<decltype(F(*O))> {
if (O)
return F(*O);
return std::nullopt;
}
// TODO: Remove this in favor of std::optional<T>::transform once we switch to
// C++23.
template <typename T, typename Function>
auto transformOptional(std::optional<T> &&O, const Function &F)
-> std::optional<decltype(F(*std::move(O)))> {
if (O)
return F(*std::move(O));
template <typename Optional, typename Function,
typename Value = typename wpi::util::remove_cvref_t<Optional>::value_type>
std::optional<std::invoke_result_t<Function, Value>>
transformOptional(Optional &&O, Function &&F) {
if (O) {
return F(*std::forward<Optional>(O));
}
return std::nullopt;
}
@@ -67,6 +170,11 @@ template <typename Enum>
return static_cast<std::underlying_type_t<Enum>>(E);
}
// A tag for constructors accepting ranges.
struct from_range_t {
explicit from_range_t() = default;
};
inline constexpr from_range_t from_range{};
} // namespace wpi::util
#endif // WPIUTIL_WPI_STLFORWARDCOMPAT_H

View File

@@ -15,7 +15,11 @@
#ifndef WPIUTIL_WPI_SMALLPTRSET_H
#define WPIUTIL_WPI_SMALLPTRSET_H
#include "wpi/util/ADL.hpp"
#include "wpi/util/EpochTracker.hpp"
#include "wpi/util/STLForwardCompat.hpp"
#include "wpi/util/iterator_range.hpp"
#include "wpi/util/Compiler.hpp"
#include "wpi/util/MathExtras.hpp"
#include "wpi/util/ReverseIteration.hpp"
#include "wpi/util/type_traits.hpp"
@@ -43,7 +47,7 @@ namespace wpi::util {
/// sets are often small. In this case, no memory allocation is used, and only
/// light-weight and cache-efficient scanning is used.
///
/// Large sets use a classic exponentially-probed hash table. Empty buckets are
/// Large sets use a classic quadratically-probed hash table. Empty buckets are
/// represented with an illegal pointer value (-1) to allow null pointers to be
/// inserted. Tombstones are represented with another illegal pointer value
/// (-2), to allow deletion. The hash table is resized when the table is 3/4 or
@@ -58,25 +62,26 @@ protected:
/// CurArraySize - The allocated size of CurArray, always a power of two.
unsigned CurArraySize;
/// Number of elements in CurArray that contain a value or are a tombstone.
/// Number of elements in CurArray that contain a value.
/// If small, all these elements are at the beginning of CurArray and the rest
/// is uninitialized.
unsigned NumNonEmpty;
unsigned NumEntries;
/// Number of tombstones in CurArray.
unsigned NumTombstones;
/// Whether the set is in small representation.
bool IsSmall;
// Helpers to copy and move construct a SmallPtrSet.
SmallPtrSetImplBase(const void **SmallStorage,
const SmallPtrSetImplBase &that);
SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize,
const void **RHSSmallStorage, SmallPtrSetImplBase &&that);
LLVM_ABI SmallPtrSetImplBase(const void **SmallStorage,
const SmallPtrSetImplBase &that);
LLVM_ABI SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize,
const void **RHSSmallStorage,
SmallPtrSetImplBase &&that);
explicit SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize)
: CurArray(SmallStorage), CurArraySize(SmallSize), NumNonEmpty(0),
: CurArray(SmallStorage), CurArraySize(SmallSize), NumEntries(0),
NumTombstones(0), IsSmall(true) {
assert(SmallSize && (SmallSize & (SmallSize-1)) == 0 &&
assert(std::has_single_bit(SmallSize) &&
"Initial size must be a power of two!");
}
@@ -91,8 +96,8 @@ public:
SmallPtrSetImplBase &operator=(const SmallPtrSetImplBase &) = delete;
[[nodiscard]] bool empty() const { return size() == 0; }
size_type size() const { return NumNonEmpty - NumTombstones; }
size_type capacity() const { return CurArraySize; }
[[nodiscard]] size_type size() const { return NumEntries; }
[[nodiscard]] size_type capacity() const { return CurArraySize; }
void clear() {
incrementEpoch();
@@ -105,42 +110,58 @@ public:
memset(CurArray, -1, CurArraySize * sizeof(void *));
}
NumNonEmpty = 0;
NumEntries = 0;
NumTombstones = 0;
}
void reserve(size_type NumEntries) {
void reserve(size_type NewNumEntries) {
incrementEpoch();
// Do nothing if we're given zero as a reservation size.
if (NumEntries == 0)
if (NewNumEntries == 0)
return;
// No need to expand if we're small and NumEntries will fit in the space.
if (isSmall() && NumEntries <= CurArraySize)
// No need to expand if we're small and NewNumEntries will fit in the space.
if (isSmall() && NewNumEntries <= CurArraySize)
return;
// insert_imp_big will reallocate if stores is more than 75% full, on the
// /final/ insertion.
if (!isSmall() && ((NumEntries - 1) * 4) < (CurArraySize * 3))
if (!isSmall() && ((NewNumEntries - 1) * 4) < (CurArraySize * 3))
return;
// We must Grow -- find the size where we'd be 75% full, then round up to
// the next power of two.
size_type NewSize = NumEntries + (NumEntries / 3);
NewSize = 1 << (Log2_32_Ceil(NewSize) + 1);
size_type NewSize = NewNumEntries + (NewNumEntries / 3);
NewSize = std::bit_ceil(NewSize);
// Like insert_imp_big, always allocate at least 128 elements.
NewSize = (std::max)(128u, NewSize);
Grow(NewSize);
}
protected:
static void *getTombstoneMarker() { return reinterpret_cast<void*>(-2); }
static void *getTombstoneMarker() { return reinterpret_cast<void *>(-2); }
static void *getEmptyMarker() {
// Note that -1 is chosen to make clear() efficiently implementable with
// memset and because it's not a valid pointer value.
return reinterpret_cast<void*>(-1);
return reinterpret_cast<void *>(-1);
}
const void **EndPointer() const {
return isSmall() ? CurArray + NumNonEmpty : CurArray + CurArraySize;
return isSmall() ? CurArray + NumEntries : CurArray + CurArraySize;
}
iterator_range<const void **> small_buckets() {
return make_range(CurArray, CurArray + NumEntries);
}
iterator_range<const void *const *> small_buckets() const {
return {CurArray, CurArray + NumEntries};
}
iterator_range<const void **> buckets() {
return make_range(CurArray, EndPointer());
}
iterator_range<const void *const *> buckets() const {
return make_range(CurArray, EndPointer());
}
/// insert_imp - This returns true if the pointer was new to the set, false if
@@ -149,18 +170,16 @@ protected:
std::pair<const void *const *, bool> insert_imp(const void *Ptr) {
if (isSmall()) {
// Check to see if it is already in the set.
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
APtr != E; ++APtr) {
const void *Value = *APtr;
if (Value == Ptr)
return std::make_pair(APtr, false);
for (const void *&Bucket : small_buckets()) {
if (Bucket == Ptr)
return {&Bucket, false};
}
// Nope, there isn't. If we stay small, just 'pushback' now.
if (NumNonEmpty < CurArraySize) {
CurArray[NumNonEmpty++] = Ptr;
if (NumEntries < CurArraySize) {
CurArray[NumEntries++] = Ptr;
incrementEpoch();
return std::make_pair(CurArray + (NumNonEmpty - 1), true);
return {CurArray + (NumEntries - 1), true};
}
// Otherwise, hit the big set case, which will call grow.
}
@@ -171,12 +190,11 @@ protected:
/// return true, otherwise return false. This is hidden from the client so
/// that the derived class can check that the right type of pointer is passed
/// in.
bool erase_imp(const void * Ptr) {
bool erase_imp(const void *Ptr) {
if (isSmall()) {
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
APtr != E; ++APtr) {
if (*APtr == Ptr) {
*APtr = CurArray[--NumNonEmpty];
for (const void *&Bucket : small_buckets()) {
if (Bucket == Ptr) {
Bucket = CurArray[--NumEntries];
incrementEpoch();
return true;
}
@@ -190,6 +208,7 @@ protected:
*const_cast<const void **>(Bucket) = getTombstoneMarker();
NumTombstones++;
--NumEntries;
// Treat this consistently from an API perspective, even if we don't
// actually invalidate iterators here.
incrementEpoch();
@@ -199,14 +218,12 @@ protected:
/// Returns the raw pointer needed to construct an iterator. If element not
/// found, this will be EndPointer. Otherwise, it will be a pointer to the
/// slot which stores Ptr;
const void *const * find_imp(const void * Ptr) const {
const void *const *find_imp(const void *Ptr) const {
if (isSmall()) {
// Linear search for the item.
for (const void *const *APtr = CurArray, *const *E =
CurArray + NumNonEmpty;
APtr != E; ++APtr)
if (*APtr == Ptr)
return APtr;
for (const void *const &Bucket : small_buckets())
if (Bucket == Ptr)
return &Bucket;
return EndPointer();
}
@@ -219,10 +236,8 @@ protected:
bool contains_imp(const void *Ptr) const {
if (isSmall()) {
// Linear search for the item.
const void *const *APtr = CurArray;
const void *const *E = CurArray + NumNonEmpty;
for (; APtr != E; ++APtr)
if (*APtr == Ptr)
for (const void *const &Bucket : small_buckets())
if (Bucket == Ptr)
return true;
return false;
}
@@ -233,24 +248,26 @@ protected:
bool isSmall() const { return IsSmall; }
private:
std::pair<const void *const *, bool> insert_imp_big(const void *Ptr);
LLVM_ABI std::pair<const void *const *, bool> insert_imp_big(const void *Ptr);
const void *const *doFind(const void *Ptr) const;
const void * const *FindBucketFor(const void *Ptr) const;
void shrink_and_clear();
LLVM_ABI const void *const *doFind(const void *Ptr) const;
const void *const *FindBucketFor(const void *Ptr) const;
LLVM_ABI void shrink_and_clear();
/// Grow - Allocate a larger backing store for the buckets and move it over.
void Grow(unsigned NewSize);
LLVM_ABI void Grow(unsigned NewSize);
protected:
/// swap - Swaps the elements of two sets.
/// Note: This method assumes that both sets have the same small size.
void swap(const void **SmallStorage, const void **RHSSmallStorage,
SmallPtrSetImplBase &RHS);
LLVM_ABI void swap(const void **SmallStorage, const void **RHSSmallStorage,
SmallPtrSetImplBase &RHS);
void copyFrom(const void **SmallStorage, const SmallPtrSetImplBase &RHS);
void moveFrom(const void **SmallStorage, unsigned SmallSize,
const void **RHSSmallStorage, SmallPtrSetImplBase &&RHS);
LLVM_ABI void copyFrom(const void **SmallStorage,
const SmallPtrSetImplBase &RHS);
LLVM_ABI void moveFrom(const void **SmallStorage, unsigned SmallSize,
const void **RHSSmallStorage,
SmallPtrSetImplBase &&RHS);
private:
/// Code shared by moveFrom() and move constructor.
@@ -262,18 +279,12 @@ private:
/// SmallPtrSetIteratorImpl - This is the common base class shared between all
/// instances of SmallPtrSetIterator.
class SmallPtrSetIteratorImpl {
protected:
const void *const *Bucket;
const void *const *End;
class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIteratorImpl
: public DebugEpochBase::HandleBase {
public:
explicit SmallPtrSetIteratorImpl(const void *const *BP, const void*const *E)
: Bucket(BP), End(E) {
if (shouldReverseIterate()) {
RetreatIfNotValid();
return;
}
explicit SmallPtrSetIteratorImpl(const void *const *BP, const void *const *E,
const DebugEpochBase &Epoch)
: DebugEpochBase::HandleBase(&Epoch), Bucket(BP), End(E) {
AdvanceIfNotValid();
}
@@ -285,6 +296,18 @@ public:
}
protected:
void *dereference() const {
assert(isHandleInSync() && "invalid iterator access!");
assert(Bucket < End);
return const_cast<void *>(*Bucket);
}
void increment() {
assert(isHandleInSync() && "invalid iterator access!");
++Bucket;
AdvanceIfNotValid();
}
private:
/// AdvanceIfNotValid - If the current bucket isn't valid, advance to a bucket
/// that is. This is guaranteed to stop because the end() bucket is marked
/// valid.
@@ -295,21 +318,19 @@ protected:
*Bucket == SmallPtrSetImplBase::getTombstoneMarker()))
++Bucket;
}
void RetreatIfNotValid() {
assert(Bucket >= End);
while (Bucket != End &&
(Bucket[-1] == SmallPtrSetImplBase::getEmptyMarker() ||
Bucket[-1] == SmallPtrSetImplBase::getTombstoneMarker())) {
--Bucket;
}
}
using BucketItTy =
std::conditional_t<shouldReverseIterate(),
std::reverse_iterator<const void *const *>,
const void *const *>;
BucketItTy Bucket;
BucketItTy End;
};
/// SmallPtrSetIterator - This implements a const_iterator for SmallPtrSet.
template <typename PtrTy>
class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIterator
: public SmallPtrSetIteratorImpl,
DebugEpochBase::HandleBase {
class SmallPtrSetIterator : public SmallPtrSetIteratorImpl {
using PtrTraits = PointerLikeTypeTraits<PtrTy>;
public:
@@ -319,37 +340,22 @@ public:
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
explicit SmallPtrSetIterator(const void *const *BP, const void *const *E,
const DebugEpochBase &Epoch)
: SmallPtrSetIteratorImpl(BP, E), DebugEpochBase::HandleBase(&Epoch) {}
using SmallPtrSetIteratorImpl::SmallPtrSetIteratorImpl;
// Most methods are provided by the base class.
const PtrTy operator*() const {
assert(isHandleInSync() && "invalid iterator access!");
if (shouldReverseIterate()) {
assert(Bucket > End);
return PtrTraits::getFromVoidPointer(const_cast<void *>(Bucket[-1]));
}
assert(Bucket < End);
return PtrTraits::getFromVoidPointer(const_cast<void*>(*Bucket));
[[nodiscard]] const PtrTy operator*() const {
return PtrTraits::getFromVoidPointer(dereference());
}
inline SmallPtrSetIterator& operator++() { // Preincrement
assert(isHandleInSync() && "invalid iterator access!");
if (shouldReverseIterate()) {
--Bucket;
RetreatIfNotValid();
return *this;
}
++Bucket;
AdvanceIfNotValid();
inline SmallPtrSetIterator &operator++() { // Preincrement
increment();
return *this;
}
SmallPtrSetIterator operator++(int) { // Postincrement
SmallPtrSetIterator operator++(int) { // Postincrement
SmallPtrSetIterator tmp = *this;
++*this;
increment();
return tmp;
}
};
@@ -359,8 +365,7 @@ public:
///
/// This is particularly useful for passing around between interface boundaries
/// to avoid encoding a particular small size in the interface boundary.
template <typename PtrType>
class SmallPtrSetImpl : public SmallPtrSetImplBase {
template <typename PtrType> class SmallPtrSetImpl : public SmallPtrSetImplBase {
using ConstPtrType = typename add_const_past_pointer<PtrType>::type;
using PtrTraits = PointerLikeTypeTraits<PtrType>;
using ConstPtrTraits = PointerLikeTypeTraits<ConstPtrType>;
@@ -383,15 +388,13 @@ public:
/// the element equal to Ptr.
std::pair<iterator, bool> insert(PtrType Ptr) {
auto p = insert_imp(PtrTraits::getAsVoidPointer(Ptr));
return std::make_pair(makeIterator(p.first), p.second);
return {makeIterator(p.first), p.second};
}
/// Insert the given pointer with an iterator hint that is ignored. This is
/// identical to calling insert(Ptr), but allows SmallPtrSet to be used by
/// std::insert_iterator and std::inserter().
iterator insert(iterator, PtrType Ptr) {
return insert(Ptr).first;
}
iterator insert(iterator, PtrType Ptr) { return insert(Ptr).first; }
/// Remove pointer from the set.
///
@@ -414,16 +417,16 @@ public:
/// Returns whether anything was removed. It is safe to read the set inside
/// the predicate function. However, the predicate must not modify the set
/// itself, only indicate a removal by returning true.
template <typename UnaryPredicate>
bool remove_if(UnaryPredicate P) {
template <typename UnaryPredicate> bool remove_if(UnaryPredicate P) {
bool Removed = false;
if (isSmall()) {
const void **APtr = CurArray, **E = CurArray + NumNonEmpty;
auto Buckets = small_buckets();
const void **APtr = Buckets.begin(), **E = Buckets.end();
while (APtr != E) {
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(*APtr));
if (P(Ptr)) {
*APtr = *--E;
--NumNonEmpty;
--NumEntries;
incrementEpoch();
Removed = true;
} else {
@@ -433,14 +436,14 @@ public:
return Removed;
}
for (const void **APtr = CurArray, **E = EndPointer(); APtr != E; ++APtr) {
const void *Value = *APtr;
if (Value == getTombstoneMarker() || Value == getEmptyMarker())
for (const void *&Bucket : buckets()) {
if (Bucket == getTombstoneMarker() || Bucket == getEmptyMarker())
continue;
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(Value));
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(Bucket));
if (P(Ptr)) {
*APtr = getTombstoneMarker();
Bucket = getTombstoneMarker();
++NumTombstones;
--NumEntries;
incrementEpoch();
Removed = true;
}
@@ -449,18 +452,17 @@ public:
}
/// count - Return 1 if the specified pointer is in the set, 0 otherwise.
size_type count(ConstPtrType Ptr) const {
[[nodiscard]] size_type count(ConstPtrType Ptr) const {
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
}
iterator find(ConstPtrType Ptr) const {
[[nodiscard]] iterator find(ConstPtrType Ptr) const {
return makeIterator(find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)));
}
bool contains(ConstPtrType Ptr) const {
[[nodiscard]] bool contains(ConstPtrType Ptr) const {
return contains_imp(ConstPtrTraits::getAsVoidPointer(Ptr));
}
template <typename IterT>
void insert(IterT I, IterT E) {
template <typename IterT> void insert(IterT I, IterT E) {
for (; I != E; ++I)
insert(*I);
}
@@ -469,19 +471,25 @@ public:
insert(IL.begin(), IL.end());
}
iterator begin() const {
if (shouldReverseIterate())
return makeIterator(EndPointer() - 1);
return makeIterator(CurArray);
template <typename Range> void insert_range(Range &&R) {
insert(adl_begin(R), adl_end(R));
}
iterator end() const { return makeIterator(EndPointer()); }
[[nodiscard]] iterator begin() const {
if constexpr (shouldReverseIterate())
return makeIterator(EndPointer() - 1);
else
return makeIterator(CurArray);
}
[[nodiscard]] iterator end() const { return makeIterator(EndPointer()); }
private:
/// Create an iterator that dereferences to same place as the given pointer.
iterator makeIterator(const void *const *P) const {
if (shouldReverseIterate())
if constexpr (shouldReverseIterate())
return iterator(P == EndPointer() ? CurArray : P + 1, CurArray, *this);
return iterator(P, EndPointer(), *this);
else
return iterator(P, EndPointer(), *this);
}
};
@@ -490,8 +498,8 @@ private:
/// Iterates over elements of LHS confirming that each value from LHS is also in
/// RHS, and that no additional values are in RHS.
template <typename PtrType>
bool operator==(const SmallPtrSetImpl<PtrType> &LHS,
const SmallPtrSetImpl<PtrType> &RHS) {
[[nodiscard]] bool operator==(const SmallPtrSetImpl<PtrType> &LHS,
const SmallPtrSetImpl<PtrType> &RHS) {
if (LHS.size() != RHS.size())
return false;
@@ -506,8 +514,8 @@ bool operator==(const SmallPtrSetImpl<PtrType> &LHS,
///
/// Equivalent to !(LHS == RHS).
template <typename PtrType>
bool operator!=(const SmallPtrSetImpl<PtrType> &LHS,
const SmallPtrSetImpl<PtrType> &RHS) {
[[nodiscard]] bool operator!=(const SmallPtrSetImpl<PtrType> &LHS,
const SmallPtrSetImpl<PtrType> &RHS) {
return !(LHS == RHS);
}
@@ -515,7 +523,7 @@ bool operator!=(const SmallPtrSetImpl<PtrType> &LHS,
/// SmallSize or less elements. This internally rounds up SmallSize to the next
/// power of two if it is not already a power of two. See the comments above
/// SmallPtrSetImplBase for details of the algorithm.
template<class PtrType, unsigned SmallSize>
template <class PtrType, unsigned SmallSize>
class SmallPtrSet : public SmallPtrSetImpl<PtrType> {
// In small mode SmallPtrSet uses linear search for the elements, so it is
// not a good idea to choose this value too high. You may consider using a
@@ -524,18 +532,8 @@ class SmallPtrSet : public SmallPtrSetImpl<PtrType> {
using BaseT = SmallPtrSetImpl<PtrType>;
// A constexpr version of wpi::util::bit_ceil.
// TODO: Replace this with std::bit_ceil once C++20 is available.
static constexpr size_t RoundUpToPowerOfTwo(size_t X) {
size_t C = 1;
size_t CMax = C << (std::numeric_limits<size_t>::digits - 1);
while (C < X && C < CMax)
C <<= 1;
return C;
}
// Make sure that SmallSize is a power of two, round up if not.
static constexpr size_t SmallSizePowTwo = RoundUpToPowerOfTwo(SmallSize);
static constexpr size_t SmallSizePowTwo = std::bit_ceil(SmallSize);
/// SmallStorage - Fixed size storage used in 'small mode'.
const void *SmallStorage[SmallSizePowTwo];
@@ -546,11 +544,15 @@ public:
: BaseT(SmallStorage, SmallSizePowTwo, that.SmallStorage,
std::move(that)) {}
template<typename It>
template <typename It>
SmallPtrSet(It I, It E) : BaseT(SmallStorage, SmallSizePowTwo) {
this->insert(I, E);
}
template <typename Range>
SmallPtrSet(wpi::util::from_range_t, Range &&R)
: SmallPtrSet(adl_begin(R), adl_end(R)) {}
SmallPtrSet(std::initializer_list<PtrType> IL)
: BaseT(SmallStorage, SmallSizePowTwo) {
this->insert(IL.begin(), IL.end());
@@ -584,16 +586,16 @@ public:
}
};
} // end namespace wpi::util
} // namespace wpi::util
namespace std {
/// Implement std::swap in terms of SmallPtrSet swap.
template<class T, unsigned N>
inline void swap(wpi::util::SmallPtrSet<T, N> &LHS, wpi::util::SmallPtrSet<T, N> &RHS) {
LHS.swap(RHS);
}
/// Implement std::swap in terms of SmallPtrSet swap.
template <class T, unsigned N>
inline void swap(wpi::util::SmallPtrSet<T, N> &LHS, wpi::util::SmallPtrSet<T, N> &RHS) {
LHS.swap(RHS);
}
} // end namespace std
} // namespace std
#endif // WPIUTIL_WPI_SMALLPTRSET_H

View File

@@ -14,6 +14,8 @@
#ifndef WPIUTIL_WPI_SMALLSET_H
#define WPIUTIL_WPI_SMALLSET_H
#include "wpi/util/ADL.hpp"
#include "wpi/util/STLForwardCompat.hpp"
#include "wpi/util/SmallPtrSet.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/iterator.hpp"
@@ -34,7 +36,6 @@ class SmallSetIterator
private:
using SetIterTy = typename std::set<T, C>::const_iterator;
using VecIterTy = typename SmallVector<T, N>::const_iterator;
using SelfTy = SmallSetIterator<T, N, C>;
/// Iterators to the parts of the SmallSet containing the data. They are set
/// depending on isSmall.
@@ -161,10 +162,9 @@ public:
insert(Begin, End);
}
template <typename RangeT>
explicit SmallSet(const iterator_range<RangeT> &R) {
insert(R.begin(), R.end());
}
template <typename Range>
SmallSet(wpi::util::from_range_t, Range &&R)
: SmallSet(adl_begin(R), adl_end(R)) {}
SmallSet(std::initializer_list<T> L) { insert(L.begin(), L.end()); }
@@ -173,12 +173,14 @@ public:
[[nodiscard]] bool empty() const { return Vector.empty() && Set.empty(); }
size_type size() const {
[[nodiscard]] size_type size() const {
return isSmall() ? Vector.size() : Set.size();
}
/// count - Return 1 if the element is in the set, 0 otherwise.
size_type count(const T &V) const { return contains(V) ? 1 : 0; }
[[nodiscard]] size_type count(const T &V) const {
return contains(V) ? 1 : 0;
}
/// insert - Insert an element into the set if it isn't already there.
/// Returns a pair. The first value of it is an iterator to the inserted
@@ -196,6 +198,10 @@ public:
insert(*I);
}
template <typename Range> void insert_range(Range &&R) {
insert(adl_begin(R), adl_end(R));
}
bool erase(const T &V) {
if (!isSmall())
return Set.erase(V);
@@ -212,20 +218,20 @@ public:
Set.clear();
}
const_iterator begin() const {
[[nodiscard]] const_iterator begin() const {
if (isSmall())
return {Vector.begin()};
return {Set.begin()};
}
const_iterator end() const {
[[nodiscard]] const_iterator end() const {
if (isSmall())
return {Vector.end()};
return {Set.end()};
}
/// Check if the SmallSet contains the given element.
bool contains(const T &V) const {
[[nodiscard]] bool contains(const T &V) const {
if (isSmall())
return vfind(V) != Vector.end();
return Set.find(V) != Set.end();
@@ -270,7 +276,7 @@ private:
/// If this set is of pointer values, transparently switch over to using
/// SmallPtrSet for performance.
template <typename PointeeType, unsigned N>
class SmallSet<PointeeType*, N> : public SmallPtrSet<PointeeType*, N> {};
class SmallSet<PointeeType *, N> : public SmallPtrSet<PointeeType *, N> {};
/// Equality comparison for SmallSet.
///
@@ -281,7 +287,8 @@ class SmallSet<PointeeType*, N> : public SmallPtrSet<PointeeType*, N> {};
/// For large-set mode amortized complexity is linear, worst case is O(N^2) (if
/// every hash collides).
template <typename T, unsigned LN, unsigned RN, typename C>
bool operator==(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
[[nodiscard]] bool operator==(const SmallSet<T, LN, C> &LHS,
const SmallSet<T, RN, C> &RHS) {
if (LHS.size() != RHS.size())
return false;
@@ -293,7 +300,8 @@ bool operator==(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
///
/// Equivalent to !(LHS == RHS). See operator== for performance notes.
template <typename T, unsigned LN, unsigned RN, typename C>
bool operator!=(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
[[nodiscard]] bool operator!=(const SmallSet<T, LN, C> &LHS,
const SmallSet<T, RN, C> &RHS) {
return !(LHS == RHS);
}

View File

@@ -14,6 +14,9 @@
#ifndef WPIUTIL_WPI_SMALLVECTOR_H
#define WPIUTIL_WPI_SMALLVECTOR_H
#include "wpi/util/ADL.hpp"
#include "wpi/util/DenseMapInfo.hpp"
// This file uses std::memcpy() to copy std::pair<unsigned int, unsigned int>.
// That type is POD, but the standard doesn't guarantee that. GCC doesn't treat
// the type as POD so it throws a warning. We want to consider this a warning
@@ -73,13 +76,13 @@ protected:
/// This is a helper for \a grow() that's out of line to reduce code
/// duplication. This function will report a fatal error if it can't grow at
/// least to \p MinSize.
void *mallocForGrow(void *FirstEl, size_t MinSize, size_t TSize,
size_t &NewCapacity);
LLVM_ABI void *mallocForGrow(void *FirstEl, size_t MinSize, size_t TSize,
size_t &NewCapacity);
/// This is an implementation of the grow() method which only works
/// on POD-like data types and is out of line to reduce code duplication.
/// This function will report a fatal error if it cannot increase capacity.
void grow_pod(void *FirstEl, size_t MinSize, size_t TSize);
LLVM_ABI void grow_pod(void *FirstEl, size_t MinSize, size_t TSize);
public:
size_t size() const { return Size; }
@@ -200,30 +203,34 @@ protected:
}
/// Check whether any part of the range will be invalidated by clearing.
void assertSafeToReferenceAfterClear(const T *From, const T *To) {
if (From == To)
return;
this->assertSafeToReferenceAfterResize(From, 0);
this->assertSafeToReferenceAfterResize(To - 1, 0);
template <class ItTy>
void assertSafeToReferenceAfterClear(ItTy From, ItTy To) {
if constexpr (std::is_pointer_v<ItTy> &&
std::is_same_v<
std::remove_const_t<std::remove_pointer_t<ItTy>>,
std::remove_const_t<T>>) {
if (From == To)
return;
this->assertSafeToReferenceAfterResize(From, 0);
this->assertSafeToReferenceAfterResize(To - 1, 0);
}
(void)From;
(void)To;
}
template <
class ItTy,
std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T *>::value,
bool> = false>
void assertSafeToReferenceAfterClear(ItTy, ItTy) {}
/// Check whether any part of the range will be invalidated by growing.
void assertSafeToAddRange(const T *From, const T *To) {
if (From == To)
return;
this->assertSafeToAdd(From, To - From);
this->assertSafeToAdd(To - 1, To - From);
template <class ItTy> void assertSafeToAddRange(ItTy From, ItTy To) {
if constexpr (std::is_pointer_v<ItTy> &&
std::is_same_v<std::remove_cv_t<std::remove_pointer_t<ItTy>>,
T>) {
if (From == To)
return;
this->assertSafeToAdd(From, To - From);
this->assertSafeToAdd(To - 1, To - From);
}
(void)From;
(void)To;
}
template <
class ItTy,
std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T *>::value,
bool> = false>
void assertSafeToAddRange(ItTy, ItTy) {}
/// Reserve enough space to add one element, and return the updated element
/// pointer in case it was a reference to the storage.
@@ -501,25 +508,22 @@ protected:
/// Copy the range [I, E) onto the uninitialized memory
/// starting with "Dest", constructing elements into it as needed.
template<typename It1, typename It2>
template <typename It1, typename It2>
static void uninitialized_copy(It1 I, It1 E, It2 Dest) {
// Arbitrary iterator types; just use the basic implementation.
std::uninitialized_copy(I, E, Dest);
}
/// Copy the range [I, E) onto the uninitialized memory
/// starting with "Dest", constructing elements into it as needed.
template <typename T1, typename T2>
static void uninitialized_copy(
T1 *I, T1 *E, T2 *Dest,
std::enable_if_t<std::is_same<std::remove_const_t<T1>, T2>::value> * =
nullptr) {
// Use memcpy for PODs iterated by pointers (which includes SmallVector
// iterators): std::uninitialized_copy optimizes to memmove, but we can
// use memcpy here. Note that I and E are iterators and thus might be
// invalid for memcpy if they are equal.
if (I != E)
memcpy(reinterpret_cast<void *>(Dest), I, (E - I) * sizeof(T));
if constexpr (std::is_pointer_v<It1> && std::is_pointer_v<It2> &&
std::is_same_v<
std::remove_const_t<std::remove_pointer_t<It1>>,
std::remove_pointer_t<It2>>) {
// Use memcpy for PODs iterated by pointers (which includes SmallVector
// iterators): std::uninitialized_copy optimizes to memmove, but we can
// use memcpy here. Note that I and E are iterators and thus might be
// invalid for memcpy if they are equal.
if (I != E)
std::memcpy(reinterpret_cast<void *>(Dest), I, (E - I) * sizeof(T));
} else {
// Arbitrary iterator types; just use the basic implementation.
std::uninitialized_copy(I, E, Dest);
}
}
/// Double the size of the allocated memory, guaranteeing space for at
@@ -562,7 +566,7 @@ protected:
public:
void push_back(ValueParamT Elt) {
const T *EltPtr = reserveForParamAndGetAddress(Elt);
memcpy(reinterpret_cast<void *>(this->end()), EltPtr, sizeof(T));
std::memcpy(reinterpret_cast<void *>(this->end()), EltPtr, sizeof(T));
this->set_size(this->size() + 1);
}
@@ -736,6 +740,12 @@ public:
void assign(const SmallVectorImpl &RHS) { assign(RHS.begin(), RHS.end()); }
template <typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
void assign(std::span<const U> AR) {
assign(AR.begin(), AR.end());
}
iterator erase(const_iterator CI) {
// Just cast away constness because this is a non-const member function.
iterator I = const_cast<iterator>(CI);
@@ -1230,7 +1240,7 @@ public:
}
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
explicit SmallVector(std::span<const U> A) : SmallVectorImpl<T>(N) {
this->append(A.begin(), A.end());
}
@@ -1291,30 +1301,49 @@ inline size_t capacity_in_bytes(const SmallVector<T, N> &X) {
template <typename RangeType>
using ValueTypeFromRangeType =
std::remove_const_t<std::remove_reference_t<decltype(*std::begin(
std::declval<RangeType &>()))>>;
std::remove_const_t<detail::ValueOfRange<RangeType>>;
/// Given a range of type R, iterate the entire range and return a
/// SmallVector with elements of the vector. This is useful, for example,
/// when you want to iterate a range and then sort the results.
template <unsigned Size, typename R>
SmallVector<ValueTypeFromRangeType<R>, Size> to_vector(R &&Range) {
return {std::begin(Range), std::end(Range)};
return {adl_begin(Range), adl_end(Range)};
}
template <typename R>
SmallVector<ValueTypeFromRangeType<R>> to_vector(R &&Range) {
return {std::begin(Range), std::end(Range)};
return {adl_begin(Range), adl_end(Range)};
}
template <typename Out, unsigned Size, typename R>
SmallVector<Out, Size> to_vector_of(R &&Range) {
return {std::begin(Range), std::end(Range)};
return {adl_begin(Range), adl_end(Range)};
}
template <typename Out, typename R> SmallVector<Out> to_vector_of(R &&Range) {
return {std::begin(Range), std::end(Range)};
return {adl_begin(Range), adl_end(Range)};
}
// Provide DenseMapInfo for SmallVector of a type which has info.
template <typename T, unsigned N> struct DenseMapInfo<wpi::util::SmallVector<T, N>> {
static SmallVector<T, N> getEmptyKey() {
return {DenseMapInfo<T>::getEmptyKey()};
}
static SmallVector<T, N> getTombstoneKey() {
return {DenseMapInfo<T>::getTombstoneKey()};
}
static unsigned getHashValue(const SmallVector<T, N> &V) {
return static_cast<unsigned>(hash_combine_range(V));
}
static bool isEqual(const SmallVector<T, N> &LHS,
const SmallVector<T, N> &RHS) {
return LHS == RHS;
}
};
template <typename T, typename Pred>
typename SmallVectorImpl<T>::size_type erase_if(
SmallVectorImpl<T>& c, Pred pred) {

View File

@@ -26,7 +26,7 @@ namespace sys {
constexpr bool IsBigEndianHost =
wpi::util::endianness::native == wpi::util::endianness::big;
static const bool IsLittleEndianHost = !IsBigEndianHost;
constexpr bool IsLittleEndianHost = !IsBigEndianHost;
inline unsigned char getSwappedBytes(unsigned char C) { return wpi::util::byteswap(C); }
inline signed char getSwappedBytes( signed char C) { return wpi::util::byteswap(C); }

View File

@@ -15,6 +15,7 @@
#define WPIUTIL_WPI_VERSIONTUPLE_H
#include "wpi/util/DenseMapInfo.hpp"
#include "wpi/util/Compiler.hpp"
#include <optional>
#include <string>
#include <tuple>
@@ -97,9 +98,7 @@ public:
/// Return a version tuple that contains a different major version but
/// everything else is the same.
VersionTuple withMajorReplaced(unsigned NewMajor) const {
return VersionTuple(NewMajor, Minor, Subminor, Build);
}
LLVM_ABI VersionTuple withMajorReplaced(unsigned NewMajor) const;
/// Return a version tuple that contains only components that are non-zero.
VersionTuple normalize() const {

View File

@@ -9,11 +9,12 @@
#ifndef WPIUTIL_WPI_WINDOWSERROR_H
#define WPIUTIL_WPI_WINDOWSERROR_H
#include "wpi/util/Compiler.hpp"
#include <system_error>
namespace wpi::util {
std::error_code mapLastWindowsError();
std::error_code mapWindowsError(unsigned EV);
LLVM_ABI std::error_code mapLastWindowsError();
LLVM_ABI std::error_code mapWindowsError(unsigned EV);
}
#endif

View File

@@ -15,6 +15,7 @@
#define WPIUTIL_WPI_BIT_H
#include "wpi/util/Compiler.hpp"
#include <cstddef> // for std::size_t
#include <cstdint>
#include <limits>
#include <type_traits>

View File

@@ -85,7 +85,9 @@ public:
using pointer = PointerT;
using reference = ReferenceT;
protected:
// Note: These were previously protected, but MSVC has trouble with SFINAE
// accessing protected members in derived class templates (specifically in
// iterator_adaptor_base::operator-). Making them public fixes the build.
enum {
IsRandomAccess = std::is_base_of<std::random_access_iterator_tag,
IteratorCategoryT>::value,
@@ -93,6 +95,7 @@ protected:
IteratorCategoryT>::value,
};
protected:
/// A proxy object for computing a reference via indirecting a copy of an
/// iterator. This is used in APIs which need to produce a reference via
/// indirection but for which the iterator object might be a temporary. The
@@ -267,6 +270,8 @@ public:
return *static_cast<DerivedT *>(this);
}
using BaseT::operator-;
template <bool Enabled = BaseT::IsRandomAccess,
typename = std::enable_if_t<Enabled>>
difference_type operator-(const DerivedT &RHS) const {
static_assert(
BaseT::IsRandomAccess,

View File

@@ -24,16 +24,6 @@
namespace wpi::util {
template <typename From, typename To, typename = void>
struct explicitly_convertible : std::false_type {};
template <typename From, typename To>
struct explicitly_convertible<
From, To,
std::void_t<decltype(static_cast<To>(
std::declval<std::add_rvalue_reference_t<From>>()))>> : std::true_type {
};
/// A range adaptor for a pair of iterators.
///
/// This just wraps two iterators into a range-compatible interface. Nothing
@@ -42,6 +32,10 @@ template <typename IteratorT>
class iterator_range {
IteratorT begin_iterator, end_iterator;
template <typename From, typename To>
using explicitly_converted_t = decltype(static_cast<To>(
std::declval<std::add_rvalue_reference_t<From>>()));
public:
#if defined(__GNUC__) && \
(__GNUC__ == 7 || (__GNUC__ == 8 && __GNUC_MINOR__ < 4))
@@ -49,10 +43,9 @@ public:
// See https://github.com/llvm/llvm-project/issues/63843
template <typename Container>
#else
template <
typename Container,
std::enable_if_t<explicitly_convertible<
wpi::util::detail::IterOfRange<Container>, IteratorT>::value> * = nullptr>
template <typename Container,
std::void_t<explicitly_converted_t<
wpi::util::detail::IterOfRange<Container>, IteratorT>> * = nullptr>
#endif
iterator_range(Container &&c)
: begin_iterator(adl_begin(c)), end_iterator(adl_end(c)) {

View File

@@ -13,6 +13,7 @@
#ifndef WPIUTIL_WPI_RAW_OS_OSTREAM_H
#define WPIUTIL_WPI_RAW_OS_OSTREAM_H
#include "wpi/util/Compiler.hpp"
#include "wpi/util/raw_ostream.hpp"
#include <iosfwd>
@@ -21,7 +22,7 @@ namespace wpi::util {
/// raw_os_ostream - A raw_ostream that writes to an std::ostream. This is a
/// simple adaptor class. It does not check for output errors; clients should
/// use the underlying stream to detect errors.
class raw_os_ostream : public raw_ostream {
class LLVM_ABI raw_os_ostream : public raw_ostream {
std::ostream &OS;
/// write_impl - See raw_ostream::write_impl.

View File

@@ -14,6 +14,7 @@
#define WPIUTIL_WPI_RAW_OSTREAM_H
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/Compiler.hpp"
#include <cassert>
#include <cstddef>
#include <cstdint>
@@ -40,7 +41,7 @@ namespace wpi::util {
/// output to a stream. It does not support seeking, reopening, rewinding, line
/// buffered disciplines etc. It is a simple buffer that outputs
/// a chunk at a time.
class raw_ostream {
class LLVM_ABI raw_ostream {
public:
// Class kinds to support LLVM-style RTTI.
enum class OStreamKind {
@@ -409,7 +410,7 @@ operator<<(OStream &&OS, const T &Value) {
/// An abstract base class for streams implementations that also support a
/// pwrite operation. This is useful for code that can mostly stream out data,
/// but needs to patch in a header that needs to know the output size.
class raw_pwrite_stream : public raw_ostream {
class LLVM_ABI raw_pwrite_stream : public raw_ostream {
virtual void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) = 0;
void anchor() override;
@@ -435,7 +436,7 @@ public:
/// A raw_ostream that writes to a file descriptor.
///
class raw_fd_ostream : public raw_pwrite_stream {
class LLVM_ABI raw_fd_ostream : public raw_pwrite_stream {
int FD;
bool ShouldClose;
bool SupportsSeeking = false;
@@ -548,17 +549,17 @@ public:
/// This returns a reference to a raw_fd_ostream for standard output. Use it
/// like: outs() << "foo" << "bar";
raw_fd_ostream &outs();
LLVM_ABI raw_fd_ostream &outs();
/// This returns a reference to a raw_ostream for standard error.
/// Use it like: errs() << "foo" << "bar";
/// By default, the stream is tied to stdout to ensure stdout is flushed before
/// stderr is written, to ensure the error messages are written in their
/// expected place.
raw_fd_ostream &errs();
LLVM_ABI raw_fd_ostream &errs();
/// This returns a reference to a raw_ostream which simply discards output.
raw_ostream &nulls();
LLVM_ABI raw_ostream &nulls();
//===----------------------------------------------------------------------===//
// File Streams
@@ -571,10 +572,10 @@ public:
/// Open the specified file for reading/writing/seeking. If an error occurs,
/// information about the error is put into EC, and the stream should be
/// immediately destroyed.
raw_fd_stream(std::string_view Filename, std::error_code &EC);
LLVM_ABI raw_fd_stream(std::string_view Filename, std::error_code &EC);
/// Check if \p OS is a pointer of type raw_fd_stream*.
static bool classof(const raw_ostream *OS);
LLVM_ABI static bool classof(const raw_ostream *OS);
};
//===----------------------------------------------------------------------===//
@@ -586,7 +587,7 @@ public:
/// raw_string_ostream operates without a buffer, delegating all memory
/// management to the std::string. Thus the std::string is always up-to-date,
/// may be used directly and there is no need to call flush().
class raw_string_ostream : public raw_ostream {
class LLVM_ABI raw_string_ostream : public raw_ostream {
std::string &OS;
/// See raw_ostream::write_impl.
@@ -616,7 +617,7 @@ public:
/// raw_svector_ostream operates without a buffer, delegating all memory
/// management to the SmallString. Thus the SmallString is always up-to-date,
/// may be used directly and there is no need to call flush().
class raw_svector_ostream : public raw_pwrite_stream {
class LLVM_ABI raw_svector_ostream : public raw_pwrite_stream {
SmallVectorImpl<char> &OS;
/// See raw_ostream::write_impl.
@@ -660,7 +661,7 @@ public:
/// raw_vector_ostream operates without a buffer, delegating all memory
/// management to the vector. Thus the vector is always up-to-date,
/// may be used directly and there is no need to call flush().
class raw_vector_ostream : public raw_pwrite_stream {
class LLVM_ABI raw_vector_ostream : public raw_pwrite_stream {
std::vector<char> &OS;
/// See raw_ostream::write_impl.
@@ -693,7 +694,7 @@ public:
/// raw_svector_ostream operates without a buffer, delegating all memory
/// management to the SmallString. Thus the SmallString is always up-to-date,
/// may be used directly and there is no need to call flush().
class raw_usvector_ostream : public raw_pwrite_stream {
class LLVM_ABI raw_usvector_ostream : public raw_pwrite_stream {
SmallVectorImpl<uint8_t> &OS;
/// See raw_ostream::write_impl.
@@ -727,7 +728,7 @@ public:
/// raw_vector_ostream operates without a buffer, delegating all memory
/// management to the vector. Thus the vector is always up-to-date,
/// may be used directly and there is no need to call flush().
class raw_uvector_ostream : public raw_pwrite_stream {
class LLVM_ABI raw_uvector_ostream : public raw_pwrite_stream {
std::vector<uint8_t> &OS;
/// See raw_ostream::write_impl.
@@ -758,7 +759,7 @@ public:
/// A raw_ostream that discards all output.
class raw_null_ostream : public raw_pwrite_stream {
class LLVM_ABI raw_null_ostream : public raw_pwrite_stream {
/// See raw_ostream::write_impl.
void write_impl(const char *Ptr, size_t size) override;
void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override;
@@ -768,11 +769,11 @@ class raw_null_ostream : public raw_pwrite_stream {
uint64_t current_pos() const override;
public:
explicit raw_null_ostream() = default;
explicit raw_null_ostream() : raw_pwrite_stream(/*Unbuffered=*/true) {}
~raw_null_ostream() override;
};
class buffer_ostream : public raw_svector_ostream {
class LLVM_ABI buffer_ostream : public raw_svector_ostream {
raw_ostream &OS;
SmallVector<char, 0> Buffer;
@@ -783,7 +784,7 @@ public:
~buffer_ostream() override { OS << str(); }
};
class buffer_unique_ostream : public raw_svector_ostream {
class LLVM_ABI buffer_unique_ostream : public raw_svector_ostream {
std::unique_ptr<raw_ostream> OS;
SmallVector<char, 0> Buffer;

View File

@@ -15,11 +15,9 @@
#include "wpi/util/Compiler.hpp"
#include <type_traits>
#include <utility>
namespace wpi::util {
/// Metafunction that determines whether the given type is either an
/// integral type or an enumeration type, including enum classes.
///
@@ -40,47 +38,28 @@ public:
};
/// If T is a pointer, just return it. If it is not, return T&.
template<typename T, typename Enable = void>
struct add_lvalue_reference_if_not_pointer { using type = T &; };
template <typename T>
struct add_lvalue_reference_if_not_pointer<
T, std::enable_if_t<std::is_pointer_v<T>>> {
using type = T;
template <typename T> struct add_lvalue_reference_if_not_pointer {
using type = std::conditional_t<std::is_pointer_v<T>, T, T &>;
};
/// If T is a pointer to X, return a pointer to const X. If it is not,
/// return const T.
template<typename T, typename Enable = void>
struct add_const_past_pointer { using type = const T; };
template <typename T>
struct add_const_past_pointer<T, std::enable_if_t<std::is_pointer_v<T>>> {
using type = const std::remove_pointer_t<T> *;
template <typename T> struct add_const_past_pointer {
using type = std::conditional_t<std::is_pointer_v<T>,
const std::remove_pointer_t<T> *, const T>;
};
template <typename T, typename Enable = void>
struct const_pointer_or_const_ref {
using type = const T &;
template <typename T> struct const_pointer_or_const_ref {
using type =
std::conditional_t<std::is_pointer_v<T>,
typename add_const_past_pointer<T>::type, const T &>;
};
template <typename T>
struct const_pointer_or_const_ref<T, std::enable_if_t<std::is_pointer_v<T>>> {
using type = typename add_const_past_pointer<T>::type;
};
namespace detail {
template<class T>
union trivial_helper {
T t;
};
} // end namespace detail
// https://stackoverflow.com/questions/55288555/c-check-if-statement-can-be-evaluated-constexpr
template<class Lambda, int=(Lambda{}(), 0)>
constexpr bool is_constexpr(Lambda) { return true; }
constexpr bool is_constexpr(...) { return false; }
} // end namespace wpi::util
} // namespace wpi::util
#endif // WPIUTIL_WPI_TYPE_TRAITS_H

View File

@@ -14,9 +14,12 @@
#include "CountCopyAndMove.hpp"
#include "wpi/util/DenseMapInfo.hpp"
#include "wpi/util/DenseMapInfoVariant.hpp"
#include "wpi/util/STLForwardCompat.hpp"
#include "wpi/util/SmallSet.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <map>
#include <optional>
#include <set>
#include <string_view>
#include <utility>
@@ -71,6 +74,16 @@ public:
int getValue() const { return Value; }
bool operator==(const CtorTester &RHS) const { return Value == RHS.Value; }
// Return the number of live CtorTester objects, excluding the empty and
// tombstone keys.
static size_t getNumConstructed() {
return std::count_if(Constructed.begin(), Constructed.end(),
[](const CtorTester *Obj) {
int V = Obj->getValue();
return V != -1 && V != -2;
});
}
};
std::set<CtorTester *> CtorTester::Constructed;
@@ -89,6 +102,10 @@ struct CtorTesterMapInfo {
CtorTester getTestKey(int i, CtorTester *) { return CtorTester(i); }
CtorTester getTestValue(int i, CtorTester *) { return CtorTester(42 + i); }
std::optional<uint32_t> getTestKey(int i, std::optional<uint32_t> *) {
return i;
}
// Test fixture, with helper functions implemented by forwarding to global
// function overloads selected by component types of the type parameter. This
// allows all of the map implementations to be tested with shared
@@ -116,16 +133,17 @@ typename T::mapped_type *const DenseMapTest<T>::dummy_value_ptr = nullptr;
// Register these types for testing.
// clang-format off
typedef ::testing::Types<DenseMap<uint32_t, uint32_t>,
DenseMap<uint32_t *, uint32_t *>,
DenseMap<CtorTester, CtorTester, CtorTesterMapInfo>,
DenseMap<EnumClass, uint32_t>,
SmallDenseMap<uint32_t, uint32_t>,
SmallDenseMap<uint32_t *, uint32_t *>,
SmallDenseMap<CtorTester, CtorTester, 4,
CtorTesterMapInfo>,
SmallDenseMap<EnumClass, uint32_t>
> DenseMapTestTypes;
using DenseMapTestTypes = ::testing::Types<
DenseMap<uint32_t, uint32_t>,
DenseMap<uint32_t *, uint32_t *>,
DenseMap<CtorTester, CtorTester, CtorTesterMapInfo>,
DenseMap<EnumClass, uint32_t>,
DenseMap<std::optional<uint32_t>, uint32_t>,
SmallDenseMap<uint32_t, uint32_t>,
SmallDenseMap<uint32_t *, uint32_t *>,
SmallDenseMap<CtorTester, CtorTester, 4, CtorTesterMapInfo>,
SmallDenseMap<EnumClass, uint32_t>,
SmallDenseMap<std::optional<uint32_t>, uint32_t>>;
// clang-format on
TYPED_TEST_SUITE(DenseMapTest, DenseMapTestTypes, );
@@ -186,6 +204,14 @@ TYPED_TEST(DenseMapTest, AtTest) {
EXPECT_EQ(this->getValue(0), this->Map.at(this->getKey(0)));
EXPECT_EQ(this->getValue(1), this->Map.at(this->getKey(1)));
EXPECT_EQ(this->getValue(2), this->Map.at(this->getKey(2)));
this->Map.at(this->getKey(0)) = this->getValue(1);
EXPECT_EQ(this->getValue(1), this->Map.at(this->getKey(0)));
const auto &ConstMap = this->Map;
EXPECT_EQ(this->getValue(1), ConstMap.at(this->getKey(0)));
EXPECT_EQ(this->getValue(1), ConstMap.at(this->getKey(1)));
EXPECT_EQ(this->getValue(2), ConstMap.at(this->getKey(2)));
}
// Test clear() method
@@ -245,6 +271,25 @@ TYPED_TEST(DenseMapTest, CopyConstructorNotSmallTest) {
EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);
}
// Test range constructors.
TYPED_TEST(DenseMapTest, RangeConstructorTest) {
using KeyAndValue =
std::pair<typename TypeParam::key_type, typename TypeParam::mapped_type>;
KeyAndValue PlainArray[] = {{this->getKey(0), this->getValue(0)},
{this->getKey(1), this->getValue(1)}};
TypeParam MapFromRange(wpi::util::from_range, PlainArray);
EXPECT_EQ(2u, MapFromRange.size());
EXPECT_EQ(this->getValue(0), MapFromRange[this->getKey(0)]);
EXPECT_EQ(this->getValue(1), MapFromRange[this->getKey(1)]);
TypeParam MapFromInitList({{this->getKey(0), this->getValue(1)},
{this->getKey(1), this->getValue(2)}});
EXPECT_EQ(2u, MapFromInitList.size());
EXPECT_EQ(this->getValue(1), MapFromInitList[this->getKey(0)]);
EXPECT_EQ(this->getValue(2), MapFromInitList[this->getKey(1)]);
}
// Test copying from a default-constructed map.
TYPED_TEST(DenseMapTest, CopyConstructorFromDefaultTest) {
TypeParam copyMap(this->Map);
@@ -363,6 +408,58 @@ TYPED_TEST(DenseMapTest, ConstIteratorTest) {
EXPECT_TRUE(cit == cit2);
}
// TYPED_TEST below cycles through different types. We define UniversalSmallSet
// here so that we'll use SmallSet or SmallPtrSet depending on whether the
// element type is a pointer.
template <typename T, unsigned N>
using UniversalSmallSet =
std::conditional_t<std::is_pointer_v<T>, SmallPtrSet<T, N>, SmallSet<T, N>>;
TYPED_TEST(DenseMapTest, KeysValuesIterator) {
UniversalSmallSet<typename TypeParam::key_type, 10> Keys;
UniversalSmallSet<typename TypeParam::mapped_type, 10> Values;
for (int I = 0; I < 10; ++I) {
auto K = this->getKey(I);
auto V = this->getValue(I);
Keys.insert(K);
Values.insert(V);
this->Map[K] = V;
}
UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys;
UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues;
for (auto K : this->Map.keys())
ActualKeys.insert(K);
for (auto V : this->Map.values())
ActualValues.insert(V);
EXPECT_EQ(Keys, ActualKeys);
EXPECT_EQ(Values, ActualValues);
}
TYPED_TEST(DenseMapTest, ConstKeysValuesIterator) {
UniversalSmallSet<typename TypeParam::key_type, 10> Keys;
UniversalSmallSet<typename TypeParam::mapped_type, 10> Values;
for (int I = 0; I < 10; ++I) {
auto K = this->getKey(I);
auto V = this->getValue(I);
Keys.insert(K);
Values.insert(V);
this->Map[K] = V;
}
const TypeParam &ConstMap = this->Map;
UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys;
UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues;
for (auto K : ConstMap.keys())
ActualKeys.insert(K);
for (auto V : ConstMap.values())
ActualValues.insert(V);
EXPECT_EQ(Keys, ActualKeys);
EXPECT_EQ(Values, ActualValues);
}
// Test initializer list construction.
TEST(DenseMapCustomTest, InitializerList) {
DenseMap<int, int> M({{0, 0}, {0, 1}, {1, 2}});
@@ -383,6 +480,28 @@ TEST(DenseMapCustomTest, EqualityComparison) {
EXPECT_NE(M1, M3);
}
TEST(DenseMapCustomTest, InsertRange) {
DenseMap<int, int> M;
std::pair<int, int> InputVals[3] = {{0, 0}, {0, 1}, {1, 2}};
M.insert_range(InputVals);
EXPECT_EQ(M.size(), 2u);
EXPECT_THAT(M, testing::UnorderedElementsAre(testing::Pair(0, 0),
testing::Pair(1, 2)));
}
TEST(SmallDenseMapCustomTest, InsertRange) {
SmallDenseMap<int, int> M;
std::pair<int, int> InputVals[3] = {{0, 0}, {0, 1}, {1, 2}};
M.insert_range(InputVals);
EXPECT_EQ(M.size(), 2u);
EXPECT_THAT(M, testing::UnorderedElementsAre(testing::Pair(0, 0),
testing::Pair(1, 2)));
}
// Test for the default minimum size of a DenseMap
TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) {
// IF THIS VALUE CHANGE, please update InitialSizeTest, InitFromIterator, and
@@ -527,6 +646,68 @@ TEST(DenseMapCustomTest, InsertOrAssignTest) {
EXPECT_EQ(1, CountCopyAndMove::MoveAssignments);
}
TEST(DenseMapCustomTest, EmplaceOrAssign) {
DenseMap<int, CountCopyAndMove> Map;
CountCopyAndMove::ResetCounts();
auto Try0 = Map.emplace_or_assign(3, 3);
EXPECT_TRUE(Try0.second);
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
EXPECT_EQ(0, CountCopyAndMove::TotalMoves());
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
CountCopyAndMove::ResetCounts();
auto Try1 = Map.emplace_or_assign(3, 4);
EXPECT_FALSE(Try1.second);
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
EXPECT_EQ(0, CountCopyAndMove::MoveConstructions);
EXPECT_EQ(1, CountCopyAndMove::MoveAssignments);
int Key = 5;
CountCopyAndMove::ResetCounts();
auto Try2 = Map.emplace_or_assign(Key, 3);
EXPECT_TRUE(Try2.second);
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
EXPECT_EQ(0, CountCopyAndMove::TotalMoves());
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
CountCopyAndMove::ResetCounts();
auto Try3 = Map.emplace_or_assign(Key, 4);
EXPECT_FALSE(Try3.second);
EXPECT_EQ(0, CountCopyAndMove::TotalCopies());
EXPECT_EQ(1, CountCopyAndMove::ValueConstructions);
EXPECT_EQ(0, CountCopyAndMove::MoveConstructions);
EXPECT_EQ(1, CountCopyAndMove::MoveAssignments);
}
struct NonDefaultConstructible {
unsigned V;
NonDefaultConstructible(unsigned V) : V(V) {};
bool operator==(const NonDefaultConstructible &Other) const {
return V == Other.V;
}
};
TEST(DenseMapCustomTest, LookupOr) {
DenseMap<int, NonDefaultConstructible> M;
M.insert_or_assign(0, 3u);
M.insert_or_assign(1, 2u);
M.insert_or_assign(1, 0u);
EXPECT_EQ(M.lookup_or(0, 4u), 3u);
EXPECT_EQ(M.lookup_or(1, 4u), 0u);
EXPECT_EQ(M.lookup_or(2, 4u), 4u);
}
TEST(DenseMapCustomTest, LookupOrConstness) {
DenseMap<int, unsigned *> M;
unsigned Default = 3u;
unsigned *Ret = M.lookup_or(0, &Default);
EXPECT_EQ(Ret, &Default);
}
// Key traits that allows lookup with either an unsigned or char* key;
// In the latter case, "a" == 0, "b" == 1 and so on.
struct TestDenseMapInfo {
@@ -568,6 +749,15 @@ TEST(DenseMapCustomTest, FindAsTest) {
EXPECT_TRUE(map.find_as("d") == map.end());
}
TEST(DenseMapCustomTest, SmallDenseMapFromRange) {
std::pair<int, std::string_view> PlainArray[] = {{0, "0"}, {1, "1"}, {2, "2"}};
SmallDenseMap<int, std::string_view> M(wpi::util::from_range, PlainArray);
EXPECT_EQ(3u, M.size());
using testing::Pair;
EXPECT_THAT(M, testing::UnorderedElementsAre(Pair(0, "0"), Pair(1, "1"),
Pair(2, "2")));
}
TEST(DenseMapCustomTest, SmallDenseMapInitializerList) {
SmallDenseMap<int, int> M = {{0, 0}, {0, 1}, {1, 2}};
EXPECT_EQ(2u, M.size());
@@ -761,4 +951,133 @@ TEST(DenseMapCustomTest, VariantSupport) {
EXPECT_FALSE(DenseMapInfo<variant>::isEqual(Keys[2], Keys[2]));
}
TEST(DenseMapCustomTest, InitSize) {
constexpr unsigned ElemSize = sizeof(std::pair<int *, int>);
{
DenseMap<int *, int> Map;
EXPECT_EQ(ElemSize * 0U, Map.getMemorySize());
}
{
DenseMap<int *, int> Map(0);
EXPECT_EQ(ElemSize * 0U, Map.getMemorySize());
}
{
DenseMap<int *, int> Map(1);
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
DenseMap<int *, int> Map(2);
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
DenseMap<int *, int> Map(3);
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
}
{
int A, B;
DenseMap<int *, int> Map = {{&A, 1}, {&B, 2}};
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
int A, B, C;
DenseMap<int *, int> Map = {{&A, 1}, {&B, 2}, {&C, 3}};
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
}
}
TEST(SmallDenseMapCustomTest, InitSize) {
constexpr unsigned ElemSize = sizeof(std::pair<int *, int>);
{
SmallDenseMap<int *, int> Map;
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
SmallDenseMap<int *, int> Map(0);
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
SmallDenseMap<int *, int> Map(1);
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
SmallDenseMap<int *, int> Map(2);
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
SmallDenseMap<int *, int> Map(3);
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
}
{
int A, B;
SmallDenseMap<int *, int> Map = {{&A, 1}, {&B, 2}};
EXPECT_EQ(ElemSize * 4U, Map.getMemorySize());
}
{
int A, B, C;
SmallDenseMap<int *, int> Map = {{&A, 1}, {&B, 2}, {&C, 3}};
EXPECT_EQ(ElemSize * 8U, Map.getMemorySize());
}
}
TEST(DenseMapCustomTest, KeyDtor) {
// This test relies on CtorTester being non-trivially destructible.
static_assert(!std::is_trivially_destructible_v<CtorTester>,
"CtorTester must not be trivially destructible");
// Test that keys are destructed on scope exit.
EXPECT_EQ(0u, CtorTester::getNumConstructed());
{
DenseMap<CtorTester, int, CtorTesterMapInfo> Map;
Map.try_emplace(CtorTester(0), 1);
Map.try_emplace(CtorTester(1), 2);
EXPECT_EQ(2u, CtorTester::getNumConstructed());
}
EXPECT_EQ(0u, CtorTester::getNumConstructed());
// Test that keys are destructed on erase and shrink_and_clear.
EXPECT_EQ(0u, CtorTester::getNumConstructed());
{
DenseMap<CtorTester, int, CtorTesterMapInfo> Map;
Map.try_emplace(CtorTester(0), 1);
Map.try_emplace(CtorTester(1), 2);
EXPECT_EQ(2u, CtorTester::getNumConstructed());
Map.erase(CtorTester(1));
EXPECT_EQ(1u, CtorTester::getNumConstructed());
Map.shrink_and_clear();
EXPECT_EQ(0u, CtorTester::getNumConstructed());
}
EXPECT_EQ(0u, CtorTester::getNumConstructed());
}
TEST(DenseMapCustomTest, ValueDtor) {
// This test relies on CtorTester being non-trivially destructible.
static_assert(!std::is_trivially_destructible_v<CtorTester>,
"CtorTester must not be trivially destructible");
// Test that values are destructed on scope exit.
EXPECT_EQ(0u, CtorTester::getNumConstructed());
{
DenseMap<int, CtorTester> Map;
Map.try_emplace(0, CtorTester(1));
Map.try_emplace(1, CtorTester(2));
EXPECT_EQ(2u, CtorTester::getNumConstructed());
}
EXPECT_EQ(0u, CtorTester::getNumConstructed());
// Test that values are destructed on erase and shrink_and_clear.
EXPECT_EQ(0u, CtorTester::getNumConstructed());
{
DenseMap<int, CtorTester> Map;
Map.try_emplace(0, CtorTester(1));
Map.try_emplace(1, CtorTester(2));
EXPECT_EQ(2u, CtorTester::getNumConstructed());
Map.erase(1);
EXPECT_EQ(1u, CtorTester::getNumConstructed());
Map.shrink_and_clear();
EXPECT_EQ(0u, CtorTester::getNumConstructed());
}
EXPECT_EQ(0u, CtorTester::getNumConstructed());
}
} // namespace

View File

@@ -23,16 +23,15 @@ TEST(Endian, Read) {
unsigned char littleval[] = {0x00, 0x04, 0x03, 0x02, 0x01};
int32_t BigAsHost = 0x00010203;
EXPECT_EQ(BigAsHost,
(endian::read<int32_t, wpi::util::endianness::big, unaligned>(bigval)));
(endian::read<int32_t, unaligned>(bigval, wpi::util::endianness::big)));
int32_t LittleAsHost = 0x02030400;
EXPECT_EQ(
LittleAsHost,
(endian::read<int32_t, wpi::util::endianness::little, unaligned>(littleval)));
EXPECT_EQ(LittleAsHost, (endian::read<int32_t, unaligned>(
littleval, wpi::util::endianness::little)));
EXPECT_EQ(
(endian::read<int32_t, wpi::util::endianness::big, unaligned>(bigval + 1)),
(endian::read<int32_t, wpi::util::endianness::little, unaligned>(littleval +
1)));
(endian::read<int32_t, unaligned>(bigval + 1, wpi::util::endianness::big)),
(endian::read<int32_t, unaligned>(littleval + 1,
wpi::util::endianness::little)));
}
TEST(Endian, WriteNext) {
@@ -200,26 +199,26 @@ TEST(Endian, WriteBitAligned) {
TEST(Endian, Write) {
unsigned char data[5];
endian::write<int32_t, wpi::util::endianness::big, unaligned>(data, -1362446643);
endian::write<int32_t, unaligned>(data, -1362446643, wpi::util::endianness::big);
EXPECT_EQ(data[0], 0xAE);
EXPECT_EQ(data[1], 0xCA);
EXPECT_EQ(data[2], 0xB6);
EXPECT_EQ(data[3], 0xCD);
endian::write<int32_t, wpi::util::endianness::big, unaligned>(data + 1,
-1362446643);
endian::write<int32_t, unaligned>(data + 1, -1362446643,
wpi::util::endianness::big);
EXPECT_EQ(data[1], 0xAE);
EXPECT_EQ(data[2], 0xCA);
EXPECT_EQ(data[3], 0xB6);
EXPECT_EQ(data[4], 0xCD);
endian::write<int32_t, wpi::util::endianness::little, unaligned>(data,
-1362446643);
endian::write<int32_t, unaligned>(data, -1362446643,
wpi::util::endianness::little);
EXPECT_EQ(data[0], 0xCD);
EXPECT_EQ(data[1], 0xB6);
EXPECT_EQ(data[2], 0xCA);
EXPECT_EQ(data[3], 0xAE);
endian::write<int32_t, wpi::util::endianness::little, unaligned>(data + 1,
-1362446643);
endian::write<int32_t, unaligned>(data + 1, -1362446643,
wpi::util::endianness::little);
EXPECT_EQ(data[1], 0xCD);
EXPECT_EQ(data[2], 0xB6);
EXPECT_EQ(data[3], 0xCA);
@@ -236,6 +235,7 @@ TEST(Endian, PackedEndianSpecificIntegral) {
reinterpret_cast<little32_t *>(little + 1);
EXPECT_EQ(*big_val, *little_val);
EXPECT_EQ(big_val->value(), little_val->value());
}
TEST(Endian, PacketEndianSpecificIntegralAsEnum) {

View File

@@ -11,7 +11,6 @@
#include "gtest/gtest.h"
#include <memory>
#include <type_traits>
using namespace wpi::util;

View File

@@ -152,23 +152,23 @@ TEST(MathExtras, PowerOf2Ceil) {
EXPECT_EQ(8U, PowerOf2Ceil(7U));
}
TEST(MathExtras, CTLog2) {
EXPECT_EQ(CTLog2<1ULL << 0>(), 0U);
EXPECT_EQ(CTLog2<1ULL << 1>(), 1U);
EXPECT_EQ(CTLog2<1ULL << 2>(), 2U);
EXPECT_EQ(CTLog2<1ULL << 3>(), 3U);
EXPECT_EQ(CTLog2<1ULL << 4>(), 4U);
EXPECT_EQ(CTLog2<1ULL << 5>(), 5U);
EXPECT_EQ(CTLog2<1ULL << 6>(), 6U);
EXPECT_EQ(CTLog2<1ULL << 7>(), 7U);
EXPECT_EQ(CTLog2<1ULL << 8>(), 8U);
EXPECT_EQ(CTLog2<1ULL << 9>(), 9U);
EXPECT_EQ(CTLog2<1ULL << 10>(), 10U);
EXPECT_EQ(CTLog2<1ULL << 11>(), 11U);
EXPECT_EQ(CTLog2<1ULL << 12>(), 12U);
EXPECT_EQ(CTLog2<1ULL << 13>(), 13U);
EXPECT_EQ(CTLog2<1ULL << 14>(), 14U);
EXPECT_EQ(CTLog2<1ULL << 15>(), 15U);
TEST(MathExtras, ConstantLog2) {
EXPECT_EQ(ConstantLog2<1ULL << 0>(), 0U);
EXPECT_EQ(ConstantLog2<1ULL << 1>(), 1U);
EXPECT_EQ(ConstantLog2<1ULL << 2>(), 2U);
EXPECT_EQ(ConstantLog2<1ULL << 3>(), 3U);
EXPECT_EQ(ConstantLog2<1ULL << 4>(), 4U);
EXPECT_EQ(ConstantLog2<1ULL << 5>(), 5U);
EXPECT_EQ(ConstantLog2<1ULL << 6>(), 6U);
EXPECT_EQ(ConstantLog2<1ULL << 7>(), 7U);
EXPECT_EQ(ConstantLog2<1ULL << 8>(), 8U);
EXPECT_EQ(ConstantLog2<1ULL << 9>(), 9U);
EXPECT_EQ(ConstantLog2<1ULL << 10>(), 10U);
EXPECT_EQ(ConstantLog2<1ULL << 11>(), 11U);
EXPECT_EQ(ConstantLog2<1ULL << 12>(), 12U);
EXPECT_EQ(ConstantLog2<1ULL << 13>(), 13U);
EXPECT_EQ(ConstantLog2<1ULL << 14>(), 14U);
EXPECT_EQ(ConstantLog2<1ULL << 15>(), 15U);
}
TEST(MathExtras, MinAlign) {

View File

@@ -12,9 +12,9 @@ using namespace wpi::util;
namespace {
typedef PointerUnion<int *, float *> PU;
typedef PointerUnion<int *, float *, long long *> PU3;
typedef PointerUnion<int *, float *, long long *, double *> PU4;
using PU = PointerUnion<int *, float *>;
using PU3 = PointerUnion<int *, float *, long long *>;
using PU4 = PointerUnion<int *, float *, long long *, double *>;
struct PointerUnionTest : public testing::Test {
float f;
@@ -116,9 +116,9 @@ TEST_F(PointerUnionTest, Get) {
template<int I> struct alignas(8) Aligned {};
typedef PointerUnion<Aligned<0> *, Aligned<1> *, Aligned<2> *, Aligned<3> *,
Aligned<4> *, Aligned<5> *, Aligned<6> *, Aligned<7> *>
PU8;
using PU8 =
PointerUnion<Aligned<0> *, Aligned<1> *, Aligned<2> *, Aligned<3> *,
Aligned<4> *, Aligned<5> *, Aligned<6> *, Aligned<7> *>;
TEST_F(PointerUnionTest, ManyElements) {
Aligned<0> a0;

View File

@@ -10,6 +10,10 @@
#include "CountCopyAndMove.hpp"
#include "gtest/gtest.h"
#include <optional>
#include <type_traits>
#include <utility>
namespace {
template <typename T>
@@ -45,6 +49,25 @@ TYPED_TEST(STLForwardCompatRemoveCVRefTest, RemoveCVRefT) {
wpi::util::remove_cvref_t<From>>::value));
}
template <typename T> class TypeIdentityTest : public ::testing::Test {
public:
using TypeIdentity = wpi::util::type_identity<T>;
};
struct A {
struct B {};
};
using TypeIdentityTestTypes =
::testing::Types<int, volatile int, A, const A::B>;
TYPED_TEST_SUITE(TypeIdentityTest, TypeIdentityTestTypes, /*NameGenerator*/);
TYPED_TEST(TypeIdentityTest, Identity) {
// TestFixture is the instantiated TypeIdentityTest.
EXPECT_TRUE(
(std::is_same_v<TypeParam, typename TestFixture::TypeIdentity::type>));
}
TEST(TransformTest, TransformStd) {
std::optional<int> A;
@@ -123,6 +146,26 @@ TEST(TransformTest, MoveTransformLlvm) {
EXPECT_EQ(0, CountCopyAndMove::Destructions);
}
TEST(TransformTest, TransformCategory) {
struct StructA {
int x;
};
struct StructB : StructA {
StructB(StructA &&A) : StructA(std::move(A)) {}
};
std::optional<StructA> A{StructA{}};
wpi::util::transformOptional(A, [](auto &&s) {
EXPECT_FALSE(std::is_rvalue_reference_v<decltype(s)>);
return StructB{std::move(s)};
});
wpi::util::transformOptional(std::move(A), [](auto &&s) {
EXPECT_TRUE(std::is_rvalue_reference_v<decltype(s)>);
return StructB{std::move(s)};
});
}
TEST(TransformTest, ToUnderlying) {
enum E { A1 = 0, B1 = -1 };
static_assert(wpi::util::to_underlying(A1) == 0);
@@ -140,4 +183,26 @@ TEST(TransformTest, ToUnderlying) {
static_assert(wpi::util::to_underlying(E3::B3) == 0);
}
TEST(STLForwardCompatTest, IdentityCxx20) {
wpi::util::identity identity;
// Test with an lvalue.
int X = 42;
int &Y = identity(X);
EXPECT_EQ(&X, &Y);
// Test with a const lvalue.
const int CX = 10;
const int &CY = identity(CX);
EXPECT_EQ(&CX, &CY);
// Test with an rvalue.
EXPECT_EQ(identity(123), 123);
// Test perfect forwarding.
static_assert(std::is_same_v<int &, decltype(identity(X))>);
static_assert(std::is_same_v<const int &, decltype(identity(CX))>);
static_assert(std::is_same_v<int &&, decltype(identity(int(5)))>);
}
} // namespace

View File

@@ -58,7 +58,7 @@ TEST(SmallPtrSetTest, GrowthTest) {
SmallPtrSet<int *, 4> s;
typedef SmallPtrSet<int *, 4>::iterator iter;
using iter = SmallPtrSet<int *, 4>::iterator;
s.insert(&buf[0]);
s.insert(&buf[1]);
@@ -412,6 +412,25 @@ TEST(SmallPtrSetTest, RemoveIf) {
EXPECT_FALSE(Removed);
}
TEST(SmallPtrSetTest, CtorRange) {
int V0 = 0;
int V1 = 1;
int V2 = 2;
int *Args[] = {&V2, &V0, &V1};
SmallPtrSet<int *, 4> Set(wpi::util::from_range, Args);
EXPECT_THAT(Set, UnorderedElementsAre(&V0, &V1, &V2));
}
TEST(SmallPtrSetTest, InsertRange) {
int V0 = 0;
int V1 = 1;
int V2 = 2;
SmallPtrSet<int *, 4> Set;
int *Args[] = {&V2, &V0, &V1};
Set.insert_range(Args);
EXPECT_THAT(Set, UnorderedElementsAre(&V0, &V1, &V2));
}
TEST(SmallPtrSetTest, Reserve) {
// Check that we don't do anything silly when using reserve().
SmallPtrSet<int *, 4> Set;
@@ -457,4 +476,8 @@ TEST(SmallPtrSetTest, Reserve) {
EXPECT_EQ(Set.capacity(), 128u);
EXPECT_EQ(Set.size(), 6u);
EXPECT_THAT(Set, UnorderedElementsAre(&Vals[0], &Vals[1], &Vals[2], &Vals[3], &Vals[4], &Vals[5]));
// Reserving 192 should result in 256 buckets.
Set.reserve(192);
EXPECT_EQ(Set.capacity(), 256u);
}

View File

@@ -24,13 +24,6 @@ TEST(SmallSetTest, ConstructorIteratorPair) {
EXPECT_THAT(S, testing::UnorderedElementsAreArray(L));
}
TEST(SmallSet, ConstructorRange) {
std::initializer_list<int> L = {1, 2, 3, 4, 5};
SmallSet<int, 4> S(wpi::util::make_range(std::begin(L), std::end(L)));
EXPECT_THAT(S, testing::UnorderedElementsAreArray(L));
}
TEST(SmallSet, ConstructorInitializerList) {
std::initializer_list<int> L = {1, 2, 3, 4, 5};
SmallSet<int, 4> S = {1, 2, 3, 4, 5};
@@ -127,6 +120,19 @@ TEST(SmallSetTest, InsertPerfectFwd) {
}
}
TEST(SmallSetTest, CtorRange) {
constexpr unsigned Args[] = {3, 1, 2};
SmallSet<int, 4> s1(wpi::util::from_range, Args);
EXPECT_THAT(s1, ::testing::UnorderedElementsAre(1, 2, 3));
}
TEST(SmallSetTest, InsertRange) {
SmallSet<int, 4> s1;
constexpr unsigned Args[] = {3, 1, 2};
s1.insert_range(Args);
EXPECT_THAT(s1, ::testing::UnorderedElementsAre(1, 2, 3));
}
TEST(SmallSetTest, Grow) {
SmallSet<int, 4> s1;

View File

@@ -23,7 +23,7 @@ namespace {
// Test fixture class
class SmallStringTest : public testing::Test {
protected:
typedef SmallString<40> StringType;
using StringType = SmallString<40>;
StringType theString;

View File

@@ -12,6 +12,7 @@
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/Compiler.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <array>
#include <list>
@@ -132,24 +133,24 @@ public:
return c0.getValue() == c1.getValue();
}
friend bool LLVM_ATTRIBUTE_UNUSED operator!=(const Constructable &c0,
const Constructable &c1) {
[[maybe_unused]] friend bool operator!=(const Constructable &c0,
const Constructable &c1) {
return c0.getValue() != c1.getValue();
}
friend bool operator<(const Constructable &c0, const Constructable &c1) {
return c0.getValue() < c1.getValue();
}
friend bool LLVM_ATTRIBUTE_UNUSED operator<=(const Constructable &c0,
const Constructable &c1) {
[[maybe_unused]] friend bool operator<=(const Constructable &c0,
const Constructable &c1) {
return c0.getValue() <= c1.getValue();
}
friend bool LLVM_ATTRIBUTE_UNUSED operator>(const Constructable &c0,
const Constructable &c1) {
[[maybe_unused]] friend bool operator>(const Constructable &c0,
const Constructable &c1) {
return c0.getValue() > c1.getValue();
}
friend bool LLVM_ATTRIBUTE_UNUSED operator>=(const Constructable &c0,
const Constructable &c1) {
[[maybe_unused]] friend bool operator>=(const Constructable &c0,
const Constructable &c1) {
return c0.getValue() >= c1.getValue();
}
};
@@ -163,7 +164,7 @@ int Constructable::numCopyAssignmentCalls;
int Constructable::numMoveAssignmentCalls;
struct NonCopyable {
NonCopyable() {}
NonCopyable() = default;
NonCopyable(NonCopyable &&) {}
NonCopyable &operator=(NonCopyable &&) { return *this; }
private:
@@ -230,13 +231,10 @@ protected:
VectorT otherVector;
};
typedef ::testing::Types<SmallVector<Constructable, 0>,
SmallVector<Constructable, 1>,
SmallVector<Constructable, 2>,
SmallVector<Constructable, 4>,
SmallVector<Constructable, 5>
> SmallVectorTestTypes;
using SmallVectorTestTypes = ::testing::Types<
SmallVector<Constructable, 0>, SmallVector<Constructable, 1>,
SmallVector<Constructable, 2>, SmallVector<Constructable, 4>,
SmallVector<Constructable, 5>>;
TYPED_TEST_SUITE(SmallVectorTest, SmallVectorTestTypes, );
// Constructor test.
@@ -541,11 +539,11 @@ TYPED_TEST(SmallVectorTest, AppendNonIterTest) {
}
struct output_iterator {
typedef std::output_iterator_tag iterator_category;
typedef int value_type;
typedef int difference_type;
typedef value_type *pointer;
typedef value_type &reference;
using iterator_category = std::output_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = value_type *;
using reference = value_type &;
operator int() { return 2; }
operator Constructable() { return 7; }
};
@@ -604,6 +602,15 @@ TYPED_TEST(SmallVectorTest, AssignSmallVector) {
assertValuesInOrder(V, 2u, 7, 7);
}
TYPED_TEST(SmallVectorTest, AssignSpan) {
SCOPED_TRACE("AssignSpan");
auto &V = this->theVector;
Constructable Other[] = {7, 8, 9};
V.push_back(Constructable(1));
V.assign(std::span<const Constructable>(Other));
assertValuesInOrder(V, 3u, 7, 8, 9);
}
// Move-assign test
TYPED_TEST(SmallVectorTest, MoveAssignTest) {
SCOPED_TRACE("MoveAssignTest");
@@ -891,7 +898,7 @@ protected:
VectorT2 otherVector;
};
typedef ::testing::Types<
using DualSmallVectorTestTypes = ::testing::Types<
// Small mode -> Small mode.
std::pair<SmallVector<Constructable, 4>, SmallVector<Constructable, 4>>,
// Small mode -> Big mode.
@@ -899,8 +906,7 @@ typedef ::testing::Types<
// Big mode -> Small mode.
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 4>>,
// Big mode -> Big mode.
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 2>>
> DualSmallVectorTestTypes;
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 2>>>;
TYPED_TEST_SUITE(DualSmallVectorsTest, DualSmallVectorTestTypes, );
@@ -1134,6 +1140,17 @@ TEST(SmallVectorTest, DefaultInlinedElements) {
EXPECT_EQ(NestedV[0][0][0], 42);
}
namespace namespace_with_adl {
struct MyVector {
std::vector<int> data;
};
std::vector<int>::const_iterator begin(const MyVector &V) {
return V.data.begin();
}
std::vector<int>::const_iterator end(const MyVector &V) { return V.data.end(); }
} // namespace namespace_with_adl
TEST(SmallVectorTest, ToVector) {
{
std::vector<char> v = {'a', 'b', 'c'};
@@ -1151,6 +1168,15 @@ TEST(SmallVectorTest, ToVector) {
for (size_t I = 0; I < v.size(); ++I)
EXPECT_EQ(v[I], Vector[I]);
}
{
// Check that to_vector and to_vector_of work with types that require ADL
// for being/end iterators.
namespace_with_adl::MyVector V = {{1, 2, 3}};
auto IntVector = to_vector(V);
EXPECT_THAT(IntVector, testing::ElementsAre(1, 2, 3));
IntVector = to_vector<3>(V);
EXPECT_THAT(IntVector, testing::ElementsAre(1, 2, 3));
}
}
struct To {
@@ -1209,6 +1235,15 @@ TEST(SmallVectorTest, ToVectorOf) {
for (size_t I = 0; I < StdVector.size(); ++I)
EXPECT_EQ(StdVector[I], Vector[I]);
}
{
// Check that to_vector works with types that require ADL for being/end
// iterators.
namespace_with_adl::MyVector V = {{1, 2, 3}};
auto UnsignedVector = to_vector_of<unsigned>(V);
EXPECT_THAT(UnsignedVector, testing::ElementsAre(1u, 2u, 3u));
UnsignedVector = to_vector_of<unsigned, 3>(V);
EXPECT_THAT(UnsignedVector, testing::ElementsAre(1u, 2u, 3u));
}
}
template <class VectorT>