From 9f4d18baacd754936b108218a78cfd1ad544d541 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Mon, 27 May 2024 07:03:03 +0200 Subject: [PATCH] src: yet more constexpr updates --- src/check/dt_check.cpp | 76 +++++++++++++++------------ src/check/dt_cxxlib.cpp | 111 +++++++++++++++++++++++++++++++++++++++- src/util/cxxlib.h | 19 ++++--- 3 files changed, 166 insertions(+), 40 deletions(-) diff --git a/src/check/dt_check.cpp b/src/check/dt_check.cpp index f6efb1fb..92d44638 100644 --- a/src/check/dt_check.cpp +++ b/src/check/dt_check.cpp @@ -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 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::value) // std::is_pod is deprecated in C++20 + static_assert(std::is_pod::value); // std::is_pod is deprecated in C++20 #endif - COMPILE_TIME_ASSERT(std::is_standard_layout::value) - COMPILE_TIME_ASSERT(std::is_trivial::value) + static_assert(std::is_standard_layout::value); + static_assert(std::is_trivial::value); // more checks, these are probably implied by std::is_trivial - COMPILE_TIME_ASSERT(std::is_nothrow_default_constructible::value) - COMPILE_TIME_ASSERT(std::is_nothrow_destructible::value) - COMPILE_TIME_ASSERT(std::is_trivially_copyable::value) - COMPILE_TIME_ASSERT(std::is_trivially_default_constructible::value) + static_assert(std::is_nothrow_default_constructible::value); + static_assert(std::is_nothrow_destructible::value); + static_assert(std::is_trivially_copyable::value); + static_assert(std::is_trivially_default_constructible::value); // UPX extras - COMPILE_TIME_ASSERT(upx_is_integral::value) - COMPILE_TIME_ASSERT(upx_is_integral_v) + static_assert(upx_is_integral::value); + static_assert(upx_is_integral_v); } 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 struct CheckSignedness { template 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(); @@ -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::value)) - COMPILE_TIME_ASSERT((std::is_same::value)) - COMPILE_TIME_ASSERT((std::is_same::value)) + static_assert((std::is_same::value)); + static_assert((std::is_same::value)); + static_assert((std::is_same::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::check(); CheckSignedness::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 { diff --git a/src/check/dt_cxxlib.cpp b/src/check/dt_cxxlib.cpp index 9b831d00..3c2de6b7 100644 --- a/src/check/dt_cxxlib.cpp +++ b/src/check/dt_cxxlib.cpp @@ -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(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(vp))); + static_assert((bp == ptr_static_cast(bp))); + static_assert((ip == ptr_static_cast(ip))); + static_assert((dp == ptr_static_cast(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(vc))); + static_assert((bc == ptr_static_cast(bc))); + static_assert((ic == ptr_static_cast(ic))); + static_assert((dc == ptr_static_cast(dc))); + + constexpr void **vpp = nullptr; + constexpr byte **bpp = nullptr; + constexpr int **ipp = nullptr; + constexpr double **dpp = nullptr; + static_assert((vpp == ptr_static_cast(vpp))); + static_assert((bpp == ptr_static_cast(bpp))); + static_assert((ipp == ptr_static_cast(ipp))); + static_assert((dpp == ptr_static_cast(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(vcp))); + static_assert((bcp == ptr_static_cast(bcp))); + static_assert((icp == ptr_static_cast(icp))); + static_assert((dcp == ptr_static_cast(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(vpc))); + static_assert((bpc == ptr_static_cast(bpc))); + static_assert((ipc == ptr_static_cast(ipc))); + static_assert((dpc == ptr_static_cast(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(vcc))); + static_assert((bcc == ptr_static_cast(bcc))); + static_assert((icc == ptr_static_cast(icc))); + static_assert((dcc == ptr_static_cast(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(vp))); + static_assert((vp == static_cast(bp))); + static_assert((vp == static_cast(ip))); + static_assert((vp == static_cast(dp))); + static_assert((vp == ptr_static_cast(vp))); + static_assert((vp == ptr_static_cast(bp))); + static_assert((vp == ptr_static_cast(ip))); + static_assert((vp == ptr_static_cast(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(vp))); + static_assert((vc == static_cast(bp))); + static_assert((vc == static_cast(ip))); + static_assert((vc == static_cast(dp))); + static_assert((vc == ptr_static_cast(vp))); + static_assert((vc == ptr_static_cast(dp))); + static_assert((vc == ptr_static_cast(bp))); + static_assert((vc == ptr_static_cast(ip))); + static_assert((vc == static_cast(vc))); + static_assert((vc == static_cast(bc))); + static_assert((vc == static_cast(ic))); + static_assert((vc == static_cast(dc))); + static_assert((vc == ptr_static_cast(vc))); + static_assert((vc == ptr_static_cast(dc))); + static_assert((vc == ptr_static_cast(bc))); + static_assert((vc == ptr_static_cast(ic))); + + // these are invalid: + //// constexpr char *cc1 = static_cast(bp); + //// constexpr char *cc2 = ptr_static_cast(bp); + //// constexpr unsigned *uc1 = static_cast(ip); + //// constexpr unsigned *uc2 = ptr_static_cast(ip); +} + TEST_CASE("upx::noncopyable") { struct Test : private upx::noncopyable { int v = 1; diff --git a/src/util/cxxlib.h b/src/util/cxxlib.h index 6026c686..c4f52e07 100644 --- a/src/util/cxxlib.h +++ b/src/util/cxxlib.h @@ -239,7 +239,7 @@ forceinline constexpr T wrapping_sub(const T &a, const T &b) noexcept { **************************************************************************/ template -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 forceinline constexpr Result ptr_static_cast(From *ptr) noexcept { static_assert(std::is_pointer_v); - static_assert(!std::is_const_v >); // enforce same constness - // don't cast through "void *" if types already match - typedef std::conditional_t, Result, void *> VoidPtr; + // don't cast through "void *" if type is convertible + typedef std::conditional_t, Result, void *> + VoidPtr; // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) return static_cast(static_cast(ptr)); } @@ -258,8 +258,9 @@ template forceinline constexpr Result ptr_static_cast(const From *ptr) noexcept { static_assert(std::is_pointer_v); static_assert(std::is_const_v >); // required - // don't cast through "void *" if types already match - typedef std::conditional_t, Result, const void *> VoidPtr; + // don't cast through "const void *" if type is convertible + typedef std::conditional_t, Result, const void *> + VoidPtr; // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) return static_cast(static_cast(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);