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 // std::bit_cast does not preserve padding bits, so if T has padding bits, 25 // the results might not memcmp cleanly. 26 template<bool HasUniqueObjectRepresentations = true, typename T> 27 void test_roundtrip_through_buffer(T from) { 28 struct Buffer { char buffer[sizeof(T)]; }; 29 Buffer middle = std::bit_cast<Buffer>(from); 30 T to = std::bit_cast<T>(middle); 31 Buffer middle2 = std::bit_cast<Buffer>(to); 32 33 assert((from == to) == (from == from)); // because NaN 34 35 if constexpr (HasUniqueObjectRepresentations) { 36 assert(std::memcmp(&from, &middle, sizeof(T)) == 0); 37 assert(std::memcmp(&to, &middle, sizeof(T)) == 0); 38 assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); 39 } 40 } 41 42 template<bool HasUniqueObjectRepresentations = true, typename T> 43 void test_roundtrip_through_nested_T(T from) { 44 struct Nested { T x; }; 45 static_assert(sizeof(Nested) == sizeof(T)); 46 47 Nested middle = std::bit_cast<Nested>(from); 48 T to = std::bit_cast<T>(middle); 49 Nested middle2 = std::bit_cast<Nested>(to); 50 51 assert((from == to) == (from == from)); // because NaN 52 53 if constexpr (HasUniqueObjectRepresentations) { 54 assert(std::memcmp(&from, &middle, sizeof(T)) == 0); 55 assert(std::memcmp(&to, &middle, sizeof(T)) == 0); 56 assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); 57 } 58 } 59 60 template <typename Intermediate, bool HasUniqueObjectRepresentations = true, typename T> 61 void test_roundtrip_through(T from) { 62 static_assert(sizeof(Intermediate) == sizeof(T)); 63 64 Intermediate middle = std::bit_cast<Intermediate>(from); 65 T to = std::bit_cast<T>(middle); 66 Intermediate middle2 = std::bit_cast<Intermediate>(to); 67 68 assert((from == to) == (from == from)); // because NaN 69 70 if constexpr (HasUniqueObjectRepresentations) { 71 assert(std::memcmp(&from, &middle, sizeof(T)) == 0); 72 assert(std::memcmp(&to, &middle, sizeof(T)) == 0); 73 assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0); 74 } 75 } 76 77 template <typename T> 78 constexpr std::array<T, 10> generate_signed_integral_values() { 79 return {std::numeric_limits<T>::min(), 80 std::numeric_limits<T>::min() + 1, 81 static_cast<T>(-2), static_cast<T>(-1), 82 static_cast<T>(0), static_cast<T>(1), 83 static_cast<T>(2), static_cast<T>(3), 84 std::numeric_limits<T>::max() - 1, 85 std::numeric_limits<T>::max()}; 86 } 87 88 template <typename T> 89 constexpr std::array<T, 6> generate_unsigned_integral_values() { 90 return {static_cast<T>(0), static_cast<T>(1), 91 static_cast<T>(2), static_cast<T>(3), 92 std::numeric_limits<T>::max() - 1, 93 std::numeric_limits<T>::max()}; 94 } 95 96 bool tests() { 97 for (bool b : {false, true}) { 98 test_roundtrip_through_nested_T(b); 99 test_roundtrip_through_buffer(b); 100 test_roundtrip_through<char>(b); 101 } 102 103 for (char c : {'\0', 'a', 'b', 'c', 'd'}) { 104 test_roundtrip_through_nested_T(c); 105 test_roundtrip_through_buffer(c); 106 } 107 108 // Fundamental signed integer types 109 for (signed char i : generate_signed_integral_values<signed char>()) { 110 test_roundtrip_through_nested_T(i); 111 test_roundtrip_through_buffer(i); 112 } 113 114 for (short i : generate_signed_integral_values<short>()) { 115 test_roundtrip_through_nested_T(i); 116 test_roundtrip_through_buffer(i); 117 } 118 119 for (int i : generate_signed_integral_values<int>()) { 120 test_roundtrip_through_nested_T(i); 121 test_roundtrip_through_buffer(i); 122 test_roundtrip_through<float>(i); 123 } 124 125 for (long i : generate_signed_integral_values<long>()) { 126 test_roundtrip_through_nested_T(i); 127 test_roundtrip_through_buffer(i); 128 } 129 130 for (long long i : generate_signed_integral_values<long long>()) { 131 test_roundtrip_through_nested_T(i); 132 test_roundtrip_through_buffer(i); 133 test_roundtrip_through<double>(i); 134 } 135 136 // Fundamental unsigned integer types 137 for (unsigned char i : generate_unsigned_integral_values<unsigned char>()) { 138 test_roundtrip_through_nested_T(i); 139 test_roundtrip_through_buffer(i); 140 } 141 142 for (unsigned short i : generate_unsigned_integral_values<unsigned short>()) { 143 test_roundtrip_through_nested_T(i); 144 test_roundtrip_through_buffer(i); 145 } 146 147 for (unsigned int i : generate_unsigned_integral_values<unsigned int>()) { 148 test_roundtrip_through_nested_T(i); 149 test_roundtrip_through_buffer(i); 150 test_roundtrip_through<float>(i); 151 } 152 153 for (unsigned long i : generate_unsigned_integral_values<unsigned long>()) { 154 test_roundtrip_through_nested_T(i); 155 test_roundtrip_through_buffer(i); 156 } 157 158 for (unsigned long long i : generate_unsigned_integral_values<unsigned long long>()) { 159 test_roundtrip_through_nested_T(i); 160 test_roundtrip_through_buffer(i); 161 test_roundtrip_through<double>(i); 162 } 163 164 // Fixed width signed integer types 165 for (std::int32_t i : generate_signed_integral_values<std::int32_t>()) { 166 test_roundtrip_through_nested_T(i); 167 test_roundtrip_through_buffer(i); 168 test_roundtrip_through<int>(i); 169 test_roundtrip_through<std::uint32_t>(i); 170 test_roundtrip_through<float>(i); 171 } 172 173 for (std::int64_t i : generate_signed_integral_values<std::int64_t>()) { 174 test_roundtrip_through_nested_T(i); 175 test_roundtrip_through_buffer(i); 176 test_roundtrip_through<long long>(i); 177 test_roundtrip_through<std::uint64_t>(i); 178 test_roundtrip_through<double>(i); 179 } 180 181 // Fixed width unsigned integer types 182 for (std::uint32_t i : generate_unsigned_integral_values<std::uint32_t>()) { 183 test_roundtrip_through_nested_T(i); 184 test_roundtrip_through_buffer(i); 185 test_roundtrip_through<int>(i); 186 test_roundtrip_through<std::int32_t>(i); 187 test_roundtrip_through<float>(i); 188 } 189 190 for (std::uint64_t i : generate_unsigned_integral_values<std::uint64_t>()) { 191 test_roundtrip_through_nested_T(i); 192 test_roundtrip_through_buffer(i); 193 test_roundtrip_through<long long>(i); 194 test_roundtrip_through<std::int64_t>(i); 195 test_roundtrip_through<double>(i); 196 } 197 198 // Floating point types 199 for (float i : {0.0f, 1.0f, -1.0f, 10.0f, -10.0f, 1e10f, 1e-10f, 1e20f, 1e-20f, 2.71828f, 3.14159f, 200 std::nanf(""), 201 __builtin_nanf("0x55550001"), // NaN with a payload 202 std::numeric_limits<float>::signaling_NaN(), 203 std::numeric_limits<float>::quiet_NaN()}) { 204 test_roundtrip_through_nested_T(i); 205 test_roundtrip_through_buffer(i); 206 test_roundtrip_through<int>(i); 207 } 208 209 for (double i : {0.0, 1.0, -1.0, 10.0, -10.0, 1e10, 1e-10, 1e100, 1e-100, 210 2.718281828459045, 211 3.141592653589793238462643383279502884197169399375105820974944, 212 std::nan(""), 213 std::numeric_limits<double>::signaling_NaN(), 214 std::numeric_limits<double>::quiet_NaN()}) { 215 test_roundtrip_through_nested_T(i); 216 test_roundtrip_through_buffer(i); 217 test_roundtrip_through<long long>(i); 218 } 219 220 for (long double i : {0.0l, 1.0l, -1.0l, 10.0l, -10.0l, 1e10l, 1e-10l, 1e100l, 1e-100l, 221 2.718281828459045l, 222 3.141592653589793238462643383279502884197169399375105820974944l, 223 std::nanl(""), 224 std::numeric_limits<long double>::signaling_NaN(), 225 std::numeric_limits<long double>::quiet_NaN()}) { 226 // Note that x86's `long double` has 80 value bits and 48 padding bits. 227 test_roundtrip_through_nested_T<false>(i); 228 test_roundtrip_through_buffer<false>(i); 229 230 // On arm64 on Apple platforms, long double is just double, so we don't 231 // test against int128, but instead against double itself. Otherwise, 232 // we test against int128 if we have those types available. 233 #if defined(__aarch64__) && defined(__APPLE__) 234 # define LONG_DOUBLE_IS_DOUBLE 235 #endif 236 237 #if defined(LONG_DOUBLE_IS_DOUBLE) 238 test_roundtrip_through<double, false>(i); 239 #elif !defined(_LIBCPP_HAS_NO_INT128) 240 test_roundtrip_through<__int128_t, false>(i); 241 test_roundtrip_through<__uint128_t, false>(i); 242 #endif 243 } 244 245 return true; 246 } 247 248 // TODO: There doesn't seem to be a way to perform non-trivial correctness 249 // tests inside constexpr. 250 constexpr bool basic_constexpr_test() { 251 struct Nested { char buffer[sizeof(int)]; }; 252 int from = 3; 253 Nested middle = std::bit_cast<Nested>(from); 254 int to = std::bit_cast<int>(middle); 255 assert(from == to); 256 return true; 257 } 258 259 int main(int, char**) { 260 tests(); 261 static_assert(basic_constexpr_test()); 262 return 0; 263 } 264