1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17 10 11 // <bit> 12 // 13 // template<class To, class From> 14 // constexpr To bit_cast(const From& from) noexcept; // C++20 15 16 #include <array> 17 #include <bit> 18 #include <cassert> 19 #include <cmath> 20 #include <cstdint> 21 #include <cstring> 22 #include <limits> 23 24 #include "test_macros.h" 25 26 // std::bit_cast does not preserve padding bits, so if T has padding bits, 27 // the results might not memcmp cleanly. 28 template<bool HasUniqueObjectRepresentations = true, typename T> 29 void test_roundtrip_through_buffer(T from) { 30 struct Buffer { char buffer[sizeof(T)]; }; 31 Buffer middle = std::bit_cast<Buffer>(from); 32 T to = std::bit_cast<T>(middle); 33 Buffer middle2 = std::bit_cast<Buffer>(to); 34 35 assert((from == to) == (from == from)); // because NaN 36 37 if constexpr (HasUniqueObjectRepresentations) { 38 assert(std::memcmp(&from, &middle, sizeof(T)) == 0); 39 assert(std::memcmp(&to, &middle, sizeof(T)) == 0); 40 assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); 41 } 42 } 43 44 template<bool HasUniqueObjectRepresentations = true, typename T> 45 void test_roundtrip_through_nested_T(T from) { 46 struct Nested { T x; }; 47 static_assert(sizeof(Nested) == sizeof(T)); 48 49 Nested middle = std::bit_cast<Nested>(from); 50 T to = std::bit_cast<T>(middle); 51 Nested middle2 = std::bit_cast<Nested>(to); 52 53 assert((from == to) == (from == from)); // because NaN 54 55 if constexpr (HasUniqueObjectRepresentations) { 56 assert(std::memcmp(&from, &middle, sizeof(T)) == 0); 57 assert(std::memcmp(&to, &middle, sizeof(T)) == 0); 58 assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); 59 } 60 } 61 62 template <typename Intermediate, bool HasUniqueObjectRepresentations = true, typename T> 63 void test_roundtrip_through(T from) { 64 static_assert(sizeof(Intermediate) == sizeof(T)); 65 66 Intermediate middle = std::bit_cast<Intermediate>(from); 67 T to = std::bit_cast<T>(middle); 68 Intermediate middle2 = std::bit_cast<Intermediate>(to); 69 70 assert((from == to) == (from == from)); // because NaN 71 72 if constexpr (HasUniqueObjectRepresentations) { 73 assert(std::memcmp(&from, &middle, sizeof(T)) == 0); 74 assert(std::memcmp(&to, &middle, sizeof(T)) == 0); 75 assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); 76 } 77 } 78 79 template <typename T> 80 constexpr std::array<T, 10> generate_signed_integral_values() { 81 return {std::numeric_limits<T>::min(), 82 std::numeric_limits<T>::min() + 1, 83 static_cast<T>(-2), static_cast<T>(-1), 84 static_cast<T>(0), static_cast<T>(1), 85 static_cast<T>(2), static_cast<T>(3), 86 std::numeric_limits<T>::max() - 1, 87 std::numeric_limits<T>::max()}; 88 } 89 90 template <typename T> 91 constexpr std::array<T, 6> generate_unsigned_integral_values() { 92 return {static_cast<T>(0), static_cast<T>(1), 93 static_cast<T>(2), static_cast<T>(3), 94 std::numeric_limits<T>::max() - 1, 95 std::numeric_limits<T>::max()}; 96 } 97 98 bool tests() { 99 for (bool b : {false, true}) { 100 test_roundtrip_through_nested_T(b); 101 test_roundtrip_through_buffer(b); 102 test_roundtrip_through<char>(b); 103 } 104 105 for (char c : {'\0', 'a', 'b', 'c', 'd'}) { 106 test_roundtrip_through_nested_T(c); 107 test_roundtrip_through_buffer(c); 108 } 109 110 // Fundamental signed integer types 111 for (signed char i : generate_signed_integral_values<signed char>()) { 112 test_roundtrip_through_nested_T(i); 113 test_roundtrip_through_buffer(i); 114 } 115 116 for (short i : generate_signed_integral_values<short>()) { 117 test_roundtrip_through_nested_T(i); 118 test_roundtrip_through_buffer(i); 119 } 120 121 for (int i : generate_signed_integral_values<int>()) { 122 test_roundtrip_through_nested_T(i); 123 test_roundtrip_through_buffer(i); 124 test_roundtrip_through<float>(i); 125 } 126 127 for (long i : generate_signed_integral_values<long>()) { 128 test_roundtrip_through_nested_T(i); 129 test_roundtrip_through_buffer(i); 130 } 131 132 for (long long i : generate_signed_integral_values<long long>()) { 133 test_roundtrip_through_nested_T(i); 134 test_roundtrip_through_buffer(i); 135 test_roundtrip_through<double>(i); 136 } 137 138 // Fundamental unsigned integer types 139 for (unsigned char i : generate_unsigned_integral_values<unsigned char>()) { 140 test_roundtrip_through_nested_T(i); 141 test_roundtrip_through_buffer(i); 142 } 143 144 for (unsigned short i : generate_unsigned_integral_values<unsigned short>()) { 145 test_roundtrip_through_nested_T(i); 146 test_roundtrip_through_buffer(i); 147 } 148 149 for (unsigned int i : generate_unsigned_integral_values<unsigned int>()) { 150 test_roundtrip_through_nested_T(i); 151 test_roundtrip_through_buffer(i); 152 test_roundtrip_through<float>(i); 153 } 154 155 for (unsigned long i : generate_unsigned_integral_values<unsigned long>()) { 156 test_roundtrip_through_nested_T(i); 157 test_roundtrip_through_buffer(i); 158 } 159 160 for (unsigned long long i : generate_unsigned_integral_values<unsigned long long>()) { 161 test_roundtrip_through_nested_T(i); 162 test_roundtrip_through_buffer(i); 163 test_roundtrip_through<double>(i); 164 } 165 166 // Fixed width signed integer types 167 for (std::int32_t i : generate_signed_integral_values<std::int32_t>()) { 168 test_roundtrip_through_nested_T(i); 169 test_roundtrip_through_buffer(i); 170 test_roundtrip_through<int>(i); 171 test_roundtrip_through<std::uint32_t>(i); 172 test_roundtrip_through<float>(i); 173 } 174 175 for (std::int64_t i : generate_signed_integral_values<std::int64_t>()) { 176 test_roundtrip_through_nested_T(i); 177 test_roundtrip_through_buffer(i); 178 test_roundtrip_through<long long>(i); 179 test_roundtrip_through<std::uint64_t>(i); 180 test_roundtrip_through<double>(i); 181 } 182 183 // Fixed width unsigned integer types 184 for (std::uint32_t i : generate_unsigned_integral_values<std::uint32_t>()) { 185 test_roundtrip_through_nested_T(i); 186 test_roundtrip_through_buffer(i); 187 test_roundtrip_through<int>(i); 188 test_roundtrip_through<std::int32_t>(i); 189 test_roundtrip_through<float>(i); 190 } 191 192 for (std::uint64_t i : generate_unsigned_integral_values<std::uint64_t>()) { 193 test_roundtrip_through_nested_T(i); 194 test_roundtrip_through_buffer(i); 195 test_roundtrip_through<long long>(i); 196 test_roundtrip_through<std::int64_t>(i); 197 test_roundtrip_through<double>(i); 198 } 199 200 // Floating point types 201 for (float i : {0.0f, 1.0f, -1.0f, 10.0f, -10.0f, 1e10f, 1e-10f, 1e20f, 1e-20f, 2.71828f, 3.14159f, 202 std::nanf(""), 203 __builtin_nanf("0x55550001"), // NaN with a payload 204 std::numeric_limits<float>::signaling_NaN(), 205 std::numeric_limits<float>::quiet_NaN()}) { 206 test_roundtrip_through_nested_T(i); 207 test_roundtrip_through_buffer(i); 208 test_roundtrip_through<int>(i); 209 } 210 211 for (double i : {0.0, 1.0, -1.0, 10.0, -10.0, 1e10, 1e-10, 1e100, 1e-100, 212 2.718281828459045, 213 3.141592653589793238462643383279502884197169399375105820974944, 214 std::nan(""), 215 std::numeric_limits<double>::signaling_NaN(), 216 std::numeric_limits<double>::quiet_NaN()}) { 217 test_roundtrip_through_nested_T(i); 218 test_roundtrip_through_buffer(i); 219 test_roundtrip_through<long long>(i); 220 } 221 222 for (long double i : {0.0l, 1.0l, -1.0l, 10.0l, -10.0l, 1e10l, 1e-10l, 1e100l, 1e-100l, 223 2.718281828459045l, 224 3.141592653589793238462643383279502884197169399375105820974944l, 225 std::nanl(""), 226 std::numeric_limits<long double>::signaling_NaN(), 227 std::numeric_limits<long double>::quiet_NaN()}) { 228 // Note that x86's `long double` has 80 value bits and 48 padding bits. 229 test_roundtrip_through_nested_T<false>(i); 230 test_roundtrip_through_buffer<false>(i); 231 232 #ifdef TEST_LONG_DOUBLE_IS_DOUBLE 233 test_roundtrip_through<double, false>(i); 234 #endif 235 #if defined(__SIZEOF_INT128__) && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_INT128__ && \ 236 !TEST_HAS_FEATURE(memory_sanitizer) // Some bits are just padding. 237 test_roundtrip_through<__int128_t, false>(i); 238 test_roundtrip_through<__uint128_t, false>(i); 239 #endif 240 } 241 242 // Test pointers 243 { 244 { 245 int obj = 3; 246 void* p = &obj; 247 test_roundtrip_through_nested_T(p); 248 test_roundtrip_through_buffer(p); 249 test_roundtrip_through<void*>(p); 250 test_roundtrip_through<char*>(p); 251 test_roundtrip_through<int*>(p); 252 } 253 { 254 int obj = 3; 255 int* p = &obj; 256 test_roundtrip_through_nested_T(p); 257 test_roundtrip_through_buffer(p); 258 test_roundtrip_through<int*>(p); 259 test_roundtrip_through<char*>(p); 260 test_roundtrip_through<void*>(p); 261 } 262 } 263 264 return true; 265 } 266 267 // TODO: There doesn't seem to be a way to perform non-trivial correctness 268 // tests inside constexpr. 269 constexpr bool basic_constexpr_test() { 270 struct Nested { char buffer[sizeof(int)]; }; 271 int from = 3; 272 Nested middle = std::bit_cast<Nested>(from); 273 int to = std::bit_cast<int>(middle); 274 assert(from == to); 275 return true; 276 } 277 278 int main(int, char**) { 279 tests(); 280 static_assert(basic_constexpr_test()); 281 return 0; 282 } 283