[wpiutil] Upgrade to LLVM 16.0.6 (#5435)

Fixes #5332.
This commit is contained in:
Tyler Veness
2023-07-12 22:50:13 -07:00
committed by GitHub
parent 701df9eb87
commit 828bc5276f
77 changed files with 3798 additions and 1879 deletions

View File

@@ -99,6 +99,28 @@ public:
void PrintStats() const {}
};
namespace detail {
template <typename Alloc> class AllocatorHolder : Alloc {
public:
AllocatorHolder() = default;
AllocatorHolder(const Alloc &A) : Alloc(A) {}
AllocatorHolder(Alloc &&A) : Alloc(static_cast<Alloc &&>(A)) {}
Alloc &getAllocator() { return *this; }
const Alloc &getAllocator() const { return *this; }
};
template <typename Alloc> class AllocatorHolder<Alloc &> {
Alloc &A;
public:
AllocatorHolder(Alloc &A) : A(A) {}
Alloc &getAllocator() { return A; }
const Alloc &getAllocator() const { return A; }
};
} // namespace detail
} // namespace wpi
#endif // WPIUTIL_WPI_ALLOCATORBASE_H

View File

@@ -0,0 +1,808 @@
//===- llvm/Support/Casting.h - Allow flexible, checked, casts --*- 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the isa<X>(), cast<X>(), dyn_cast<X>(),
// cast_if_present<X>(), and dyn_cast_if_present<X>() templates.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_CASTING_H
#define WPIUTIL_WPI_CASTING_H
#include "wpi/Compiler.h"
#include "wpi/type_traits.h"
#include <cassert>
#include <memory>
#include <optional>
#include <type_traits>
namespace wpi {
//===----------------------------------------------------------------------===//
// simplify_type
//===----------------------------------------------------------------------===//
/// Define a template that can be specialized by smart pointers to reflect the
/// fact that they are automatically dereferenced, and are not involved with the
/// template selection process... the default implementation is a noop.
// TODO: rename this and/or replace it with other cast traits.
template <typename From> struct simplify_type {
using SimpleType = From; // The real type this represents...
// An accessor to get the real value...
static SimpleType &getSimplifiedValue(From &Val) { return Val; }
};
template <typename From> struct simplify_type<const From> {
using NonConstSimpleType = typename simplify_type<From>::SimpleType;
using SimpleType = typename add_const_past_pointer<NonConstSimpleType>::type;
using RetType =
typename add_lvalue_reference_if_not_pointer<SimpleType>::type;
static RetType getSimplifiedValue(const From &Val) {
return simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val));
}
};
// TODO: add this namespace once everyone is switched to using the new
// interface.
// namespace detail {
//===----------------------------------------------------------------------===//
// isa_impl
//===----------------------------------------------------------------------===//
// The core of the implementation of isa<X> is here; To and From should be
// the names of classes. This template can be specialized to customize the
// implementation of isa<> without rewriting it from scratch.
template <typename To, typename From, typename Enabler = void> struct isa_impl {
static inline bool doit(const From &Val) { return To::classof(&Val); }
};
// Always allow upcasts, and perform no dynamic check for them.
template <typename To, typename From>
struct isa_impl<To, From, std::enable_if_t<std::is_base_of<To, From>::value>> {
static inline bool doit(const From &) { return true; }
};
template <typename To, typename From> struct isa_impl_cl {
static inline bool doit(const From &Val) {
return isa_impl<To, From>::doit(Val);
}
};
template <typename To, typename From> struct isa_impl_cl<To, const From> {
static inline bool doit(const From &Val) {
return isa_impl<To, From>::doit(Val);
}
};
template <typename To, typename From>
struct isa_impl_cl<To, const std::unique_ptr<From>> {
static inline bool doit(const std::unique_ptr<From> &Val) {
assert(Val && "isa<> used on a null pointer");
return isa_impl_cl<To, From>::doit(*Val);
}
};
template <typename To, typename From> struct isa_impl_cl<To, From *> {
static inline bool doit(const From *Val) {
assert(Val && "isa<> used on a null pointer");
return isa_impl<To, From>::doit(*Val);
}
};
template <typename To, typename From> struct isa_impl_cl<To, From *const> {
static inline bool doit(const From *Val) {
assert(Val && "isa<> used on a null pointer");
return isa_impl<To, From>::doit(*Val);
}
};
template <typename To, typename From> struct isa_impl_cl<To, const From *> {
static inline bool doit(const From *Val) {
assert(Val && "isa<> used on a null pointer");
return isa_impl<To, From>::doit(*Val);
}
};
template <typename To, typename From>
struct isa_impl_cl<To, const From *const> {
static inline bool doit(const From *Val) {
assert(Val && "isa<> used on a null pointer");
return isa_impl<To, From>::doit(*Val);
}
};
template <typename To, typename From, typename SimpleFrom>
struct isa_impl_wrap {
// When From != SimplifiedType, we can simplify the type some more by using
// the simplify_type template.
static bool doit(const From &Val) {
return isa_impl_wrap<To, SimpleFrom,
typename simplify_type<SimpleFrom>::SimpleType>::
doit(simplify_type<const From>::getSimplifiedValue(Val));
}
};
template <typename To, typename FromTy>
struct isa_impl_wrap<To, FromTy, FromTy> {
// When From == SimpleType, we are as simple as we are going to get.
static bool doit(const FromTy &Val) {
return isa_impl_cl<To, FromTy>::doit(Val);
}
};
//===----------------------------------------------------------------------===//
// cast_retty + cast_retty_impl
//===----------------------------------------------------------------------===//
template <class To, class From> struct cast_retty;
// Calculate what type the 'cast' function should return, based on a requested
// type of To and a source type of From.
template <class To, class From> struct cast_retty_impl {
using ret_type = To &; // Normal case, return Ty&
};
template <class To, class From> struct cast_retty_impl<To, const From> {
using ret_type = const To &; // Normal case, return Ty&
};
template <class To, class From> struct cast_retty_impl<To, From *> {
using ret_type = To *; // Pointer arg case, return Ty*
};
template <class To, class From> struct cast_retty_impl<To, const From *> {
using ret_type = const To *; // Constant pointer arg case, return const Ty*
};
template <class To, class From> struct cast_retty_impl<To, const From *const> {
using ret_type = const To *; // Constant pointer arg case, return const Ty*
};
template <class To, class From>
struct cast_retty_impl<To, std::unique_ptr<From>> {
private:
using PointerType = typename cast_retty_impl<To, From *>::ret_type;
using ResultType = std::remove_pointer_t<PointerType>;
public:
using ret_type = std::unique_ptr<ResultType>;
};
template <class To, class From, class SimpleFrom> struct cast_retty_wrap {
// When the simplified type and the from type are not the same, use the type
// simplifier to reduce the type, then reuse cast_retty_impl to get the
// resultant type.
using ret_type = typename cast_retty<To, SimpleFrom>::ret_type;
};
template <class To, class FromTy> struct cast_retty_wrap<To, FromTy, FromTy> {
// When the simplified type is equal to the from type, use it directly.
using ret_type = typename cast_retty_impl<To, FromTy>::ret_type;
};
template <class To, class From> struct cast_retty {
using ret_type = typename cast_retty_wrap<
To, From, typename simplify_type<From>::SimpleType>::ret_type;
};
//===----------------------------------------------------------------------===//
// cast_convert_val
//===----------------------------------------------------------------------===//
// Ensure the non-simple values are converted using the simplify_type template
// that may be specialized by smart pointers...
//
template <class To, class From, class SimpleFrom> struct cast_convert_val {
// This is not a simple type, use the template to simplify it...
static typename cast_retty<To, From>::ret_type doit(const From &Val) {
return cast_convert_val<To, SimpleFrom,
typename simplify_type<SimpleFrom>::SimpleType>::
doit(simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val)));
}
};
template <class To, class FromTy> struct cast_convert_val<To, FromTy, FromTy> {
// If it's a reference, switch to a pointer to do the cast and then deref it.
static typename cast_retty<To, FromTy>::ret_type doit(const FromTy &Val) {
return *(std::remove_reference_t<typename cast_retty<To, FromTy>::ret_type>
*)&const_cast<FromTy &>(Val);
}
};
template <class To, class FromTy>
struct cast_convert_val<To, FromTy *, FromTy *> {
// If it's a pointer, we can use c-style casting directly.
static typename cast_retty<To, FromTy *>::ret_type doit(const FromTy *Val) {
return (typename cast_retty<To, FromTy *>::ret_type) const_cast<FromTy *>(
Val);
}
};
//===----------------------------------------------------------------------===//
// is_simple_type
//===----------------------------------------------------------------------===//
template <class X> struct is_simple_type {
static const bool value =
std::is_same<X, typename simplify_type<X>::SimpleType>::value;
};
// } // namespace detail
//===----------------------------------------------------------------------===//
// CastIsPossible
//===----------------------------------------------------------------------===//
/// This struct provides a way to check if a given cast is possible. It provides
/// a static function called isPossible that is used to check if a cast can be
/// performed. It should be overridden like this:
///
/// template<> struct CastIsPossible<foo, bar> {
/// static inline bool isPossible(const bar &b) {
/// return bar.isFoo();
/// }
/// };
template <typename To, typename From, typename Enable = void>
struct CastIsPossible {
static inline bool isPossible(const From &f) {
return isa_impl_wrap<
To, const From,
typename simplify_type<const From>::SimpleType>::doit(f);
}
};
// Needed for optional unwrapping. This could be implemented with isa_impl, but
// we want to implement things in the new method and move old implementations
// over. In fact, some of the isa_impl templates should be moved over to
// CastIsPossible.
template <typename To, typename From>
struct CastIsPossible<To, std::optional<From>> {
static inline bool isPossible(const std::optional<From> &f) {
assert(f && "CastIsPossible::isPossible called on a nullopt!");
return isa_impl_wrap<
To, const From,
typename simplify_type<const From>::SimpleType>::doit(*f);
}
};
/// Upcasting (from derived to base) and casting from a type to itself should
/// always be possible.
template <typename To, typename From>
struct CastIsPossible<To, From,
std::enable_if_t<std::is_base_of<To, From>::value>> {
static inline bool isPossible(const From &f) { return true; }
};
//===----------------------------------------------------------------------===//
// Cast traits
//===----------------------------------------------------------------------===//
/// All of these cast traits are meant to be implementations for useful casts
/// that users may want to use that are outside the standard behavior. An
/// example of how to use a special cast called `CastTrait` is:
///
/// template<> struct CastInfo<foo, bar> : public CastTrait<foo, bar> {};
///
/// Essentially, if your use case falls directly into one of the use cases
/// supported by a given cast trait, simply inherit your special CastInfo
/// directly from one of these to avoid having to reimplement the boilerplate
/// `isPossible/castFailed/doCast/doCastIfPossible`. A cast trait can also
/// provide a subset of those functions.
/// This cast trait just provides castFailed for the specified `To` type to make
/// CastInfo specializations more declarative. In order to use this, the target
/// result type must be `To` and `To` must be constructible from `nullptr`.
template <typename To> struct NullableValueCastFailed {
static To castFailed() { return To(nullptr); }
};
/// This cast trait just provides the default implementation of doCastIfPossible
/// to make CastInfo specializations more declarative. The `Derived` template
/// parameter *must* be provided for forwarding castFailed and doCast.
template <typename To, typename From, typename Derived>
struct DefaultDoCastIfPossible {
static To doCastIfPossible(From f) {
if (!Derived::isPossible(f))
return Derived::castFailed();
return Derived::doCast(f);
}
};
namespace detail {
/// A helper to derive the type to use with `Self` for cast traits, when the
/// provided CRTP derived type is allowed to be void.
template <typename OptionalDerived, typename Default>
using SelfType = std::conditional_t<std::is_same<OptionalDerived, void>::value,
Default, OptionalDerived>;
} // namespace detail
/// This cast trait provides casting for the specific case of casting to a
/// value-typed object from a pointer-typed object. Note that `To` must be
/// nullable/constructible from a pointer to `From` to use this cast.
template <typename To, typename From, typename Derived = void>
struct ValueFromPointerCast
: public CastIsPossible<To, From *>,
public NullableValueCastFailed<To>,
public DefaultDoCastIfPossible<
To, From *,
detail::SelfType<Derived, ValueFromPointerCast<To, From>>> {
static inline To doCast(From *f) { return To(f); }
};
/// This cast trait provides std::unique_ptr casting. It has the semantics of
/// moving the contents of the input unique_ptr into the output unique_ptr
/// 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 *> {
using Self = detail::SelfType<Derived, UniquePtrCast<To, From>>;
using CastResultType = std::unique_ptr<
std::remove_reference_t<typename cast_retty<To, From>::ret_type>>;
static inline CastResultType doCast(std::unique_ptr<From> &&f) {
return CastResultType((typename CastResultType::element_type *)f.release());
}
static inline CastResultType castFailed() { return CastResultType(nullptr); }
static inline CastResultType doCastIfPossible(std::unique_ptr<From> &&f) {
if (!Self::isPossible(f))
return castFailed();
return doCast(f);
}
};
/// This cast trait provides std::optional<T> casting. This means that if you
/// have a value type, you can cast it to another value type and have dyn_cast
/// return an std::optional<T>.
template <typename To, typename From, typename Derived = void>
struct OptionalValueCast
: public CastIsPossible<To, From>,
public DefaultDoCastIfPossible<
std::optional<To>, From,
detail::SelfType<Derived, OptionalValueCast<To, From>>> {
static inline std::optional<To> castFailed() { return std::optional<To>{}; }
static inline std::optional<To> doCast(const From &f) { return To(f); }
};
/// Provides a cast trait that strips `const` from types to make it easier to
/// implement a const-version of a non-const cast. It just removes boilerplate
/// and reduces the amount of code you as the user need to implement. You can
/// use it like this:
///
/// template<> struct CastInfo<foo, bar> {
/// ...verbose implementation...
/// };
///
/// template<> struct CastInfo<foo, const bar> : public
/// ConstStrippingForwardingCast<foo, const bar, CastInfo<foo, bar>> {};
///
template <typename To, typename From, typename ForwardTo>
struct ConstStrippingForwardingCast {
// Remove the pointer if it exists, then we can get rid of consts/volatiles.
using DecayedFrom = std::remove_cv_t<std::remove_pointer_t<From>>;
// Now if it's a pointer, add it back. Otherwise, we want a ref.
using NonConstFrom = std::conditional_t<std::is_pointer<From>::value,
DecayedFrom *, DecayedFrom &>;
static inline bool isPossible(const From &f) {
return ForwardTo::isPossible(const_cast<NonConstFrom>(f));
}
static inline decltype(auto) castFailed() { return ForwardTo::castFailed(); }
static inline decltype(auto) doCast(const From &f) {
return ForwardTo::doCast(const_cast<NonConstFrom>(f));
}
static inline decltype(auto) doCastIfPossible(const From &f) {
return ForwardTo::doCastIfPossible(const_cast<NonConstFrom>(f));
}
};
/// Provides a cast trait that uses a defined pointer to pointer cast as a base
/// for reference-to-reference casts. Note that it does not provide castFailed
/// and doCastIfPossible because a pointer-to-pointer cast would likely just
/// return `nullptr` which could cause nullptr dereference. You can use it like
/// this:
///
/// template <> struct CastInfo<foo, bar *> { ... verbose implementation... };
///
/// template <>
/// struct CastInfo<foo, bar>
/// : public ForwardToPointerCast<foo, bar, CastInfo<foo, bar *>> {};
///
template <typename To, typename From, typename ForwardTo>
struct ForwardToPointerCast {
static inline bool isPossible(const From &f) {
return ForwardTo::isPossible(&f);
}
static inline decltype(auto) doCast(const From &f) {
return *ForwardTo::doCast(&f);
}
};
//===----------------------------------------------------------------------===//
// CastInfo
//===----------------------------------------------------------------------===//
/// This struct provides a method for customizing the way a cast is performed.
/// It inherits from CastIsPossible, to support the case of declaring many
/// CastIsPossible specializations without having to specialize the full
/// CastInfo.
///
/// In order to specialize different behaviors, specify different functions in
/// your CastInfo specialization.
/// For isa<> customization, provide:
///
/// `static bool isPossible(const From &f)`
///
/// For cast<> customization, provide:
///
/// `static To doCast(const From &f)`
///
/// For dyn_cast<> and the *_if_present<> variants' customization, provide:
///
/// `static To castFailed()` and `static To doCastIfPossible(const From &f)`
///
/// Your specialization might look something like this:
///
/// template<> struct CastInfo<foo, bar> : public CastIsPossible<foo, bar> {
/// static inline foo doCast(const bar &b) {
/// return foo(const_cast<bar &>(b));
/// }
/// static inline foo castFailed() { return foo(); }
/// static inline foo doCastIfPossible(const bar &b) {
/// if (!CastInfo<foo, bar>::isPossible(b))
/// return castFailed();
/// return doCast(b);
/// }
/// };
// The default implementations of CastInfo don't use cast traits for now because
// we need to specify types all over the place due to the current expected
// casting behavior and the way cast_retty works. New use cases can and should
// take advantage of the cast traits whenever possible!
template <typename To, typename From, typename Enable = void>
struct CastInfo : public CastIsPossible<To, From> {
using Self = CastInfo<To, From, Enable>;
using CastReturnType = typename cast_retty<To, From>::ret_type;
static inline CastReturnType doCast(const From &f) {
return cast_convert_val<
To, From,
typename simplify_type<From>::SimpleType>::doit(const_cast<From &>(f));
}
// This assumes that you can construct the cast return type from `nullptr`.
// This is largely to support legacy use cases - if you don't want this
// behavior you should specialize CastInfo for your use case.
static inline CastReturnType castFailed() { return CastReturnType(nullptr); }
static inline CastReturnType doCastIfPossible(const From &f) {
if (!Self::isPossible(f))
return castFailed();
return doCast(f);
}
};
/// This struct provides an overload for CastInfo where From has simplify_type
/// defined. This simply forwards to the appropriate CastInfo with the
/// simplified type/value, so you don't have to implement both.
template <typename To, typename From>
struct CastInfo<To, From, std::enable_if_t<!is_simple_type<From>::value>> {
using Self = CastInfo<To, From>;
using SimpleFrom = typename simplify_type<From>::SimpleType;
using SimplifiedSelf = CastInfo<To, SimpleFrom>;
static inline bool isPossible(From &f) {
return SimplifiedSelf::isPossible(
simplify_type<From>::getSimplifiedValue(f));
}
static inline decltype(auto) doCast(From &f) {
return SimplifiedSelf::doCast(simplify_type<From>::getSimplifiedValue(f));
}
static inline decltype(auto) castFailed() {
return SimplifiedSelf::castFailed();
}
static inline decltype(auto) doCastIfPossible(From &f) {
return SimplifiedSelf::doCastIfPossible(
simplify_type<From>::getSimplifiedValue(f));
}
};
//===----------------------------------------------------------------------===//
// Pre-specialized CastInfo
//===----------------------------------------------------------------------===//
/// Provide a CastInfo specialized for std::unique_ptr.
template <typename To, typename From>
struct CastInfo<To, std::unique_ptr<From>> : public UniquePtrCast<To, From> {};
/// Provide a CastInfo specialized for std::optional<From>. It's assumed that if
/// 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> {
};
/// 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>
[[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);
}
/// cast<X> - Return the argument parameter cast to the specified type. This
/// casting operator asserts that the type is correct, so it does not return
/// null on failure. It does not allow a null argument (use cast_if_present for
/// that). It is typically used like this:
///
/// cast<Instruction>(myVal)->getParent()
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) cast(const From &Val) {
assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
return CastInfo<To, const From>::doCast(Val);
}
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) cast(From &Val) {
assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
return CastInfo<To, From>::doCast(Val);
}
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) cast(From *Val) {
assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
return CastInfo<To, From *>::doCast(Val);
}
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) cast(std::unique_ptr<From> &&Val) {
assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!");
return CastInfo<To, std::unique_ptr<From>>::doCast(std::move(Val));
}
//===----------------------------------------------------------------------===//
// ValueIsPresent
//===----------------------------------------------------------------------===//
template <typename T>
constexpr bool IsNullable =
std::is_pointer_v<T> || std::is_constructible_v<T, std::nullptr_t>;
/// ValueIsPresent provides a way to check if a value is, well, present. For
/// pointers, this is the equivalent of checking against nullptr, for Optionals
/// this is the equivalent of checking hasValue(). It also provides a method for
/// unwrapping a value (think calling .value() on an optional).
// Generic values can't *not* be present.
template <typename T, typename Enable = void> struct ValueIsPresent {
using UnwrappedType = T;
static inline bool isPresent(const T &t) { return true; }
static inline decltype(auto) unwrapValue(T &t) { return t; }
};
// Optional provides its own way to check if something is present.
template <typename T> struct ValueIsPresent<std::optional<T>> {
using UnwrappedType = T;
static inline bool isPresent(const std::optional<T> &t) {
return t.has_value();
}
static inline decltype(auto) unwrapValue(std::optional<T> &t) { return *t; }
};
// If something is "nullable" then we just compare it to nullptr to see if it
// exists.
template <typename T>
struct ValueIsPresent<T, std::enable_if_t<IsNullable<T>>> {
using UnwrappedType = T;
static inline bool isPresent(const T &t) { return t != T(nullptr); }
static inline decltype(auto) unwrapValue(T &t) { return t; }
};
namespace detail {
// Convenience function we can use to check if a value is present. Because of
// simplify_type, we have to call it on the simplified type for now.
template <typename T> inline bool isPresent(const T &t) {
return ValueIsPresent<typename simplify_type<T>::SimpleType>::isPresent(
simplify_type<T>::getSimplifiedValue(const_cast<T &>(t)));
}
// Convenience function we can use to unwrap a value.
template <typename T> inline decltype(auto) unwrapValue(T &t) {
return ValueIsPresent<T>::unwrapValue(t);
}
} // namespace detail
/// dyn_cast<X> - Return the argument parameter cast to the specified type. This
/// casting operator returns null if the argument is of the wrong type, so it
/// can be used to test for a type as well as cast if successful. The value
/// passed in must be present, if not, use dyn_cast_if_present. This should be
/// used in the context of an if statement like this:
///
/// if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... }
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) dyn_cast(const From &Val) {
assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
return CastInfo<To, const From>::doCastIfPossible(Val);
}
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) dyn_cast(From &Val) {
assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
return CastInfo<To, From>::doCastIfPossible(Val);
}
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) dyn_cast(From *Val) {
assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
return CastInfo<To, From *>::doCastIfPossible(Val);
}
template <typename To, typename From>
[[nodiscard]] inline decltype(auto) dyn_cast(std::unique_ptr<From> &&Val) {
assert(detail::isPresent(Val) && "dyn_cast on a non-existent value");
return CastInfo<To, std::unique_ptr<From>>::doCastIfPossible(
std::forward<std::unique_ptr<From> &&>(Val));
}
/// isa_and_present<X> - Functionally identical to isa, except that a null value
/// is accepted.
template <typename... X, class Y>
[[nodiscard]] inline bool isa_and_present(const Y &Val) {
if (!detail::isPresent(Val))
return false;
return isa<X...>(Val);
}
template <typename... X, class Y>
[[nodiscard]] inline bool isa_and_nonnull(const Y &Val) {
return isa_and_present<X...>(Val);
}
/// cast_if_present<X> - Functionally identical to cast, except that a null
/// value is accepted.
template <class X, class Y>
[[nodiscard]] inline auto cast_if_present(const Y &Val) {
if (!detail::isPresent(Val))
return CastInfo<X, const Y>::castFailed();
assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!");
return cast<X>(detail::unwrapValue(Val));
}
template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y &Val) {
if (!detail::isPresent(Val))
return CastInfo<X, Y>::castFailed();
assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!");
return cast<X>(detail::unwrapValue(Val));
}
template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y *Val) {
if (!detail::isPresent(Val))
return CastInfo<X, Y *>::castFailed();
assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!");
return cast<X>(detail::unwrapValue(Val));
}
template <class X, class Y>
[[nodiscard]] inline auto cast_if_present(std::unique_ptr<Y> &&Val) {
if (!detail::isPresent(Val))
return UniquePtrCast<X, Y>::castFailed();
return UniquePtrCast<X, Y>::doCast(std::move(Val));
}
// Provide a forwarding from cast_or_null to cast_if_present for current
// users. This is deprecated and will be removed in a future patch, use
// cast_if_present instead.
template <class X, class Y> auto cast_or_null(const Y &Val) {
return cast_if_present<X>(Val);
}
template <class X, class Y> auto cast_or_null(Y &Val) {
return cast_if_present<X>(Val);
}
template <class X, class Y> auto cast_or_null(Y *Val) {
return cast_if_present<X>(Val);
}
template <class X, class Y> auto cast_or_null(std::unique_ptr<Y> &&Val) {
return cast_if_present<X>(std::move(Val));
}
/// dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a
/// null (or none in the case of optionals) value is accepted.
template <class X, class Y> auto dyn_cast_if_present(const Y &Val) {
if (!detail::isPresent(Val))
return CastInfo<X, const Y>::castFailed();
return CastInfo<X, const Y>::doCastIfPossible(detail::unwrapValue(Val));
}
template <class X, class Y> auto dyn_cast_if_present(Y &Val) {
if (!detail::isPresent(Val))
return CastInfo<X, Y>::castFailed();
return CastInfo<X, Y>::doCastIfPossible(detail::unwrapValue(Val));
}
template <class X, class Y> auto dyn_cast_if_present(Y *Val) {
if (!detail::isPresent(Val))
return CastInfo<X, Y *>::castFailed();
return CastInfo<X, Y *>::doCastIfPossible(detail::unwrapValue(Val));
}
// Forwards to dyn_cast_if_present to avoid breaking current users. This is
// deprecated and will be removed in a future patch, use
// cast_if_present instead.
template <class X, class Y> auto dyn_cast_or_null(const Y &Val) {
return dyn_cast_if_present<X>(Val);
}
template <class X, class Y> auto dyn_cast_or_null(Y &Val) {
return dyn_cast_if_present<X>(Val);
}
template <class X, class Y> auto dyn_cast_or_null(Y *Val) {
return dyn_cast_if_present<X>(Val);
}
/// unique_dyn_cast<X> - Given a unique_ptr<Y>, try to return a unique_ptr<X>,
/// taking ownership of the input pointer iff isa<X>(Val) is true. If the
/// cast is successful, From refers to nullptr on exit and the casted value
/// is returned. If the cast is unsuccessful, the function returns nullptr
/// and From is unchanged.
template <class X, class Y>
[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType
unique_dyn_cast(std::unique_ptr<Y> &Val) {
if (!isa<X>(Val))
return nullptr;
return cast<X>(std::move(Val));
}
template <class X, class Y>
[[nodiscard]] inline auto unique_dyn_cast(std::unique_ptr<Y> &&Val) {
return unique_dyn_cast<X, Y>(Val);
}
// unique_dyn_cast_or_null<X> - Functionally identical to unique_dyn_cast,
// except that a null value is accepted.
template <class X, class Y>
[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType
unique_dyn_cast_or_null(std::unique_ptr<Y> &Val) {
if (!Val)
return nullptr;
return unique_dyn_cast<X, Y>(Val);
}
template <class X, class Y>
[[nodiscard]] inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &&Val) {
return unique_dyn_cast_or_null<X, Y>(Val);
}
} // end namespace wpi
#endif // WPIUTIL_WPI_CASTING_H

