src: yet more constexpr updates

This commit is contained in:
Markus F.X.J. Oberhumer 2024-05-27 07:03:03 +02:00
parent f65c8f8c6f
commit 9f4d18baac
3 changed files with 166 additions and 40 deletions

View File

@ -180,6 +180,10 @@ struct CheckIntegral {
assert_noexcept(x[0] == 0 && x[1] == 0);
assert_noexcept(y[0] == 0 && y[1] == 0);
assert_noexcept(z[0] == 0 && z[1] == 0);
#if defined(upx_is_constant_evaluated)
static_assert(c == 0);
static_assert(z[0] == 0 && z[1] == 0);
#endif
}
{
TestU<U> t;
@ -191,18 +195,18 @@ struct CheckIntegral {
assert_noexcept(t.z[0] == 0 && t.z[1] == 0);
}
#if __cplusplus <= 201703L
COMPILE_TIME_ASSERT(std::is_pod<U>::value) // std::is_pod is deprecated in C++20
static_assert(std::is_pod<U>::value); // std::is_pod is deprecated in C++20
#endif
COMPILE_TIME_ASSERT(std::is_standard_layout<U>::value)
COMPILE_TIME_ASSERT(std::is_trivial<U>::value)
static_assert(std::is_standard_layout<U>::value);
static_assert(std::is_trivial<U>::value);
// more checks, these are probably implied by std::is_trivial
COMPILE_TIME_ASSERT(std::is_nothrow_default_constructible<U>::value)
COMPILE_TIME_ASSERT(std::is_nothrow_destructible<U>::value)
COMPILE_TIME_ASSERT(std::is_trivially_copyable<U>::value)
COMPILE_TIME_ASSERT(std::is_trivially_default_constructible<U>::value)
static_assert(std::is_nothrow_default_constructible<U>::value);
static_assert(std::is_nothrow_destructible<U>::value);
static_assert(std::is_trivially_copyable<U>::value);
static_assert(std::is_trivially_default_constructible<U>::value);
// UPX extras
COMPILE_TIME_ASSERT(upx_is_integral<U>::value)
COMPILE_TIME_ASSERT(upx_is_integral_v<U>)
static_assert(upx_is_integral<U>::value);
static_assert(upx_is_integral_v<U>);
}
static void check(void) noexcept {
{
@ -286,10 +290,10 @@ struct CheckAlignment {
COMPILE_TIME_ASSERT_ALIGNED1(Test2)
Test1 t1[7];
Test2 t2[7];
COMPILE_TIME_ASSERT(sizeof(Test1) == 1 + sizeof(T))
COMPILE_TIME_ASSERT(sizeof(t1) == 7 + 7 * sizeof(T))
COMPILE_TIME_ASSERT(sizeof(Test2) == 1 + 3 * sizeof(T))
COMPILE_TIME_ASSERT(sizeof(t2) == 7 + 21 * sizeof(T))
static_assert(sizeof(Test1) == 1 + sizeof(T));
static_assert(sizeof(t1) == 7 + 7 * sizeof(T));
static_assert(sizeof(Test2) == 1 + 3 * sizeof(T));
static_assert(sizeof(t2) == 7 + 21 * sizeof(T));
UNUSED(t1);
UNUSED(t2);
}
@ -416,6 +420,9 @@ struct TestBELE {
constexpr T zero = {};
constexpr T zero_copy = T::make(zero);
assert_noexcept(zero_copy == 0);
#if defined(upx_is_constant_evaluated)
static_assert(zero_copy == 0);
#endif
}
#if defined(upx_is_constant_evaluated)
{
@ -443,7 +450,12 @@ struct TestBELE {
static_assert(upx::max(one, U(4)) == 4);
static_assert(upx::align_down(one, four) == 0);
static_assert(upx::align_up(one, four) == 4);
static_assert(upx::align_up(one, four) == four);
static_assert(upx::align_gap(one, four) == 3);
static_assert(upx::align_gap(one, four) == T::make(four - 1));
static_assert(upx::align_gap(one, four) == T::make(four - one));
static_assert(upx::align_gap(one, four) == T::make(four + one - one - one));
static_assert(upx::align_gap(one, four) == T::make(four + one - 2 * one));
constexpr T one_copy = T::make(one);
static_assert(one_copy == one);
static_assert(one_copy == 1);
@ -457,10 +469,10 @@ template <class T, bool T_is_signed>
struct CheckSignedness {
template <class U, bool U_is_signed>
static inline void checkU(void) noexcept {
COMPILE_TIME_ASSERT(sizeof(U) == sizeof(T));
COMPILE_TIME_ASSERT(alignof(U) == alignof(T));
static_assert(sizeof(U) == sizeof(T));
static_assert(alignof(U) == alignof(T));
constexpr U all_bits = (U) (U(0) - U(1));
COMPILE_TIME_ASSERT(U_is_signed ? (all_bits < 0) : (all_bits > 0));
static_assert(U_is_signed ? (all_bits < 0) : (all_bits > 0));
}
static void check(void) noexcept {
checkU<T, T_is_signed>();
@ -620,9 +632,9 @@ void upx_compiler_sanity_check(void) noexcept {
auto a = +0;
constexpr auto b = -0;
const auto &c = -1;
COMPILE_TIME_ASSERT((std::is_same<int, decltype(a)>::value))
COMPILE_TIME_ASSERT((std::is_same<const int, decltype(b)>::value))
COMPILE_TIME_ASSERT((std::is_same<const int &, decltype(c)>::value))
static_assert((std::is_same<int, decltype(a)>::value));
static_assert((std::is_same<const int, decltype(b)>::value));
static_assert((std::is_same<const int &, decltype(c)>::value));
UNUSED(a);
UNUSED(b);
UNUSED(c);
@ -634,18 +646,18 @@ void upx_compiler_sanity_check(void) noexcept {
#include "../util/miniacc.h"
#undef ACCCHK_ASSERT
COMPILE_TIME_ASSERT(sizeof(char) == 1)
COMPILE_TIME_ASSERT(sizeof(short) == 2)
COMPILE_TIME_ASSERT(sizeof(int) == 4)
COMPILE_TIME_ASSERT(sizeof(long) >= 4)
COMPILE_TIME_ASSERT(sizeof(void *) >= 4)
static_assert(sizeof(char) == 1);
static_assert(sizeof(short) == 2);
static_assert(sizeof(int) == 4);
static_assert(sizeof(long) >= 4);
static_assert(sizeof(void *) >= 4);
COMPILE_TIME_ASSERT(sizeof(BE16) == 2)
COMPILE_TIME_ASSERT(sizeof(BE32) == 4)
COMPILE_TIME_ASSERT(sizeof(BE64) == 8)
COMPILE_TIME_ASSERT(sizeof(LE16) == 2)
COMPILE_TIME_ASSERT(sizeof(LE32) == 4)
COMPILE_TIME_ASSERT(sizeof(LE64) == 8)
static_assert(sizeof(BE16) == 2);
static_assert(sizeof(BE32) == 4);
static_assert(sizeof(BE64) == 8);
static_assert(sizeof(LE16) == 2);
static_assert(sizeof(LE32) == 4);
static_assert(sizeof(LE64) == 8);
COMPILE_TIME_ASSERT_ALIGNED1(BE16)
COMPILE_TIME_ASSERT_ALIGNED1(BE32)
@ -684,9 +696,9 @@ void upx_compiler_sanity_check(void) noexcept {
CheckSignedness<upx_sptraddr_t, true>::check();
CheckSignedness<upx_uintptr_t, false>::check();
COMPILE_TIME_ASSERT(sizeof(upx_charptr_unit_type) == 1)
static_assert(sizeof(upx_charptr_unit_type) == 1);
COMPILE_TIME_ASSERT_ALIGNED1(upx_charptr_unit_type)
COMPILE_TIME_ASSERT(sizeof(*((charptr) nullptr)) == 1)
static_assert(sizeof(*((charptr) nullptr)) == 1);
// check UPX_VERSION_xxx
{

View File

@ -120,8 +120,10 @@ static_assert(!compile_time::string_gt("abc", "abz"));
static_assert(!compile_time::string_ge("abc", "abz"));
static_assert(compile_time::string_le("abc", "abz"));
static_assert(compile_time::mem_eq((const char *) nullptr, nullptr, 0));
static_assert(compile_time::mem_eq((const byte *) nullptr, nullptr, 0));
static_assert(compile_time::mem_eq((const char *) nullptr, (const char *) nullptr, 0));
static_assert(compile_time::mem_eq((const char *) nullptr, (const byte *) nullptr, 0));
static_assert(compile_time::mem_eq((const byte *) nullptr, (const char *) nullptr, 0));
static_assert(compile_time::mem_eq((const byte *) nullptr, (const byte *) nullptr, 0));
static_assert(compile_time::mem_eq("", "", 0));
static_assert(compile_time::mem_eq("abc", "abc", 3));
static_assert(!compile_time::mem_eq("abc", "abz", 3));
@ -408,6 +410,7 @@ TEST_CASE("upx::ObjectDeleter 2") {
TEST_CASE("upx::ptr_static_cast") {
// check that we do not trigger any -Wcast-align warnings
using upx::ptr_static_cast;
void *vp = nullptr;
byte *bp = nullptr;
int *ip = nullptr;
@ -445,6 +448,110 @@ TEST_CASE("upx::ptr_static_cast") {
assert((ic == ptr_static_cast<const int *>(ic)));
}
TEST_CASE("upx::ptr_static_cast constexpr 1") {
// check that casts work at compile-time
using upx::ptr_static_cast;
constexpr void *vp = nullptr;
constexpr byte *bp = nullptr;
constexpr int *ip = nullptr;
constexpr double *dp = nullptr;
static_assert((vp == ptr_static_cast<void *>(vp)));
static_assert((bp == ptr_static_cast<byte *>(bp)));
static_assert((ip == ptr_static_cast<int *>(ip)));
static_assert((dp == ptr_static_cast<double *>(dp)));
constexpr const void *vc = nullptr;
constexpr const byte *bc = nullptr;
constexpr const int *ic = nullptr;
constexpr const double *dc = nullptr;
static_assert((vc == ptr_static_cast<const void *>(vc)));
static_assert((bc == ptr_static_cast<const byte *>(bc)));
static_assert((ic == ptr_static_cast<const int *>(ic)));
static_assert((dc == ptr_static_cast<const double *>(dc)));
constexpr void **vpp = nullptr;
constexpr byte **bpp = nullptr;
constexpr int **ipp = nullptr;
constexpr double **dpp = nullptr;
static_assert((vpp == ptr_static_cast<void **>(vpp)));
static_assert((bpp == ptr_static_cast<byte **>(bpp)));
static_assert((ipp == ptr_static_cast<int **>(ipp)));
static_assert((dpp == ptr_static_cast<double **>(dpp)));
constexpr const void **vcp = nullptr;
constexpr const byte **bcp = nullptr;
constexpr const int **icp = nullptr;
constexpr const double **dcp = nullptr;
static_assert((vcp == ptr_static_cast<const void **>(vcp)));
static_assert((bcp == ptr_static_cast<const byte **>(bcp)));
static_assert((icp == ptr_static_cast<const int **>(icp)));
static_assert((dcp == ptr_static_cast<const double **>(dcp)));
constexpr void *const *vpc = nullptr;
constexpr byte *const *bpc = nullptr;
constexpr int *const *ipc = nullptr;
constexpr double *const *dpc = nullptr;
static_assert((vpc == ptr_static_cast<void *const *>(vpc)));
static_assert((bpc == ptr_static_cast<byte *const *>(bpc)));
static_assert((ipc == ptr_static_cast<int *const *>(ipc)));
static_assert((dpc == ptr_static_cast<double *const *>(dpc)));
constexpr const void *const *vcc = nullptr;
constexpr const byte *const *bcc = nullptr;
constexpr const int *const *icc = nullptr;
constexpr const double *const *dcc = nullptr;
static_assert((vcc == ptr_static_cast<const void *const *>(vcc)));
static_assert((bcc == ptr_static_cast<const byte *const *>(bcc)));
static_assert((icc == ptr_static_cast<const int *const *>(icc)));
static_assert((dcc == ptr_static_cast<const double *const *>(dcc)));
}
TEST_CASE("upx::ptr_static_cast constexpr 2") {
// check that casts work at compile-time
using upx::ptr_static_cast;
constexpr void *vp = nullptr;
constexpr byte *bp = nullptr;
constexpr int *ip = nullptr;
constexpr double *dp = nullptr;
static_assert((vp == static_cast<void *>(vp)));
static_assert((vp == static_cast<void *>(bp)));
static_assert((vp == static_cast<void *>(ip)));
static_assert((vp == static_cast<void *>(dp)));
static_assert((vp == ptr_static_cast<void *>(vp)));
static_assert((vp == ptr_static_cast<void *>(bp)));
static_assert((vp == ptr_static_cast<void *>(ip)));
static_assert((vp == ptr_static_cast<void *>(dp)));
constexpr const void *vc = nullptr;
constexpr const byte *bc = nullptr;
constexpr const int *ic = nullptr;
constexpr const double *dc = nullptr;
static_assert((vc == static_cast<const void *>(vp)));
static_assert((vc == static_cast<const void *>(bp)));
static_assert((vc == static_cast<const void *>(ip)));
static_assert((vc == static_cast<const void *>(dp)));
static_assert((vc == ptr_static_cast<const void *>(vp)));
static_assert((vc == ptr_static_cast<const void *>(dp)));
static_assert((vc == ptr_static_cast<const void *>(bp)));
static_assert((vc == ptr_static_cast<const void *>(ip)));
static_assert((vc == static_cast<const void *>(vc)));
static_assert((vc == static_cast<const void *>(bc)));
static_assert((vc == static_cast<const void *>(ic)));
static_assert((vc == static_cast<const void *>(dc)));
static_assert((vc == ptr_static_cast<const void *>(vc)));
static_assert((vc == ptr_static_cast<const void *>(dc)));
static_assert((vc == ptr_static_cast<const void *>(bc)));
static_assert((vc == ptr_static_cast<const void *>(ic)));
// these are invalid:
//// constexpr char *cc1 = static_cast<char *>(bp);
//// constexpr char *cc2 = ptr_static_cast<char *>(bp);
//// constexpr unsigned *uc1 = static_cast<unsigned *>(ip);
//// constexpr unsigned *uc2 = ptr_static_cast<unsigned *>(ip);
}
TEST_CASE("upx::noncopyable") {
struct Test : private upx::noncopyable {
int v = 1;

View File

@ -239,7 +239,7 @@ forceinline constexpr T wrapping_sub(const T &a, const T &b) noexcept {
**************************************************************************/
template <std::size_t Size>
struct UnsignedSizeOf {
struct UnsignedSizeOf final {
static_assert(Size >= 1 && Size <= UPX_RSIZE_MAX_MEM);
static constexpr unsigned value = unsigned(Size);
};
@ -248,9 +248,9 @@ struct UnsignedSizeOf {
template <class Result, class From>
forceinline constexpr Result ptr_static_cast(From *ptr) noexcept {
static_assert(std::is_pointer_v<Result>);
static_assert(!std::is_const_v<std::remove_pointer_t<Result> >); // enforce same constness
// don't cast through "void *" if types already match
typedef std::conditional_t<std::is_same_v<Result, decltype(ptr)>, Result, void *> VoidPtr;
// don't cast through "void *" if type is convertible
typedef std::conditional_t<std::is_convertible_v<decltype(ptr), Result>, Result, void *>
VoidPtr;
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
return static_cast<Result>(static_cast<VoidPtr>(ptr));
}
@ -258,8 +258,9 @@ template <class Result, class From>
forceinline constexpr Result ptr_static_cast(const From *ptr) noexcept {
static_assert(std::is_pointer_v<Result>);
static_assert(std::is_const_v<std::remove_pointer_t<Result> >); // required
// don't cast through "void *" if types already match
typedef std::conditional_t<std::is_same_v<Result, decltype(ptr)>, Result, const void *> VoidPtr;
// don't cast through "const void *" if type is convertible
typedef std::conditional_t<std::is_convertible_v<decltype(ptr), Result>, Result, const void *>
VoidPtr;
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
return static_cast<Result>(static_cast<VoidPtr>(ptr));
}
@ -387,6 +388,12 @@ constexpr bool mem_eq(const char *a, const char *b, std::size_t n) noexcept {
constexpr bool mem_eq(const unsigned char *a, const unsigned char *b, std::size_t n) noexcept {
return n == 0 || (*a == *b && mem_eq(a + 1, b + 1, n - 1));
}
constexpr bool mem_eq(const char *a, const unsigned char *b, std::size_t n) noexcept {
return n == 0 || (*a == *b && mem_eq(a + 1, b + 1, n - 1));
}
constexpr bool mem_eq(const unsigned char *a, const char *b, std::size_t n) noexcept {
return n == 0 || (*a == *b && mem_eq(a + 1, b + 1, n - 1));
}
forceinline constexpr upx_uint16_t bswap16(upx_uint16_t v) noexcept {
return ((v >> 8) & 0xff) | ((v & 0xff) << 8);