//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // // // template // constexpr To bit_cast(const From& from) noexcept; // C++20 #include #include #include #include #include #include #include #include "test_macros.h" // std::bit_cast does not preserve padding bits, so if T has padding bits, // the results might not memcmp cleanly. template void test_roundtrip_through_buffer(T from) { struct Buffer { char buffer[sizeof(T)]; }; Buffer middle = std::bit_cast(from); T to = std::bit_cast(middle); Buffer middle2 = std::bit_cast(to); assert((from == to) == (from == from)); // because NaN if constexpr (HasUniqueObjectRepresentations) { assert(std::memcmp(&from, &middle, sizeof(T)) == 0); assert(std::memcmp(&to, &middle, sizeof(T)) == 0); assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); } } template void test_roundtrip_through_nested_T(T from) { struct Nested { T x; }; static_assert(sizeof(Nested) == sizeof(T)); Nested middle = std::bit_cast(from); T to = std::bit_cast(middle); Nested middle2 = std::bit_cast(to); assert((from == to) == (from == from)); // because NaN if constexpr (HasUniqueObjectRepresentations) { assert(std::memcmp(&from, &middle, sizeof(T)) == 0); assert(std::memcmp(&to, &middle, sizeof(T)) == 0); assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); } } template void test_roundtrip_through(T from) { static_assert(sizeof(Intermediate) == sizeof(T)); Intermediate middle = std::bit_cast(from); T to = std::bit_cast(middle); Intermediate middle2 = std::bit_cast(to); assert((from == to) == (from == from)); // because NaN if constexpr (HasUniqueObjectRepresentations) { assert(std::memcmp(&from, &middle, sizeof(T)) == 0); assert(std::memcmp(&to, &middle, sizeof(T)) == 0); assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); } } template constexpr std::array generate_signed_integral_values() { return {std::numeric_limits::min(), std::numeric_limits::min() + 1, static_cast(-2), static_cast(-1), static_cast(0), static_cast(1), static_cast(2), static_cast(3), std::numeric_limits::max() - 1, std::numeric_limits::max()}; } template constexpr std::array generate_unsigned_integral_values() { return {static_cast(0), static_cast(1), static_cast(2), static_cast(3), std::numeric_limits::max() - 1, std::numeric_limits::max()}; } bool tests() { for (bool b : {false, true}) { test_roundtrip_through_nested_T(b); test_roundtrip_through_buffer(b); test_roundtrip_through(b); } for (char c : {'\0', 'a', 'b', 'c', 'd'}) { test_roundtrip_through_nested_T(c); test_roundtrip_through_buffer(c); } // Fundamental signed integer types for (signed char i : generate_signed_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); } for (short i : generate_signed_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); } for (int i : generate_signed_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); } for (long i : generate_signed_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); } for (long long i : generate_signed_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); } // Fundamental unsigned integer types for (unsigned char i : generate_unsigned_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); } for (unsigned short i : generate_unsigned_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); } for (unsigned int i : generate_unsigned_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); } for (unsigned long i : generate_unsigned_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); } for (unsigned long long i : generate_unsigned_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); } // Fixed width signed integer types for (std::int32_t i : generate_signed_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); test_roundtrip_through(i); test_roundtrip_through(i); } for (std::int64_t i : generate_signed_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); test_roundtrip_through(i); test_roundtrip_through(i); } // Fixed width unsigned integer types for (std::uint32_t i : generate_unsigned_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); test_roundtrip_through(i); test_roundtrip_through(i); } for (std::uint64_t i : generate_unsigned_integral_values()) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); test_roundtrip_through(i); test_roundtrip_through(i); } // Floating point types for (float i : {0.0f, 1.0f, -1.0f, 10.0f, -10.0f, 1e10f, 1e-10f, 1e20f, 1e-20f, 2.71828f, 3.14159f, std::nanf(""), __builtin_nanf("0x55550001"), // NaN with a payload std::numeric_limits::signaling_NaN(), std::numeric_limits::quiet_NaN()}) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); } for (double i : {0.0, 1.0, -1.0, 10.0, -10.0, 1e10, 1e-10, 1e100, 1e-100, 2.718281828459045, 3.141592653589793238462643383279502884197169399375105820974944, std::nan(""), std::numeric_limits::signaling_NaN(), std::numeric_limits::quiet_NaN()}) { test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); test_roundtrip_through(i); } for (long double i : {0.0l, 1.0l, -1.0l, 10.0l, -10.0l, 1e10l, 1e-10l, 1e100l, 1e-100l, 2.718281828459045l, 3.141592653589793238462643383279502884197169399375105820974944l, std::nanl(""), std::numeric_limits::signaling_NaN(), std::numeric_limits::quiet_NaN()}) { // Note that x86's `long double` has 80 value bits and 48 padding bits. test_roundtrip_through_nested_T(i); test_roundtrip_through_buffer(i); #ifdef TEST_LONG_DOUBLE_IS_DOUBLE test_roundtrip_through(i); #endif #if defined(__SIZEOF_INT128__) && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_INT128__ && \ !TEST_HAS_FEATURE(memory_sanitizer) // Some bits are just padding. test_roundtrip_through<__int128_t, false>(i); test_roundtrip_through<__uint128_t, false>(i); #endif } // Test pointers { { int obj = 3; void* p = &obj; test_roundtrip_through_nested_T(p); test_roundtrip_through_buffer(p); test_roundtrip_through(p); test_roundtrip_through(p); test_roundtrip_through(p); } { int obj = 3; int* p = &obj; test_roundtrip_through_nested_T(p); test_roundtrip_through_buffer(p); test_roundtrip_through(p); test_roundtrip_through(p); test_roundtrip_through(p); } } return true; } // TODO: There doesn't seem to be a way to perform non-trivial correctness // tests inside constexpr. constexpr bool basic_constexpr_test() { struct Nested { char buffer[sizeof(int)]; }; int from = 3; Nested middle = std::bit_cast(from); int to = std::bit_cast(middle); assert(from == to); return true; } int main(int, char**) { tests(); static_assert(basic_constexpr_test()); return 0; }