View File

@@ -38,6 +38,10 @@
# define __has_builtin(x) 0
#endif
#ifndef __has_include
# define __has_include(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
@@ -101,26 +105,6 @@
#endif
#endif
/// Does the compiler support ref-qualifiers for *this?
///
/// Sadly, this is separate from just rvalue reference support because GCC
/// and MSVC implemented this later than everything else. This appears to be
/// corrected in MSVC 2019 but not MSVC 2017.
/// FIXME: Remove LLVM_HAS_RVALUE_REFERENCE_THIS macro
#define LLVM_HAS_RVALUE_REFERENCE_THIS 1
/// Expands to '&' if ref-qualifiers for *this are supported.
///
/// This can be used to provide lvalue/rvalue overrides of member functions.
/// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS
#ifndef LLVM_LVALUE_FUNCTION
#if LLVM_HAS_RVALUE_REFERENCE_THIS
#define LLVM_LVALUE_FUNCTION &
#else
#define LLVM_LVALUE_FUNCTION
#endif
#endif
/// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked
/// into a shared library, then the class should be private to the library and
/// not accessible from outside it. Can also be used to mark variables and
@@ -130,8 +114,9 @@
/// LLVM_EXTERNAL_VISIBILITY - classes, functions, and variables marked with
/// this attribute will be made public and visible outside of any shared library
/// they are linked in to.
#if __has_attribute(visibility) && !defined(__MINGW32__) && \
!defined(__CYGWIN__) && !defined(_WIN32)
#if __has_attribute(visibility) && \
(!(defined(_WIN32) || defined(__CYGWIN__)) || \
(defined(__MINGW32__) && defined(__clang__)))
#define LLVM_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden")))
#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS)
#define LLVM_EXTERNAL_VISIBILITY __attribute__((visibility("default")))
@@ -159,23 +144,10 @@
#endif
#endif
/// LLVM_NODISCARD - Warn if a type or return value is discarded.
// Use the 'nodiscard' attribute in C++17 or newer mode.
#ifndef LLVM_NODISCARD
#if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(nodiscard)
#define LLVM_NODISCARD [[nodiscard]]
#elif LLVM_HAS_CPP_ATTRIBUTE(clang::warn_unused_result)
#define LLVM_NODISCARD [[clang::warn_unused_result]]
// Clang in C++14 mode claims that it has the 'nodiscard' attribute, but also
// warns in the pedantic mode that 'nodiscard' is a C++17 extension (PR33518).
// Use the 'nodiscard' attribute in C++14 mode only with GCC.
// TODO: remove this workaround when PR33518 is resolved.
#elif defined(__GNUC__) && LLVM_HAS_CPP_ATTRIBUTE(nodiscard)
#define LLVM_NODISCARD [[nodiscard]]
#if defined(__clang__)
#define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX)))
#else
#define LLVM_NODISCARD
#endif
#define LLVM_DEPRECATED(MSG, FIX) [[deprecated(MSG)]]
#endif
// Indicate that a non-static, non-const C++ member function reinitializes
@@ -356,23 +328,18 @@
#endif
#endif
// LLVM_ATTRIBUTE_DEPRECATED(decl, "message")
// This macro will be removed.
// Use C++14's attribute instead: [[deprecated("message")]]
#ifndef LLVM_ATTRIBUTE_DEPRECATED
#define LLVM_ATTRIBUTE_DEPRECATED(decl, message) [[deprecated(message)]] decl
#endif
/// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands
/// to an expression which states that it is undefined behavior for the
/// compiler to reach this point. Otherwise is not defined.
///
/// '#else' is intentionally left out so that other macro logic (e.g.,
/// LLVM_ASSUME_ALIGNED and wpi_unreachable()) can detect whether
/// LLVM_BUILTIN_UNREACHABLE has a definition.
#ifndef LLVM_BUILTIN_UNREACHABLE
#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
# define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
#elif defined(_MSC_VER)
# define LLVM_BUILTIN_UNREACHABLE __assume(false)
#else
# define LLVM_BUILTIN_UNREACHABLE
#endif
#endif
@@ -454,24 +421,6 @@
#endif
#endif
/// \macro LLVM_PTR_SIZE
/// A constant integer equivalent to the value of sizeof(void*).
/// Generally used in combination with alignas or when doing computation in the
/// preprocessor.
#ifndef LLVM_PTR_SIZE
#ifdef __SIZEOF_POINTER__
# define LLVM_PTR_SIZE __SIZEOF_POINTER__
#elif defined(_WIN64)
# define LLVM_PTR_SIZE 8
#elif defined(_WIN32)
# define LLVM_PTR_SIZE 4
#elif defined(_MSC_VER)
# error "could not determine LLVM_PTR_SIZE as a constant int for MSVC"
#else
# define LLVM_PTR_SIZE sizeof(void *)
#endif
#endif
/// \macro LLVM_MEMORY_SANITIZER_BUILD
/// Whether LLVM itself is built with MemorySanitizer instrumentation.
#if __has_feature(memory_sanitizer)
@@ -489,13 +438,34 @@
/// Whether LLVM itself is built with AddressSanitizer instrumentation.
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
# define LLVM_ADDRESS_SANITIZER_BUILD 1
#if __has_include(<sanitizer/asan_interface.h>)
# include <sanitizer/asan_interface.h>
#else
// These declarations exist to support ASan with MSVC. If MSVC eventually ships
// asan_interface.h in their headers, then we can remove this.
#ifdef __cplusplus
extern "C" {
#endif
void __asan_poison_memory_region(void const volatile *addr, size_t size);
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
#ifdef __cplusplus
} // extern "C"
#endif
#endif
#else
# define LLVM_ADDRESS_SANITIZER_BUILD 0
# define __asan_poison_memory_region(p, size)
# define __asan_unpoison_memory_region(p, size)
#endif
/// \macro LLVM_HWADDRESS_SANITIZER_BUILD
/// Whether LLVM itself is built with HWAddressSanitizer instrumentation.
#if __has_feature(hwaddress_sanitizer)
#define LLVM_HWADDRESS_SANITIZER_BUILD 1
#else
#define LLVM_HWADDRESS_SANITIZER_BUILD 0
#endif
/// \macro LLVM_THREAD_SANITIZER_BUILD
/// Whether LLVM itself is built with ThreadSanitizer instrumentation.
#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)

View File

@@ -6,25 +6,41 @@
*
*==------------------------------------------------------------------------==*/
/*
* Copyright 2001-2004 Unicode, Inc.
* Copyright © 1991-2015 Unicode, Inc. All rights reserved.
* Distributed under the Terms of Use in
* http://www.unicode.org/copyright.html.
*
* Disclaimer
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of the Unicode data files and any associated documentation
* (the "Data Files") or Unicode software and any associated documentation
* (the "Software") to deal in the Data Files or Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, and/or sell copies of
* the Data Files or Software, and to permit persons to whom the Data Files
* or Software are furnished to do so, provided that
* (a) this copyright and permission notice appear with all copies
* of the Data Files or Software,
* (b) this copyright and permission notice appear in associated
* documentation, and
* (c) there is clear notice in each modified Data File or in the Software
* as well as in the documentation associated with the Data File(s) or
* Software that the data or software has been modified.
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
* THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
* ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT OF THIRD PARTY RIGHTS.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
* NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
* DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THE DATA FILES OR SOFTWARE.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
* Except as contained in this notice, the name of a copyright holder
* shall not be used in advertising or otherwise to promote the sale,
* use or other dealings in these Data Files or Software without prior
* written authorization of the copyright holder.
*/
/* ---------------------------------------------------------------------
@@ -89,10 +105,9 @@
#ifndef WPIUTIL_WPI_CONVERTUTF_H
#define WPIUTIL_WPI_CONVERTUTF_H
#include <span>
#include <cstddef>
#include <string>
#include <span>
#include <string_view>
#include <system_error>
@@ -126,6 +141,9 @@ typedef bool Boolean; /* 0 or 1 */
#define UNI_UTF16_BYTE_ORDER_MARK_NATIVE 0xFEFF
#define UNI_UTF16_BYTE_ORDER_MARK_SWAPPED 0xFFFE
#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 */
@@ -178,6 +196,8 @@ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd);
unsigned getUTF8SequenceSize(const UTF8 *source, const UTF8 *sourceEnd);
unsigned getNumBytesForUTF8(UTF8 firstByte);
/*************************************************************************/
@@ -279,6 +299,24 @@ bool convertUTF16ToUTF8String(std::span<const char> SrcBytes, SmallVectorImpl<ch
*/
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.
*
* \param [in] SrcBytes A buffer of what is assumed to be UTF-32 encoded text.
* \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);
/**
* Converts a UTF32 string into a UTF8 std::string.
*
* \param [in] Src A buffer of UTF-32 encoded text.
* \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);
/**
* Converts a UTF-8 string into a UTF-16 string with native endianness.
*

View File

@@ -95,9 +95,7 @@ public:
return makeConstIterator(getBucketsEnd(), getBucketsEnd(), *this, true);
}
LLVM_NODISCARD bool empty() const {
return getNumEntries() == 0;
}
[[nodiscard]] bool empty() const { return getNumEntries() == 0; }
unsigned size() const { return getNumEntries(); }
/// Grow the densemap so that it can contain at least \p NumEntries items
@@ -137,6 +135,7 @@ public:
}
}
assert(NumEntries == 0 && "Node count imbalance!");
(void)NumEntries;
}
setNumEntries(0);
setNumTombstones(0);
@@ -906,6 +905,8 @@ class SmallDenseMap
public:
explicit SmallDenseMap(unsigned NumInitBuckets = 0) {
if (NumInitBuckets > InlineBuckets)
NumInitBuckets = NextPowerOf2(NumInitBuckets - 1);
init(NumInitBuckets);
}
@@ -1196,8 +1197,7 @@ class DenseMapIterator : DebugEpochBase::HandleBase {
public:
using difference_type = ptrdiff_t;
using value_type =
typename std::conditional<IsConst, const Bucket, Bucket>::type;
using value_type = std::conditional_t<IsConst, const Bucket, Bucket>;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::forward_iterator_tag;

View File

@@ -18,7 +18,9 @@
#include <cstddef>
#include <cstdint>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
namespace wpi {
@@ -252,7 +254,7 @@ template <typename... Ts> struct DenseMapInfo<std::tuple<Ts...>> {
template <unsigned I>
static unsigned getHashValueImpl(const Tuple &values, std::false_type) {
using EltType = typename std::tuple_element<I, Tuple>::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)),
@@ -271,7 +273,7 @@ template <typename... Ts> struct DenseMapInfo<std::tuple<Ts...>> {
template <unsigned I>
static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs, std::false_type) {
using EltType = typename std::tuple_element<I, Tuple>::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);
@@ -288,6 +290,37 @@ template <typename... Ts> struct DenseMapInfo<std::tuple<Ts...>> {
}
};
// Provide DenseMapInfo for variants whose all alternatives have DenseMapInfo.
template <typename... Ts> struct DenseMapInfo<std::variant<Ts...>> {
using Variant = std::variant<Ts...>;
using FirstT = std::variant_alternative_t<0, Variant>;
static inline Variant getEmptyKey() {
return Variant(std::in_place_index<0>, DenseMapInfo<FirstT>::getEmptyKey());
}
static inline Variant getTombstoneKey() {
return Variant(std::in_place_index<0>,
DenseMapInfo<FirstT>::getTombstoneKey());
}
static unsigned getHashValue(const Variant &Val) {
return std::visit(
[&Val](auto &&Alternative) {
using T = std::decay_t<decltype(Alternative)>;
// Include index in hash to make sure same value as different
// alternatives don't collide.
return detail::combineHashValue(
DenseMapInfo<size_t>::getHashValue(Val.index()),
DenseMapInfo<T>::getHashValue(Alternative));
},
Val);
}
static bool isEqual(const Variant &LHS, const Variant &RHS) {
return LHS == RHS;
}
};
} // end namespace wpi
#endif // WPIUTIL_WPI_DENSEMAPINFO_H

View File

@@ -33,10 +33,10 @@ namespace wpi {
/// is still valid.
///
class DebugEpochBase {
uint64_t Epoch;
uint64_t Epoch = 0;
public:
DebugEpochBase() : Epoch(0) {}
DebugEpochBase() = default;
/// Calling incrementEpoch invalidates all handles pointing into the
/// calling instance.
@@ -55,11 +55,11 @@ public:
/// make an iterator-invalidating modification.
///
class HandleBase {
const uint64_t *EpochAddress;
uint64_t EpochAtCreation;
const uint64_t *EpochAddress = nullptr;
uint64_t EpochAtCreation = UINT64_MAX;
public:
HandleBase() : EpochAddress(nullptr), EpochAtCreation(UINT64_MAX) {}
HandleBase() = default;
explicit HandleBase(const DebugEpochBase *Parent)
: EpochAddress(&Parent->Epoch), EpochAtCreation(Parent->Epoch) {}

View File

@@ -15,7 +15,6 @@
#include <cerrno>
#include <string>
#include <type_traits>
namespace wpi {
namespace sys {

View File

@@ -123,19 +123,34 @@ wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr,
/// Marks that the current location is not supposed to be reachable.
/// In !NDEBUG builds, prints the message and location info to stderr.
/// In NDEBUG builds, becomes an optimizer hint that the current location
/// is not supposed to be reachable. On compilers that don't support
/// such hints, prints a reduced message instead and aborts the program.
/// In NDEBUG builds, if the platform does not support a builtin unreachable
/// then we call an internal LLVM runtime function. Otherwise the behavior is
/// controlled by the CMake flag
/// -DLLVM_UNREACHABLE_OPTIMIZE
/// * When "ON" (default) wpi_unreachable() becomes an optimizer hint
/// that the current location is not supposed to be reachable: the hint
/// turns such code path into undefined behavior. On compilers that don't
/// support such hints, prints a reduced message instead and aborts the
/// program.
/// * When "OFF", a builtin_trap is emitted instead of an
// optimizer hint or printing a reduced message.
///
/// Use this instead of assert(0). It conveys intent more clearly and
/// allows compilers to omit some unnecessary code.
/// Use this instead of assert(0). It conveys intent more clearly, suppresses
/// diagnostics for unreachable code paths, and allows compilers to omit
/// unnecessary code.
#ifndef NDEBUG
#define wpi_unreachable(msg) \
::wpi::wpi_unreachable_internal(msg, __FILE__, __LINE__)
#elif defined(LLVM_BUILTIN_UNREACHABLE)
#elif !defined(LLVM_BUILTIN_UNREACHABLE)
#define wpi_unreachable(msg) ::wpi::wpi_unreachable_internal()
#elif LLVM_UNREACHABLE_OPTIMIZE
#define wpi_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
#else
#define wpi_unreachable(msg) ::wpi::wpi_unreachable_internal()
#define wpi_unreachable(msg) \
do { \
LLVM_BUILTIN_TRAP; \
LLVM_BUILTIN_UNREACHABLE; \
} while (false)
#endif
#endif

View File

@@ -72,7 +72,7 @@ template <typename CallableT, typename ThisT>
using EnableUnlessSameType =
std::enable_if_t<!std::is_same<remove_cvref_t<CallableT>, ThisT>::value>;
template <typename CallableT, typename Ret, typename... Params>
using EnableIfCallable = std::enable_if_t<wpi::disjunction<
using EnableIfCallable = std::enable_if_t<std::disjunction<
std::is_void<Ret>,
std::is_same<decltype(std::declval<CallableT>()(std::declval<Params>()...)),
Ret>,
@@ -106,11 +106,11 @@ protected:
template <typename T> struct AdjustedParamTBase {
static_assert(!std::is_reference<T>::value,
"references should be handled by template specialization");
using type = typename std::conditional<
using type = std::conditional_t<
wpi::is_trivially_copy_constructible<T>::value &&
wpi::is_trivially_move_constructible<T>::value &&
IsSizeLessThanThresholdT<T>::value,
T, T &>::type;
T, T &>;
};
// This specialization ensures that 'AdjustedParam<V<T>&>' or
@@ -167,9 +167,8 @@ protected:
// provide four pointers worth of storage here.
// This is mutable as an inlined `const unique_function<void() const>` may
// still modify its own mutable members.
mutable
typename std::aligned_storage<InlineStorageSize, alignof(void *)>::type
InlineStorage;
mutable std::aligned_storage_t<InlineStorageSize, alignof(void *)>
InlineStorage;
} StorageUnion;
// A compressed pointer to either our dispatching callback or our table of

View File

@@ -50,6 +50,7 @@
#include <algorithm>
#include <cassert>
#include <cstring>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
@@ -126,6 +127,8 @@ hash_code hash_value(const std::tuple<Ts...> &arg);
template <typename T>
hash_code hash_value(const std::basic_string<T> &arg);
/// Compute a hash_code for a standard string.
template <typename T> hash_code hash_value(const std::optional<T> &arg);
/// Override the execution seed with a fixed value.
///
@@ -655,24 +658,8 @@ hash_code hash_value(const std::pair<T, U> &arg) {
return hash_combine(arg.first, arg.second);
}
// Implementation details for the hash_value overload for std::tuple<...>(...).
namespace hashing {
namespace detail {
template <typename... Ts, std::size_t... Indices>
hash_code hash_value_tuple_helper(const std::tuple<Ts...> &arg,
std::index_sequence<Indices...>) {
return hash_combine(std::get<Indices>(arg)...);
}
} // namespace detail
} // namespace hashing
template <typename... Ts>
hash_code hash_value(const std::tuple<Ts...> &arg) {
// TODO: Use std::apply when LLVM starts using C++17.
return ::wpi::hashing::detail::hash_value_tuple_helper(
arg, typename std::index_sequence_for<Ts...>());
template <typename... Ts> hash_code hash_value(const std::tuple<Ts...> &arg) {
return std::apply([](const auto &...xs) { return hash_combine(xs...); }, arg);
}
// Declared and documented above, but defined here so that any of the hashing
@@ -682,6 +669,10 @@ hash_code hash_value(const std::basic_string<T> &arg) {
return hash_combine_range(arg.begin(), arg.end());
}
template <typename T> hash_code hash_value(const std::optional<T> &arg) {
return arg ? hash_combine(true, *arg) : hash_value(false);
}
template <> struct DenseMapInfo<hash_code, void> {
static inline hash_code getEmptyKey() { return hash_code(-1); }
static inline hash_code getTombstoneKey() { return hash_code(-2); }

View File

@@ -39,7 +39,7 @@ class MapVector {
VectorType Vector;
static_assert(
std::is_integral<typename MapType::mapped_type>::value,
std::is_integral_v<typename MapType::mapped_type>,
"The mapped_type of the specified Map must be an integral type");
public:
@@ -109,7 +109,7 @@ public:
// Returns a copy of the value. Only allowed if ValueT is copyable.
ValueT lookup(const KeyT &Key) const {
static_assert(std::is_copy_constructible<ValueT>::value,
static_assert(std::is_copy_constructible_v<ValueT>,
"Cannot call lookup() if ValueT is not copyable.");
typename MapType::const_iterator Pos = Map.find(Key);
return Pos == Map.end()? ValueT() : Vector[Pos->second].second;

View File

@@ -13,31 +13,16 @@
#ifndef WPIUTIL_WPI_MATHEXTRAS_H
#define WPIUTIL_WPI_MATHEXTRAS_H
#include "wpi/bit.h"
#include "wpi/Compiler.h"
#include <bit>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <limits>
#include <type_traits>
#ifdef __ANDROID_NDK__
#include <android/api-level.h>
#endif
#ifdef _MSC_VER
// Declare these intrinsics manually rather including intrin.h. It's very
// expensive, and MathExtras.h is popular.
// #include <intrin.h>
extern "C" {
unsigned char _BitScanForward(unsigned long *_Index, unsigned long _Mask);
unsigned char _BitScanForward64(unsigned long *_Index, unsigned __int64 _Mask);
unsigned char _BitScanReverse(unsigned long *_Index, unsigned long _Mask);
unsigned char _BitScanReverse64(unsigned long *_Index, unsigned __int64 _Mask);
}
#endif
namespace wpi {
/// The behavior an operation has on an input of 0.
@@ -45,152 +30,31 @@ enum ZeroBehavior {
/// The returned value is undefined.
ZB_Undefined,
/// The returned value is numeric_limits<T>::max()
ZB_Max,
/// The returned value is numeric_limits<T>::digits
ZB_Width
ZB_Max
};
namespace detail {
template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
static unsigned count(T Val, ZeroBehavior) {
if (!Val)
return std::numeric_limits<T>::digits;
if (Val & 0x1)
return 0;
// Bisection method.
unsigned ZeroBits = 0;
T Shift = std::numeric_limits<T>::digits >> 1;
T Mask = (std::numeric_limits<T>::max)() >> Shift;
while (Shift) {
if ((Val & Mask) == 0) {
Val >>= Shift;
ZeroBits |= Shift;
}
Shift >>= 1;
Mask >>= Shift;
}
return ZeroBits;
}
};
#if defined(__GNUC__) || defined(_MSC_VER)
template <typename T> struct TrailingZerosCounter<T, 4> {
static unsigned count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 32;
#if __has_builtin(__builtin_ctz) || defined(__GNUC__)
return __builtin_ctz(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanForward(&Index, Val);
return Index;
#endif
}
};
#if !defined(_MSC_VER) || defined(_M_X64)
template <typename T> struct TrailingZerosCounter<T, 8> {
static unsigned count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 64;
#if __has_builtin(__builtin_ctzll) || defined(__GNUC__)
return __builtin_ctzll(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanForward64(&Index, Val);
return Index;
#endif
}
};
#endif
#endif
} // namespace detail
/// Count number of 0's from the least significant bit to the most
/// stopping at the first 1.
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
/// valid arguments.
template <typename T>
unsigned countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
/// Returns std::numeric_limits<T>::digits on an input of 0.
template <typename T> unsigned countTrailingZeros(T Val) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return wpi::detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB);
return std::countr_zero(Val);
}
namespace detail {
template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter {
static unsigned count(T Val, ZeroBehavior) {
if (!Val)
return std::numeric_limits<T>::digits;
// Bisection method.
unsigned ZeroBits = 0;
for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
T Tmp = Val >> Shift;
if (Tmp)
Val = Tmp;
else
ZeroBits |= Shift;
}
return ZeroBits;
}
};
#if defined(__GNUC__) || defined(_MSC_VER)
template <typename T> struct LeadingZerosCounter<T, 4> {
static unsigned count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 32;
#if __has_builtin(__builtin_clz) || defined(__GNUC__)
return __builtin_clz(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanReverse(&Index, Val);
return Index ^ 31;
#endif
}
};
#if !defined(_MSC_VER) || defined(_M_X64)
template <typename T> struct LeadingZerosCounter<T, 8> {
static unsigned count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 64;
#if __has_builtin(__builtin_clzll) || defined(__GNUC__)
return __builtin_clzll(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanReverse64(&Index, Val);
return Index ^ 63;
#endif
}
};
#endif
#endif
} // namespace detail
/// Count number of 0's from the most significant bit to the least
/// stopping at the first 1.
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
/// valid arguments.
template <typename T>
unsigned countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
/// Returns std::numeric_limits<T>::digits on an input of 0.
template <typename T> unsigned countLeadingZeros(T Val) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return wpi::detail::LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB);
return std::countl_zero(Val);
}
/// Get the index of the first set bit starting from the least
@@ -198,13 +62,12 @@ unsigned countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
/// valid arguments.
/// \param ZB the behavior on an input of 0.
template <typename T> T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
if (ZB == ZB_Max && Val == 0)
return (std::numeric_limits<T>::max)();
return countTrailingZeros(Val, ZB_Undefined);
return std::countr_zero(Val);
}
/// Create a bitmask with the N right-most bits set to 1, and all other
@@ -239,16 +102,14 @@ template <typename T> T maskLeadingZeros(unsigned N) {
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
/// valid arguments.
/// \param ZB the behavior on an input of 0.
template <typename T> T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) {
if (ZB == ZB_Max && Val == 0)
return (std::numeric_limits<T>::max)();
// Use ^ instead of - because both gcc and llvm can remove the associated ^
// in the __builtin_clz intrinsic on x86.
return countLeadingZeros(Val, ZB_Undefined) ^
(std::numeric_limits<T>::digits - 1);
return std::countl_zero(Val) ^ (std::numeric_limits<T>::digits - 1);
}
/// Macro compressed bit reversal table for 256 bits.
@@ -265,8 +126,24 @@ static const unsigned char BitReverseTable256[256] = {
};
/// Reverse the bits in \p Val.
template <typename T>
T reverseBits(T Val) {
template <typename T> T reverseBits(T Val) {
#if __has_builtin(__builtin_bitreverse8)
if constexpr (std::is_same_v<T, uint8_t>)
return __builtin_bitreverse8(Val);
#endif
#if __has_builtin(__builtin_bitreverse16)
if constexpr (std::is_same_v<T, uint16_t>)
return __builtin_bitreverse16(Val);
#endif
#if __has_builtin(__builtin_bitreverse32)
if constexpr (std::is_same_v<T, uint32_t>)
return __builtin_bitreverse32(Val);
#endif
#if __has_builtin(__builtin_bitreverse64)
if constexpr (std::is_same_v<T, uint64_t>)
return __builtin_bitreverse64(Val);
#endif
unsigned char in[sizeof(Val)];
unsigned char out[sizeof(Val)];
std::memcpy(in, &Val, sizeof(Val));
@@ -276,34 +153,6 @@ T reverseBits(T Val) {
return Val;
}
#if __has_builtin(__builtin_bitreverse8)
template<>
inline uint8_t reverseBits<uint8_t>(uint8_t Val) {
return __builtin_bitreverse8(Val);
}
#endif
#if __has_builtin(__builtin_bitreverse16)
template<>
inline uint16_t reverseBits<uint16_t>(uint16_t Val) {
return __builtin_bitreverse16(Val);
}
#endif
#if __has_builtin(__builtin_bitreverse32)
template<>
inline uint32_t reverseBits<uint32_t>(uint32_t Val) {
return __builtin_bitreverse32(Val);
}
#endif
#if __has_builtin(__builtin_bitreverse64)
template<>
inline uint64_t reverseBits<uint64_t>(uint64_t Val) {
return __builtin_bitreverse64(Val);
}
#endif
// NOTE: The following support functions use the _32/_64 extensions instead of
// type overloading so that signed and unsigned integers can be used without
// ambiguity.
@@ -325,17 +174,16 @@ constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) {
/// Checks if an integer fits into the given bit width.
template <unsigned N> constexpr inline bool isInt(int64_t x) {
return N >= 64 || (-(INT64_C(1)<<(N-1)) <= x && x < (INT64_C(1)<<(N-1)));
}
// Template specializations to get better code for common cases.
template <> constexpr inline bool isInt<8>(int64_t x) {
return static_cast<int8_t>(x) == x;
}
template <> constexpr inline bool isInt<16>(int64_t x) {
return static_cast<int16_t>(x) == x;
}
template <> constexpr inline bool isInt<32>(int64_t x) {
return static_cast<int32_t>(x) == x;
if constexpr (N == 8)
return static_cast<int8_t>(x) == x;
if constexpr (N == 16)
return static_cast<int16_t>(x) == x;
if constexpr (N == 32)
return static_cast<int32_t>(x) == x;
if constexpr (N < 64)
return -(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1));
(void)x; // MSVC v19.25 warns that x is unused.
return true;
}
/// Checks if a signed integer is an N bit number shifted left by S.
@@ -348,34 +196,20 @@ constexpr inline bool isShiftedInt(int64_t x) {
}
/// Checks if an unsigned integer fits into the given bit width.
///
/// This is written as two functions rather than as simply
///
/// return N >= 64 || X < (UINT64_C(1) << N);
///
/// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting
/// left too many places.
template <unsigned N>
constexpr inline std::enable_if_t<(N < 64), bool> isUInt(uint64_t X) {
template <unsigned N> constexpr inline bool isUInt(uint64_t x) {
static_assert(N > 0, "isUInt<0> doesn't make sense");
return X < (UINT64_C(1) << (N));
}
template <unsigned N>
constexpr inline std::enable_if_t<N >= 64, bool> isUInt(uint64_t) {
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));
(void)x; // MSVC v19.25 warns that x is unused.
return true;
}
// Template specializations to get better code for common cases.
template <> constexpr inline bool isUInt<8>(uint64_t x) {
return static_cast<uint8_t>(x) == x;
}
template <> constexpr inline bool isUInt<16>(uint64_t x) {
return static_cast<uint16_t>(x) == x;
}
template <> constexpr inline bool isUInt<32>(uint64_t x) {
return static_cast<uint32_t>(x) == x;
}
/// Checks if a unsigned integer is an N bit number shifted left by S.
template <unsigned N, unsigned S>
constexpr inline bool isShiftedUInt(uint64_t x) {
@@ -462,12 +296,12 @@ constexpr inline bool isShiftedMask_64(uint64_t Value) {
/// Return true if the argument is a power of two > 0.
/// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
constexpr inline bool isPowerOf2_32(uint32_t Value) {
return Value && !(Value & (Value - 1));
return std::has_single_bit(Value);
}
/// Return true if the argument is a power of two > 0 (64 bit edition.)
constexpr inline bool isPowerOf2_64(uint64_t Value) {
return Value && !(Value & (Value - 1));
return std::has_single_bit(Value);
}
/// Count the number of ones from the most significant bit to the first
@@ -476,14 +310,11 @@ constexpr inline bool isPowerOf2_64(uint64_t Value) {
/// Ex. countLeadingOnes(0xFF0FFF00) == 8.
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
/// ZB_Undefined are valid arguments.
template <typename T>
unsigned countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
/// Returns std::numeric_limits<T>::digits on an input of all ones.
template <typename T> unsigned countLeadingOnes(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return countLeadingZeros<T>(~Value, ZB);
return std::countl_one<T>(Value);
}
/// Count the number of ones from the least significant bit to the first
@@ -492,56 +323,48 @@ unsigned countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
/// Ex. countTrailingOnes(0x00FF00FF) == 8.
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
/// ZB_Undefined are valid arguments.
template <typename T>
unsigned countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
/// Returns std::numeric_limits<T>::digits on an input of all ones.
template <typename T> unsigned countTrailingOnes(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return countTrailingZeros<T>(~Value, ZB);
return std::countr_one<T>(Value);
}
namespace detail {
template <typename T, std::size_t SizeOfT> struct PopulationCounter {
static unsigned count(T Value) {
// Generic version, forward to 32 bits.
static_assert(SizeOfT <= 4, "Not implemented!");
#if defined(__GNUC__)
return __builtin_popcount(Value);
#else
uint32_t v = Value;
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
#endif
}
};
template <typename T> struct PopulationCounter<T, 8> {
static unsigned count(T Value) {
#if defined(__GNUC__)
return __builtin_popcountll(Value);
#else
uint64_t v = Value;
v = v - ((v >> 1) & 0x5555555555555555ULL);
v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
return unsigned((uint64_t)(v * 0x0101010101010101ULL) >> 56);
#endif
}
};
} // namespace detail
/// Count the number of set bits in a value.
/// Ex. countPopulation(0xF000F000) = 8
/// Returns 0 if the word is zero.
template <typename T>
inline unsigned countPopulation(T Value) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return detail::PopulationCounter<T, sizeof(T)>::count(Value);
return (unsigned)std::popcount(Value);
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true.
/// If true, \p MaskIdx will specify the index of the lowest set bit and \p
/// MaskLen is updated to specify the length of the mask, else neither are
/// updated.
inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_32(Value))
return false;
MaskIdx = std::countr_zero(Value);
MaskLen = std::popcount(Value);
return true;
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (64 bit version.) If true, \p MaskIdx will specify the index
/// of the lowest set bit and \p MaskLen is updated to specify the length of the
/// mask, else neither are updated.
inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_64(Value))
return false;
MaskIdx = std::countr_zero(Value);
MaskLen = std::popcount(Value);
return true;
}
/// Compile time Log2.
@@ -554,90 +377,58 @@ template <size_t kValue> constexpr inline size_t CTLog2() {
template <> constexpr inline size_t CTLog2<1>() { return 0; }
/// Return the log base 2 of the specified value.
inline double Log2(double Value) {
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
return __builtin_log(Value) / __builtin_log(2.0);
#else
return std::log2(Value);
#endif
}
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (32 bit edition.)
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
inline unsigned Log2_32(uint32_t Value) {
return static_cast<unsigned>(31 - countLeadingZeros(Value));
return static_cast<unsigned>(31 - std::countl_zero(Value));
}
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64(uint64_t Value) {
return static_cast<unsigned>(63 - countLeadingZeros(Value));
return static_cast<unsigned>(63 - std::countl_zero(Value));
}
/// Return the ceil log base 2 of the specified value, 32 if the value is zero.
/// (32 bit edition).
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
inline unsigned Log2_32_Ceil(uint32_t Value) {
return static_cast<unsigned>(32 - countLeadingZeros(Value - 1));
return static_cast<unsigned>(32 - std::countl_zero(Value - 1));
}
/// Return the ceil log base 2 of the specified value, 64 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64_Ceil(uint64_t Value) {
return static_cast<unsigned>(64 - countLeadingZeros(Value - 1));
}
/// Return the greatest common divisor of the values using Euclid's algorithm.
template <typename T>
inline T greatestCommonDivisor(T A, T B) {
while (B) {
T Tmp = B;
B = A % B;
A = Tmp;
}
return A;
}
inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) {
return greatestCommonDivisor<uint64_t>(A, B);
return static_cast<unsigned>(64 - std::countl_zero(Value - 1));
}
/// This function takes a 64-bit integer and returns the bit equivalent double.
inline double BitsToDouble(uint64_t Bits) {
double D;
static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes");
memcpy(&D, &Bits, sizeof(Bits));
return D;
return wpi::bit_cast<double>(Bits);
}
/// This function takes a 32-bit integer and returns the bit equivalent float.
inline float BitsToFloat(uint32_t Bits) {
float F;
static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes");
memcpy(&F, &Bits, sizeof(Bits));
return F;
return wpi::bit_cast<float>(Bits);
}
/// This function takes a double and returns the bit equivalent 64-bit integer.
/// Note that copying doubles around changes the bits of NaNs on some hosts,
/// notably x86, so this routine cannot be used if these bits are needed.
inline uint64_t DoubleToBits(double Double) {
uint64_t Bits;
static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes");
memcpy(&Bits, &Double, sizeof(Double));
return Bits;
return wpi::bit_cast<uint64_t>(Double);
}
/// This function takes a float and returns the bit equivalent 32-bit integer.
/// Note that copying floats around changes the bits of NaNs on some hosts,
/// notably x86, so this routine cannot be used if these bits are needed.
inline uint32_t FloatToBits(float Float) {
uint32_t Bits;
static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes");
memcpy(&Bits, &Float, sizeof(Float));
return Bits;
return wpi::bit_cast<uint32_t>(Float);
}
/// A and B are either alignments or offsets. Return the minimum alignment that
@@ -653,7 +444,7 @@ constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) {
/// Returns the next power of two (in 64-bits) that is strictly greater than A.
/// Returns zero on overflow.
inline uint64_t NextPowerOf2(uint64_t A) {
constexpr inline uint64_t NextPowerOf2(uint64_t A) {
A |= (A >> 1);
A |= (A >> 2);
A |= (A >> 4);
@@ -666,8 +457,7 @@ inline uint64_t NextPowerOf2(uint64_t A) {
/// Returns the power of two which is less than or equal to the given value.
/// Essentially, it is a floor operation across the domain of powers of two.
inline uint64_t PowerOf2Floor(uint64_t A) {
if (!A) return 0;
return 1ull << (63 - countLeadingZeros(A, ZB_Undefined));
return std::bit_floor(A);
}
/// Returns the power of two which is greater than or equal to the given value.
@@ -681,27 +471,40 @@ inline uint64_t PowerOf2Ceil(uint64_t A) {
/// Returns the next integer (mod 2**64) that is greater than or equal to
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
///
/// If non-zero \p Skew is specified, the return value will be a minimal
/// integer that is greater than or equal to \p Value and equal to
/// \p Align * N + \p Skew for some integer N. If \p Skew is larger than
/// \p Align, its value is adjusted to '\p Skew mod \p Align'.
///
/// Examples:
/// \code
/// alignTo(5, 8) = 8
/// alignTo(17, 8) = 24
/// alignTo(~0LL, 8) = 0
/// alignTo(321, 255) = 510
/// \endcode
inline uint64_t alignTo(uint64_t Value, uint64_t Align) {
assert(Align != 0u && "Align can't be 0.");
return (Value + Align - 1) / Align * Align;
}
inline uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
"Align must be a power of 2");
return (Value + Align - 1) & -Align;
}
/// If non-zero \p Skew is specified, the return value will be a minimal integer
/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
/// Skew mod \p A'. \p Align must be non-zero.
///
/// Examples:
/// \code
/// alignTo(5, 8, 7) = 7
/// alignTo(17, 8, 1) = 17
/// alignTo(~0LL, 8, 3) = 3
/// alignTo(321, 255, 42) = 552
/// \endcode
inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew) {
assert(Align != 0u && "Align can't be 0.");
Skew %= Align;
return (Value + Align - 1 - Skew) / Align * Align + Skew;
return alignTo(Value - Skew, Align) + Skew;
}
/// Returns the next integer (mod 2**64) that is greater than or equal to
@@ -785,6 +588,18 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
return Z;
}
/// Add multiple unsigned integers of type T. Clamp the result to the
/// maximum representable value of T on overflow.
template <class T, class... Ts>
std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
Ts... Args) {
bool Overflowed = false;
T XY = SaturatingAdd(X, Y, &Overflowed);
if (Overflowed)
return SaturatingAdd((std::numeric_limits<T>::max)(), T(1), Args...);
return SaturatingAdd(XY, Z, Args...);
}
/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
@@ -852,7 +667,7 @@ extern const float huge_valf;
/// Add two signed integers, computing the two's complement truncated result,
/// returning true if overflow occured.
/// returning true if overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed<T>::value, T> AddOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_add_overflow)

View File

@@ -61,19 +61,19 @@ public:
IntType getInt() const { return (IntType)Info::getInt(Value); }
void setPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION {
void setPointer(PointerTy PtrVal) & {
Value = Info::updatePointer(Value, PtrVal);
}
void setInt(IntType IntVal) LLVM_LVALUE_FUNCTION {
void setInt(IntType IntVal) & {
Value = Info::updateInt(Value, static_cast<intptr_t>(IntVal));
}
void initWithPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION {
void initWithPointer(PointerTy PtrVal) & {
Value = Info::updatePointer(0, PtrVal);
}
void setPointerAndInt(PointerTy PtrVal, IntType IntVal) LLVM_LVALUE_FUNCTION {
void setPointerAndInt(PointerTy PtrVal, IntType IntVal) & {
Value = Info::updateInt(Info::updatePointer(0, PtrVal),
static_cast<intptr_t>(IntVal));
}
@@ -91,7 +91,7 @@ public:
void *getOpaqueValue() const { return reinterpret_cast<void *>(Value); }
void setFromOpaqueValue(void *Val) LLVM_LVALUE_FUNCTION {
void setFromOpaqueValue(void *Val) & {
Value = reinterpret_cast<intptr_t>(Val);
}
@@ -128,7 +128,6 @@ public:
}
};
template <typename PointerT, unsigned IntBits, typename PtrTraits>
struct PointerIntPairInfo {
static_assert(PtrTraits::NumLowBitsAvailable <
@@ -228,6 +227,32 @@ struct PointerLikeTypeTraits<
PtrTraits::NumLowBitsAvailable - IntBits;
};
// Allow structured bindings on PointerIntPair.
template <std::size_t I, typename PointerTy, unsigned IntBits, typename IntType,
typename PtrTraits, typename Info>
decltype(auto)
get(const PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info> &Pair) {
static_assert(I < 2);
if constexpr (I == 0)
return Pair.getPointer();
else
return Pair.getInt();
}
} // end namespace wpi
namespace std {
template <typename PointerTy, unsigned IntBits, typename IntType,
typename PtrTraits, typename Info>
struct tuple_size<
wpi::PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info>>
: std::integral_constant<std::size_t, 2> {};
template <std::size_t I, typename PointerTy, unsigned IntBits, typename IntType,
typename PtrTraits, typename Info>
struct tuple_element<
I, wpi::PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info>>
: std::conditional<I == 0, PointerTy, IntType> {};
} // namespace std
#endif // WPIUTIL_WPI_POINTERINTPAIR_H

View File

@@ -17,6 +17,7 @@
#include "wpi/DenseMapInfo.h"
#include "wpi/PointerIntPair.h"
#include "wpi/Casting.h"
#include "wpi/PointerLikeTypeTraits.h"
#include <algorithm>
#include <cassert>
@@ -132,6 +133,9 @@ 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.
///
@@ -167,6 +171,11 @@ 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...>;
public:
PointerUnion() = default;
@@ -179,25 +188,24 @@ public:
explicit operator bool() const { return !isNull(); }
// FIXME: Replace the uses of is(), get() and dyn_cast() with
// isa<T>, cast<T> and the wpi::dyn_cast<T>
/// Test if the Union currently holds the type matching T.
template <typename T> bool is() const {
return this->Val.getInt() == FirstIndexOfType<T, PTs...>::value;
}
template <typename T> inline bool is() const { return isa<T>(*this); }
/// Returns the value of the specified pointer type.
///
/// If the specified pointer type is incorrect, assert.
template <typename T> T get() const {
assert(is<T>() && "Invalid accessor called");
return PointerLikeTypeTraits<T>::getFromVoidPointer(this->Val.getPointer());
template <typename T> inline T get() const {
assert(isa<T>(*this) && "Invalid accessor called");
return cast<T>(*this);
}
/// Returns the current pointer if it is of the specified pointer type,
/// otherwise returns null.
template <typename T> T dyn_cast() const {
if (is<T>())
return get<T>();
return T();
template <typename T> inline T dyn_cast() const {
return wpi::dyn_cast_if_present<T>(*this);
}
/// If the union is set to the first pointer type get an address pointing to
@@ -250,6 +258,52 @@ 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);
}
static To doCast(From &f) { return Impl::template doCast<To>(f); }
static inline To castFailed() { return To(); }
};
template <typename To, typename... PTs>
struct CastInfo<To, const PointerUnion<PTs...>>
: public ConstStrippingForwardingCast<To, const PointerUnion<PTs...>,
CastInfo<To, PointerUnion<PTs...>>> {
};
// Teach SmallPtrSet that PointerUnion is "basically a pointer", that has
// # low bits available = min(PT1bits,PT2bits)-1.
template <typename ...PTs>

View File

@@ -17,53 +17,11 @@
#ifndef WPIUTIL_WPI_STLFORWARDCOMPAT_H
#define WPIUTIL_WPI_STLFORWARDCOMPAT_H
#include <optional>
#include <type_traits>
namespace wpi {
//===----------------------------------------------------------------------===//
// Features from C++17
//===----------------------------------------------------------------------===//
template <typename T>
struct negation // NOLINT(readability-identifier-naming)
: std::integral_constant<bool, !bool(T::value)> {};
template <typename...>
struct conjunction // NOLINT(readability-identifier-naming)
: std::true_type {};
template <typename B1> struct conjunction<B1> : B1 {};
template <typename B1, typename... Bn>
struct conjunction<B1, Bn...>
: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
template <typename...>
struct disjunction // NOLINT(readability-identifier-naming)
: std::false_type {};
template <typename B1> struct disjunction<B1> : B1 {};
template <typename B1, typename... Bn>
struct disjunction<B1, Bn...>
: std::conditional<bool(B1::value), B1, disjunction<Bn...>>::type {};
struct in_place_t // NOLINT(readability-identifier-naming)
{
explicit in_place_t() = default;
};
/// \warning This must not be odr-used, as it cannot be made \c inline in C++14.
constexpr in_place_t in_place; // NOLINT(readability-identifier-naming)
template <typename T>
struct in_place_type_t // NOLINT(readability-identifier-naming)
{
explicit in_place_type_t() = default;
};
template <std::size_t I>
struct in_place_index_t // NOLINT(readability-identifier-naming)
{
explicit in_place_index_t() = default;
};
//===----------------------------------------------------------------------===//
// Features from C++20
//===----------------------------------------------------------------------===//
@@ -78,6 +36,30 @@ template <typename T>
using remove_cvref_t // NOLINT(readability-identifier-naming)
= typename wpi::remove_cvref<T>::type;
//===----------------------------------------------------------------------===//
// 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));
return std::nullopt;
}
} // namespace wpi
#endif // WPIUTIL_WPI_STLFORWARDCOMPAT_H

View File

@@ -89,7 +89,7 @@ public:
SmallPtrSetImplBase &operator=(const SmallPtrSetImplBase &) = delete;
LLVM_NODISCARD bool empty() const { return size() == 0; }
[[nodiscard]] bool empty() const { return size() == 0; }
size_type size() const { return NumNonEmpty - NumTombstones; }
void clear() {

View File

@@ -140,6 +140,7 @@ class SmallSet {
std::set<T, C> Set;
using VIterator = typename SmallVector<T, N>::const_iterator;
using SIterator = typename std::set<T, C>::const_iterator;
using mutable_iterator = typename SmallVector<T, N>::iterator;
// In small mode SmallPtrSet uses linear search for the elements, so it is
@@ -153,9 +154,7 @@ public:
SmallSet() = default;
LLVM_NODISCARD bool empty() const {
return Vector.empty() && Set.empty();
}
[[nodiscard]] bool empty() const { return Vector.empty() && Set.empty(); }
size_type size() const {
return isSmall() ? Vector.size() : Set.size();
@@ -172,22 +171,21 @@ public:
}
/// insert - Insert an element into the set if it isn't already there.
/// Returns true if the element is inserted (it was not in the set before).
/// The first value of the returned pair is unused and provided for
/// partial compatibility with the standard library self-associative container
/// concept.
// FIXME: Add iterators that abstract over the small and large form, and then
// return those here.
std::pair<std::nullopt_t, bool> insert(const T &V) {
if (!isSmall())
return std::make_pair(std::nullopt, Set.insert(V).second);
/// Returns a pair. The first value of it is an iterator to the inserted
/// element or the existing element in the set. The second value is true
/// if the element is inserted (it was not in the set before).
std::pair<const_iterator, bool> insert(const T &V) {
if (!isSmall()) {
auto [I, Inserted] = Set.insert(V);
return std::make_pair(const_iterator(I), Inserted);
}
VIterator I = vfind(V);
if (I != Vector.end()) // Don't reinsert if it already exists.
return std::make_pair(std::nullopt, false);
return std::make_pair(const_iterator(I), false);
if (Vector.size() < N) {
Vector.push_back(V);
return std::make_pair(std::nullopt, true);
return std::make_pair(const_iterator(std::prev(Vector.end())), true);
}
// Otherwise, grow from vector to set.
@@ -195,8 +193,7 @@ public:
Set.insert(Vector.back());
Vector.pop_back();
}
Set.insert(V);
return std::make_pair(std::nullopt, true);
return std::make_pair(const_iterator(Set.insert(V).first), true);
}
template <typename IterT>

View File

@@ -35,6 +35,7 @@
#include <limits>
#include <memory>
#include <new>
#include <span>
#include <type_traits>
#include <utility>
@@ -42,6 +43,11 @@ namespace wpi {
template <typename IteratorT> class iterator_range;
template <class Iterator>
using EnableIfConvertibleToInputIterator = std::enable_if_t<std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
std::input_iterator_tag>::value>;
/// This is all the stuff common to all SmallVectors.
///
/// The template parameter specifies the type which should be used to hold the
@@ -67,18 +73,32 @@ 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(size_t MinSize, size_t TSize, size_t &NewCapacity);
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);
/// If vector was first created with capacity 0, getFirstEl() points to the
/// memory right after, an area unallocated. If a subsequent allocation,
/// that grows the vector, happens to return the same pointer as getFirstEl(),
/// get a new allocation, otherwise isSmall() will falsely return that no
/// allocation was done (true) and the memory will not be freed in the
/// destructor. If a VSize is given (vector size), also copy that many
/// elements to the new allocation - used if realloca fails to increase
/// space, and happens to allocate precisely at BeginX.
/// This is unlikely to be called often, but resolves a memory leak when the
/// situation does occur.
void *replaceAllocation(void *NewElts, size_t TSize, size_t NewCapacity,
size_t VSize = 0);
public:
size_t size() const { return Size; }
size_t capacity() const { return Capacity; }
LLVM_NODISCARD bool empty() const { return !Size; }
[[nodiscard]] bool empty() const { return !Size; }
protected:
/// Set the array size to \p N, which the current array must have enough
@@ -99,13 +119,14 @@ template <class T, typename = void> struct SmallVectorAlignmentAndSize {
};
/// This is the part of SmallVectorTemplateBase which does not depend on whether
/// the type T is a POD. The extra dummy template argument is used by ArrayRef
/// the type T is a POD. The extra dummy template argument is used by std::span
/// to avoid unnecessarily requiring T to be complete.
template <typename T, typename = void>
class SmallVectorTemplateCommon
: public SmallVectorBase {
using Base = SmallVectorBase;
protected:
/// Find the address of the first element. For this pointer math to be valid
/// with small-size of 0 for T with lots of alignment, it's important that
/// SmallVectorStorage is properly-aligned even for small-size of 0.
@@ -116,7 +137,6 @@ class SmallVectorTemplateCommon
}
// Space after 'FirstEl' is clobbered, do not add any instance vars after it.
protected:
SmallVectorTemplateCommon(size_t Size) : Base(getFirstEl(), Size) {}
void grow_pod(size_t MinSize, size_t TSize) {
@@ -331,8 +351,7 @@ protected:
/// constructing elements as needed.
template<typename It1, typename It2>
static void uninitialized_move(It1 I, It1 E, It2 Dest) {
std::uninitialized_copy(std::make_move_iterator(I),
std::make_move_iterator(E), Dest);
std::uninitialized_move(I, E, Dest);
}
/// Copy the range [I, E) onto the uninitialized memory starting with "Dest",
@@ -349,11 +368,7 @@ protected:
/// Create a new allocation big enough for \p MinSize and pass back its size
/// in \p NewCapacity. This is the first section of \a grow().
T *mallocForGrow(size_t MinSize, size_t &NewCapacity) {
return static_cast<T *>(
SmallVectorBase::mallocForGrow(
MinSize, sizeof(T), NewCapacity));
}
T *mallocForGrow(size_t MinSize, size_t &NewCapacity);
/// Move existing elements over to the new allocation \p NewElts, the middle
/// section of \a grow().
@@ -427,6 +442,14 @@ void SmallVectorTemplateBase<T, TriviallyCopyable>::grow(size_t MinSize) {
takeAllocationForGrow(NewElts, NewCapacity);
}
template <typename T, bool TriviallyCopyable>
T *SmallVectorTemplateBase<T, TriviallyCopyable>::mallocForGrow(
size_t MinSize, size_t &NewCapacity) {
return static_cast<T *>(
SmallVectorBase::mallocForGrow(
this->getFirstEl(), MinSize, sizeof(T), NewCapacity));
}
// Define this out-of-line to dissuade the C++ compiler from inlining it.
template <typename T, bool TriviallyCopyable>
void SmallVectorTemplateBase<T, TriviallyCopyable>::moveElementsForGrow(
@@ -465,8 +488,7 @@ protected:
/// Either const T& or T, depending on whether it's cheap enough to take
/// parameters by value.
using ValueParamT =
typename std::conditional<TakesParamByValue, T, const T &>::type;
using ValueParamT = std::conditional_t<TakesParamByValue, T, const T &>;
SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon<T>(Size) {}
@@ -494,8 +516,8 @@ protected:
template <typename T1, typename T2>
static void uninitialized_copy(
T1 *I, T1 *E, T2 *Dest,
std::enable_if_t<std::is_same<typename std::remove_const<T1>::type,
T2>::value> * = nullptr) {
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
@@ -654,7 +676,7 @@ public:
truncate(this->size() - NumItems);
}
LLVM_NODISCARD T pop_back_val() {
[[nodiscard]] T pop_back_val() {
T Result = ::std::move(this->back());
this->pop_back();
return Result;
@@ -663,11 +685,8 @@ public:
void swap(SmallVectorImpl &RHS);
/// Add the specified range to the end of the SmallVector.
template <typename in_iter,
typename = std::enable_if_t<std::is_convertible<
typename std::iterator_traits<in_iter>::iterator_category,
std::input_iterator_tag>::value>>
void append(in_iter in_start, in_iter in_end) {
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
void append(ItTy in_start, ItTy in_end) {
this->assertSafeToAddRange(in_start, in_end);
size_type NumInputs = std::distance(in_start, in_end);
this->reserve(this->size() + NumInputs);
@@ -707,11 +726,8 @@ public:
// FIXME: Consider assigning over existing elements, rather than clearing &
// re-initializing them - for all assign(...) variants.
template <typename in_iter,
typename = std::enable_if_t<std::is_convertible<
typename std::iterator_traits<in_iter>::iterator_category,
std::input_iterator_tag>::value>>
void assign(in_iter in_start, in_iter in_end) {
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
void assign(ItTy in_start, ItTy in_end) {
this->assertSafeToReferenceAfterClear(in_start, in_end);
clear();
append(in_start, in_end);
@@ -861,10 +877,7 @@ public:
return I;
}
template <typename ItTy,
typename = std::enable_if_t<std::is_convertible<
typename std::iterator_traits<ItTy>::iterator_category,
std::input_iterator_tag>::value>>
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
iterator insert(iterator I, ItTy From, ItTy To) {
// Convert iterator to elt# to avoid invalidating iterator when we reserve()
size_t InsertElt = I - this->begin();
@@ -952,6 +965,9 @@ public:
return std::lexicographical_compare(this->begin(), this->end(),
RHS.begin(), RHS.end());
}
bool operator>(const SmallVectorImpl &RHS) const { return RHS < *this; }
bool operator<=(const SmallVectorImpl &RHS) const { return !(*this > RHS); }
bool operator>=(const SmallVectorImpl &RHS) const { return !(*this < RHS); }
};
template <typename T>
@@ -1197,10 +1213,7 @@ public:
this->assign(Size, Value);
}
template <typename ItTy,
typename = std::enable_if_t<std::is_convertible<
typename std::iterator_traits<ItTy>::iterator_category,
std::input_iterator_tag>::value>>
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
SmallVector(ItTy S, ItTy E) : SmallVectorImpl<T>(N) {
this->append(S, E);
}
@@ -1212,7 +1225,13 @@ public:
}
SmallVector(std::initializer_list<T> IL) : SmallVectorImpl<T>(N) {
this->assign(IL);
this->append(IL);
}
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
explicit SmallVector(std::span<const U> A) : SmallVectorImpl<T>(N) {
this->append(A.begin(), A.end());
}
SmallVector(const SmallVector &RHS) : SmallVectorImpl<T>(N) {
@@ -1271,8 +1290,8 @@ inline size_t capacity_in_bytes(const SmallVector<T, N> &X) {
template <typename RangeType>
using ValueTypeFromRangeType =
typename std::remove_const<typename std::remove_reference<
decltype(*std::begin(std::declval<RangeType &>()))>::type>::type;
std::remove_const_t<std::remove_reference_t<decltype(*std::begin(
std::declval<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,
@@ -1282,10 +1301,16 @@ SmallVector<ValueTypeFromRangeType<R>, Size> to_vector(R &&Range) {
return {std::begin(Range), std::end(Range)};
}
template <typename R>
SmallVector<ValueTypeFromRangeType<R>,
CalculateSmallVectorDefaultInlinedElements<
ValueTypeFromRangeType<R>>::value>
to_vector(R &&Range) {
SmallVector<ValueTypeFromRangeType<R>> to_vector(R &&Range) {
return {std::begin(Range), std::end(Range)};
}
template <typename Out, unsigned Size, typename R>
SmallVector<Out, Size> to_vector_of(R &&Range) {
return {std::begin(Range), std::end(Range)};
}
template <typename Out, typename R> SmallVector<Out> to_vector_of(R &&Range) {
return {std::begin(Range), std::end(Range)};
}

View File

@@ -111,8 +111,9 @@ public:
/// funky memory allocation and hashing things to make it extremely efficient,
/// storing the string data *after* the value in the map.
template <typename ValueTy, typename AllocatorTy = MallocAllocator>
class StringMap : public StringMapImpl {
AllocatorTy Allocator;
class StringMap : public StringMapImpl,
private detail::AllocatorHolder<AllocatorTy> {
using AllocTy = detail::AllocatorHolder<AllocatorTy>;
public:
using MapEntryTy = StringMapEntry<ValueTy>;
@@ -123,12 +124,11 @@ public:
: StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))) {}
explicit StringMap(AllocatorTy A)
: StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))), Allocator(A) {
}
: StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))), AllocTy(A) {}
StringMap(unsigned InitialSize, AllocatorTy A)
: StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))),
Allocator(A) {}
AllocTy(A) {}
StringMap(std::initializer_list<std::pair<std::string_view, ValueTy>> List)
: StringMapImpl(List.size(), static_cast<unsigned>(sizeof(MapEntryTy))) {
@@ -136,11 +136,11 @@ public:
}
StringMap(StringMap &&RHS)
: StringMapImpl(std::move(RHS)), Allocator(std::move(RHS.Allocator)) {}
: StringMapImpl(std::move(RHS)), AllocTy(std::move(RHS.getAllocator())) {}
StringMap(const StringMap &RHS)
: StringMapImpl(static_cast<unsigned>(sizeof(MapEntryTy))),
Allocator(RHS.Allocator) {
AllocTy(RHS.getAllocator()) {
if (RHS.empty())
return;
@@ -159,8 +159,8 @@ public:
continue;
}
TheTable[I] = MapEntryTy::Create(
static_cast<MapEntryTy *>(Bucket)->getKey(), Allocator,
TheTable[I] = MapEntryTy::create(
static_cast<MapEntryTy *>(Bucket)->getKey(), getAllocator(),
static_cast<MapEntryTy *>(Bucket)->getValue());
HashTable[I] = RHSHashTable[I];
}
@@ -175,7 +175,7 @@ public:
StringMap &operator=(StringMap RHS) {
StringMapImpl::swap(RHS);
std::swap(Allocator, RHS.Allocator);
std::swap(getAllocator(), RHS.getAllocator());
return *this;
}
@@ -187,15 +187,14 @@ public:
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *Bucket = TheTable[I];
if (Bucket && Bucket != getTombstoneVal()) {
static_cast<MapEntryTy *>(Bucket)->Destroy(Allocator);
static_cast<MapEntryTy *>(Bucket)->Destroy(getAllocator());
}
}
}
free(TheTable);
}
AllocatorTy &getAllocator() { return Allocator; }
const AllocatorTy &getAllocator() const { return Allocator; }
using AllocTy::getAllocator;
using key_type = const char *;
using mapped_type = ValueTy;
@@ -331,7 +330,7 @@ public:
/// if and only if the insertion takes place, and the iterator component of
/// the pair points to the element with key equivalent to the key of the pair.
template <typename... ArgsTy>
std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&... Args) {
std::pair<iterator, bool> try_emplace(std::string_view Key, ArgsTy &&...Args) {
unsigned BucketNo = LookupBucketFor(Key);
StringMapEntryBase *&Bucket = TheTable[BucketNo];
if (Bucket && Bucket != getTombstoneVal())
@@ -340,7 +339,8 @@ public:
if (Bucket == getTombstoneVal())
--NumTombstones;
Bucket = MapEntryTy::Create(Key, Allocator, std::forward<ArgsTy>(Args)...);
Bucket =
MapEntryTy::create(Key, getAllocator(), std::forward<ArgsTy>(Args)...);
++NumItems;
assert(NumItems + NumTombstones <= NumBuckets);
@@ -358,7 +358,7 @@ public:
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *&Bucket = TheTable[I];
if (Bucket && Bucket != getTombstoneVal()) {
static_cast<MapEntryTy *>(Bucket)->Destroy(Allocator);
static_cast<MapEntryTy *>(Bucket)->Destroy(getAllocator());
}
Bucket = nullptr;
}
@@ -374,7 +374,7 @@ public:
void erase(iterator I) {
MapEntryTy &V = *I;
remove(&V);
V.Destroy(Allocator);
V.Destroy(getAllocator());
}
bool erase(std::string_view Key) {

View File

@@ -77,7 +77,7 @@ public:
explicit StringMapEntryStorage(size_t keyLength)
: StringMapEntryBase(keyLength), second() {}
template <typename... InitTy>
StringMapEntryStorage(size_t keyLength, InitTy &&... initVals)
StringMapEntryStorage(size_t keyLength, InitTy &&...initVals)
: StringMapEntryBase(keyLength),
second(std::forward<InitTy>(initVals)...) {}
StringMapEntryStorage(StringMapEntryStorage &e) = delete;
@@ -88,9 +88,11 @@ public:
void setValue(const ValueTy &V) { second = V; }
};
template <> class StringMapEntryStorage<std::nullopt_t> : public StringMapEntryBase {
template <>
class StringMapEntryStorage<std::nullopt_t> : public StringMapEntryBase {
public:
explicit StringMapEntryStorage(size_t keyLength, std::nullopt_t = std::nullopt)
explicit StringMapEntryStorage(size_t keyLength,
std::nullopt_t = std::nullopt)
: StringMapEntryBase(keyLength) {}
StringMapEntryStorage(StringMapEntryStorage &entry) = delete;
@@ -105,6 +107,8 @@ class StringMapEntry final : public StringMapEntryStorage<ValueTy> {
public:
using StringMapEntryStorage<ValueTy>::StringMapEntryStorage;
using ValueType = ValueTy;
std::string_view getKey() const {
return std::string_view(getKeyData(), this->getKeyLength());
}
@@ -123,7 +127,7 @@ public:
/// Create a StringMapEntry for the specified key construct the value using
/// \p InitiVals.
template <typename AllocatorTy, typename... InitTy>
static StringMapEntry *Create(std::string_view key, AllocatorTy &allocator,
static StringMapEntry *create(std::string_view key, AllocatorTy &allocator,
InitTy &&... initVals) {
return new (StringMapEntryBase::allocateWithKey(
sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator))
@@ -148,6 +152,26 @@ public:
}
};
// Allow structured bindings on StringMapEntry.
template <std::size_t Index, typename ValueTy>
decltype(auto) get(const StringMapEntry<ValueTy> &E) {
static_assert(Index < 2);
if constexpr (Index == 0)
return E.first();
else
return E.second;
}
} // end namespace wpi
namespace std {
template <typename ValueTy>
struct tuple_size<wpi::StringMapEntry<ValueTy>>
: std::integral_constant<std::size_t, 2> {};
template <std::size_t I, typename ValueTy>
struct tuple_element<I, wpi::StringMapEntry<ValueTy>>
: std::conditional<I == 0, std::string_view, ValueTy> {};
} // namespace std
#endif // WPIUTIL_WPI_STRINGMAPENTRY_H

View File

@@ -14,12 +14,10 @@
#ifndef WPIUTIL_WPI_SWAPBYTEORDER_H
#define WPIUTIL_WPI_SWAPBYTEORDER_H
#include "wpi/bit.h"
#include <cstddef>
#include <cstdint>
#include <type_traits>
#if defined(_MSC_VER) && !defined(_DEBUG)
#include <stdlib.h>
#endif
#if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) || \
defined(__Fuchsia__) || defined(__EMSCRIPTEN__)
@@ -50,45 +48,13 @@ namespace wpi {
/// ByteSwap_16 - This function returns a byte-swapped representation of
/// the 16-bit argument.
inline uint16_t ByteSwap_16(uint16_t value) {
#if defined(_MSC_VER) && !defined(_DEBUG)
// The DLL version of the runtime lacks these functions (bug!?), but in a
// release build they're replaced with BSWAP instructions anyway.
return _byteswap_ushort(value);
#else
uint16_t Hi = value << 8;
uint16_t Lo = value >> 8;
return Hi | Lo;
#endif
}
inline uint16_t ByteSwap_16(uint16_t value) { return wpi::byteswap(value); }
/// This function returns a byte-swapped representation of the 32-bit argument.
inline uint32_t ByteSwap_32(uint32_t value) {
#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC))
return __builtin_bswap32(value);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_ulong(value);
#else
uint32_t Byte0 = value & 0x000000FF;
uint32_t Byte1 = value & 0x0000FF00;
uint32_t Byte2 = value & 0x00FF0000;
uint32_t Byte3 = value & 0xFF000000;
return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
#endif
}
inline uint32_t ByteSwap_32(uint32_t value) { return wpi::byteswap(value); }
/// This function returns a byte-swapped representation of the 64-bit argument.
inline uint64_t ByteSwap_64(uint64_t value) {
#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC))
return __builtin_bswap64(value);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_uint64(value);
#else
uint64_t Hi = ByteSwap_32(uint32_t(value));
uint32_t Lo = ByteSwap_32(uint32_t(value >> 32));
return (Hi << 32) | Lo;
#endif
}
inline uint64_t ByteSwap_64(uint64_t value) { return wpi::byteswap(value); }
namespace sys {
@@ -100,33 +66,21 @@ constexpr bool IsBigEndianHost = false;
static const bool IsLittleEndianHost = !IsBigEndianHost;
inline unsigned char getSwappedBytes(unsigned char C) { return C; }
inline signed char getSwappedBytes(signed char C) { return C; }
inline char getSwappedBytes(char C) { return C; }
inline unsigned char getSwappedBytes(unsigned char C) { return wpi::byteswap(C); }
inline signed char getSwappedBytes( signed char C) { return wpi::byteswap(C); }
inline char getSwappedBytes( char C) { return wpi::byteswap(C); }
inline unsigned short getSwappedBytes(unsigned short C) { return ByteSwap_16(C); }
inline signed short getSwappedBytes( signed short C) { return ByteSwap_16(C); }
inline unsigned short getSwappedBytes(unsigned short C) { return wpi::byteswap(C); }
inline signed short getSwappedBytes( signed short C) { return wpi::byteswap(C); }
inline unsigned int getSwappedBytes(unsigned int C) { return ByteSwap_32(C); }
inline signed int getSwappedBytes( signed int C) { return ByteSwap_32(C); }
inline unsigned int getSwappedBytes(unsigned int C) { return wpi::byteswap(C); }
inline signed int getSwappedBytes( signed int C) { return wpi::byteswap(C); }
inline unsigned long getSwappedBytes(unsigned long C) {
// Handle LLP64 and LP64 platforms.
return sizeof(long) == sizeof(int) ? ByteSwap_32((uint32_t)C)
: ByteSwap_64((uint64_t)C);
}
inline signed long getSwappedBytes(signed long C) {
// Handle LLP64 and LP64 platforms.
return sizeof(long) == sizeof(int) ? ByteSwap_32((uint32_t)C)
: ByteSwap_64((uint64_t)C);
}
inline unsigned long getSwappedBytes(unsigned long C) { return wpi::byteswap(C); }
inline signed long getSwappedBytes( signed long C) { return wpi::byteswap(C); }
inline unsigned long long getSwappedBytes(unsigned long long C) {
return ByteSwap_64(C);
}
inline signed long long getSwappedBytes(signed long long C) {
return ByteSwap_64(C);
}
inline unsigned long long getSwappedBytes(unsigned long long C) { return wpi::byteswap(C); }
inline signed long long getSwappedBytes( signed long long C) { return wpi::byteswap(C); }
inline float getSwappedBytes(float C) {
union {
@@ -134,7 +88,7 @@ inline float getSwappedBytes(float C) {
float f;
} in, out;
in.f = C;
out.i = ByteSwap_32(in.i);
out.i = wpi::byteswap(in.i);
return out.f;
}
@@ -144,14 +98,14 @@ inline double getSwappedBytes(double C) {
double d;
} in, out;
in.d = C;
out.i = ByteSwap_64(in.i);
out.i = wpi::byteswap(in.i);
return out.d;
}
template <typename T>
inline std::enable_if_t<std::is_enum<T>::value, T> getSwappedBytes(T C) {
return static_cast<T>(
getSwappedBytes(static_cast<std::underlying_type_t<T>>(C)));
wpi::byteswap(static_cast<std::underlying_type_t<T>>(C)));
}
template<typename T>

View File

@@ -16,11 +16,14 @@
#include "wpi/DenseMapInfo.h"
#include "wpi/Hashing.h"
#include "wpi/Endian.h"
#include <optional>
#include <string>
#include <tuple>
namespace wpi {
template <typename HasherT, support::endianness Endianness>
class HashBuilderImpl;
class raw_ostream;
/// Represents a version number in the form major[.minor[.subminor[.build]]].
@@ -37,24 +40,25 @@ class VersionTuple {
unsigned HasBuild : 1;
public:
VersionTuple()
constexpr VersionTuple()
: Major(0), Minor(0), HasMinor(false), Subminor(0), HasSubminor(false),
Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major)
explicit constexpr VersionTuple(unsigned Major)
: Major(Major), Minor(0), HasMinor(false), Subminor(0),
HasSubminor(false), Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major, unsigned Minor)
explicit constexpr VersionTuple(unsigned Major, unsigned Minor)
: Major(Major), Minor(Minor), HasMinor(true), Subminor(0),
HasSubminor(false), Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor)
explicit constexpr VersionTuple(unsigned Major, unsigned Minor,
unsigned Subminor)
: Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
HasSubminor(true), Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor,
unsigned Build)
explicit constexpr VersionTuple(unsigned Major, unsigned Minor,
unsigned Subminor, unsigned Build)
: Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
HasSubminor(true), Build(Build), HasBuild(true) {}
@@ -95,6 +99,12 @@ public:
return *this;
}
/// 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);
}
/// Return a version tuple that contains only components that are non-zero.
VersionTuple normalize() const {
VersionTuple Result = *this;

View File

@@ -0,0 +1,99 @@
//===-- llvm/ADT/bit.h - C++20 <bit> ----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements the C++20 <bit> header.
///
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_BIT_H
#define WPIUTIL_WPI_BIT_H
#include "wpi/Compiler.h"
#include <cstdint>
#include <limits>
#include <type_traits>
#if !__has_builtin(__builtin_bit_cast)
#include <cstring>
#endif
#if defined(_MSC_VER) && !defined(_DEBUG)
#include <cstdlib> // for _byteswap_{ushort,ulong,uint64}
#endif
namespace wpi {
// This implementation of bit_cast is different from the C++20 one in two ways:
// - It isn't constexpr because that requires compiler support.
// - It requires trivially-constructible To, to avoid UB in the implementation.
template <
typename To, typename From,
typename = std::enable_if_t<sizeof(To) == sizeof(From)>,
typename = std::enable_if_t<std::is_trivially_constructible<To>::value>,
typename = std::enable_if_t<std::is_trivially_copyable<To>::value>,
typename = std::enable_if_t<std::is_trivially_copyable<From>::value>>
[[nodiscard]] inline To bit_cast(const From &from) noexcept {
#if __has_builtin(__builtin_bit_cast)
return __builtin_bit_cast(To, from);
#else
To to;
std::memcpy(&to, &from, sizeof(To));
return to;
#endif
}
/// Reverses the bytes in the given integer value V.
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
[[nodiscard]] constexpr T byteswap(T V) noexcept {
if constexpr (sizeof(T) == 1) {
return V;
} else if constexpr (sizeof(T) == 2) {
uint16_t UV = V;
#if defined(_MSC_VER) && !defined(_DEBUG)
// The DLL version of the runtime lacks these functions (bug!?), but in a
// release build they're replaced with BSWAP instructions anyway.
return _byteswap_ushort(UV);
#else
uint16_t Hi = UV << 8;
uint16_t Lo = UV >> 8;
return Hi | Lo;
#endif
} else if constexpr (sizeof(T) == 4) {
uint32_t UV = V;
#if __has_builtin(__builtin_bswap32)
return __builtin_bswap32(UV);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_ulong(UV);
#else
uint32_t Byte0 = UV & 0x000000FF;
uint32_t Byte1 = UV & 0x0000FF00;
uint32_t Byte2 = UV & 0x00FF0000;
uint32_t Byte3 = UV & 0xFF000000;
return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
#endif
} else if constexpr (sizeof(T) == 8) {
uint64_t UV = V;
#if __has_builtin(__builtin_bswap64)
return __builtin_bswap64(UV);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_uint64(UV);
#else
uint64_t Hi = wpi::byteswap<uint32_t>(UV);
uint32_t Lo = wpi::byteswap<uint32_t>(UV >> 32);
return (Hi << 32) | Lo;
#endif
} else {
static_assert(!sizeof(T *), "Don't know how to handle the given type.");
return 0;
}
}
} // namespace wpi
#endif

View File

@@ -14,15 +14,14 @@
#define WPIUTIL_WPI_RAW_OSTREAM_H
#include "wpi/SmallVector.h"
#include <span>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <optional>
#include <span>
#include <string>
#if __cplusplus > 201402L
#include <string_view>
#endif
#include <system_error>
#include <type_traits>
#include <vector>
@@ -229,6 +228,20 @@ public:
return *this;
}
#if defined(__cpp_char8_t)
// When using `char8_t *` integers or pointers are written to the ostream
// instead of UTF-8 code as one might expect. This might lead to unexpected
// behavior, especially as `u8""` literals are of type `char8_t*` instead of
// type `char_t*` from C++20 onwards. Thus we disallow using them with
// raw_ostreams.
// If you have u8"" literals to stream, you can rewrite them as ordinary
// literals with escape sequences
// e.g. replace `u8"\u00a0"` by `"\xc2\xa0"`
// or use `reinterpret_cast`:
// e.g. replace `u8"\u00a0"` by `reinterpret_cast<const char *>(u8"\u00a0")`
raw_ostream &operator<<(const char8_t *Str) = delete;
#endif
raw_ostream &operator<<(const char *Str) {
// Inline fast path, particularly for constant strings where a sufficiently
// smart compiler will simplify strlen.
@@ -734,7 +747,7 @@ class buffer_ostream : public raw_svector_ostream {
raw_ostream &OS;
SmallVector<char, 0> Buffer;
virtual void anchor() override;
void anchor() override;
public:
buffer_ostream(raw_ostream &OS) : raw_svector_ostream(Buffer), OS(OS) {}
@@ -745,7 +758,7 @@ class buffer_unique_ostream : public raw_svector_ostream {
std::unique_ptr<raw_ostream> OS;
SmallVector<char, 0> Buffer;
virtual void anchor() override;
void anchor() override;
public:
buffer_unique_ostream(std::unique_ptr<raw_ostream> OS)

View File

@@ -98,7 +98,6 @@ using is_trivially_move_constructible = std::is_trivially_move_constructible<T>;
template <typename T>
using is_trivially_copy_constructible = std::is_trivially_copy_constructible<T>;
} // end namespace wpi
#endif // WPIUTIL_WPI_TYPE_TRAITS_H