[libc++] Implement C++20's P0476R2: std::bit_cast
Thanks to Arthur O'Dwyer for fixing up some of the tests.
Differential Revision: https://reviews.llvm.org/D75960
NOKEYCHECK=True
GitOrigin-RevId: b1fb3d75c953fa2e02ebddb6ebbf100f99786f0c
diff --git a/docs/FeatureTestMacroTable.rst b/docs/FeatureTestMacroTable.rst
index c2690e7..68d2e3a 100644
--- a/docs/FeatureTestMacroTable.rst
+++ b/docs/FeatureTestMacroTable.rst
@@ -190,7 +190,7 @@
------------------------------------------------- -----------------
``__cpp_lib_bind_front`` ``201907L``
------------------------------------------------- -----------------
- ``__cpp_lib_bit_cast`` *unimplemented*
+ ``__cpp_lib_bit_cast`` ``201806L``
------------------------------------------------- -----------------
``__cpp_lib_bitops`` *unimplemented*
------------------------------------------------- -----------------
diff --git a/docs/Status/Cxx20Papers.csv b/docs/Status/Cxx20Papers.csv
index 7915b79..a95a11f 100644
--- a/docs/Status/Cxx20Papers.csv
+++ b/docs/Status/Cxx20Papers.csv
@@ -29,7 +29,7 @@
"`P0019R8 <https://wg21.link/P0019R8>`__","LWG","Atomic Ref","Rapperswil","",""
"`P0458R2 <https://wg21.link/P0458R2>`__","LWG","Checking for Existence of an Element in Associative Containers","Rapperswil","|Complete|","13.0"
"`P0475R1 <https://wg21.link/P0475R1>`__","LWG","LWG 2511: guaranteed copy elision for piecewise construction","Rapperswil","|Complete|",""
-"`P0476R2 <https://wg21.link/P0476R2>`__","LWG","Bit-casting object representations","Rapperswil","",""
+"`P0476R2 <https://wg21.link/P0476R2>`__","LWG","Bit-casting object representations","Rapperswil","|Complete|","14.0"
"`P0528R3 <https://wg21.link/P0528R3>`__","CWG","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","Rapperswil","",""
"`P0542R5 <https://wg21.link/P0542R5>`__","CWG","Support for contract based programming in C++","Rapperswil","*Removed in Cologne*","n/a"
"`P0556R3 <https://wg21.link/P0556R3>`__","LWG","Integral power-of-2 operations","Rapperswil","|Complete|","9.0"
diff --git a/docs/UsingLibcxx.rst b/docs/UsingLibcxx.rst
index 8631236..bd999f0 100644
--- a/docs/UsingLibcxx.rst
+++ b/docs/UsingLibcxx.rst
@@ -338,6 +338,7 @@
* ``upper_bound``
* ``lock_guard``'s constructors
* ``as_const``
+* ``bit_cast``
* ``forward``
* ``move``
* ``move_if_noexcept``
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 4217e19..34cc9a5 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -94,6 +94,7 @@
__algorithm/upper_bound.h
__availability
__bit_reference
+ __bit/bit_cast.h
__bits
__bsd_locale_defaults.h
__bsd_locale_fallbacks.h
diff --git a/include/__bit/bit_cast.h b/include/__bit/bit_cast.h
new file mode 100644
index 0000000..6cfe4d7
--- /dev/null
+++ b/include/__bit/bit_cast.h
@@ -0,0 +1,38 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___BIT_BIT_CAST_H
+#define _LIBCPP___BIT_BIT_CAST_H
+
+#include <__config>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17
+
+template<class _ToType, class _FromType, class = enable_if_t<
+ sizeof(_ToType) == sizeof(_FromType) &&
+ is_trivially_copyable_v<_ToType> &&
+ is_trivially_copyable_v<_FromType>
+>>
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI
+constexpr _ToType bit_cast(_FromType const& __from) noexcept {
+ return __builtin_bit_cast(_ToType, __from);
+}
+
+#endif // _LIBCPP_STD_VER > 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___BIT_BIT_CAST_H
diff --git a/include/bit b/include/bit
index 168cdd9..eab6797 100644
--- a/include/bit
+++ b/include/bit
@@ -14,6 +14,9 @@
bit synopsis
namespace std {
+ // [bit.cast], bit_cast
+ template<class To, class From>
+ constexpr To bit_cast(const From& from) noexcept; // C++20
// [bit.pow.two], integral powers of 2
template <class T>
@@ -54,8 +57,9 @@
*/
-#include <__config>
+#include <__bit/bit_cast.h>
#include <__bits> // __libcpp_clz
+#include <__config>
#include <__debug>
#include <limits>
#include <type_traits>
diff --git a/include/module.modulemap b/include/module.modulemap
index 4986fd6..e49d78a 100644
--- a/include/module.modulemap
+++ b/include/module.modulemap
@@ -335,6 +335,10 @@
module bit {
header "bit"
export *
+
+ module __bit {
+ module bit_cast { private header "__bit/bit_cast.h" }
+ }
}
module bitset {
header "bitset"
diff --git a/include/version b/include/version
index 8db08ef..8aa5fd4 100644
--- a/include/version
+++ b/include/version
@@ -286,7 +286,7 @@
# define __cpp_lib_barrier 201907L
# endif
# define __cpp_lib_bind_front 201907L
-// # define __cpp_lib_bit_cast 201806L
+# define __cpp_lib_bit_cast 201806L
// # define __cpp_lib_bitops 201907L
# define __cpp_lib_bounded_array_traits 201902L
# if !defined(_LIBCPP_HAS_NO_CHAR8_T)
diff --git a/test/libcxx/diagnostics/detail.headers/bit/bit_cast.module.verify.cpp b/test/libcxx/diagnostics/detail.headers/bit/bit_cast.module.verify.cpp
new file mode 100644
index 0000000..c5fb85d
--- /dev/null
+++ b/test/libcxx/diagnostics/detail.headers/bit/bit_cast.module.verify.cpp
@@ -0,0 +1,16 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__bit/bit_cast.h'}}
+#include <__bit/bit_cast.h>
diff --git a/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp b/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp
index ae5bfa7..4989237 100644
--- a/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp
+++ b/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp
@@ -18,6 +18,7 @@
// be listed in `UsingLibcxx.rst` in the documentation for the extension.
#include <algorithm>
+#include <bit> // bit_cast
#include <cstddef> // to_integer
#include <functional> // identity
#include <iterator>
@@ -165,13 +166,13 @@
void test_nontemplate_cast_wrappers()
{
-#if TEST_STD_VER >= 17
+#if TEST_STD_VER > 14
std::byte b{42};
std::to_integer<int>(b);
#endif
-#if TEST_STD_VER >= 20
- // std::bit_cast<unsigned int>(42);
+#if TEST_STD_VER > 17
+ std::bit_cast<unsigned int>(42);
#endif
#if TEST_STD_VER > 20
diff --git a/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp b/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp
index 5f3703c..7d513ef 100644
--- a/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp
+++ b/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp
@@ -19,6 +19,7 @@
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_NODISCARD
#include <algorithm>
+#include <bit> // bit_cast
#include <cstddef> // to_integer
#include <functional> // identity
#include <iterator>
@@ -323,14 +324,15 @@
void test_nontemplate_cast_wrappers()
{
-#if TEST_STD_VER >= 17
+#if TEST_STD_VER > 14
std::byte b{42};
// expected-warning-re@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
std::to_integer<int>(b);
#endif
-#if TEST_STD_VER >= 20
- // std::bit_cast<unsigned int>(42);
+#if TEST_STD_VER > 17
+ // expected-warning-re@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+ std::bit_cast<unsigned int>(42);
#endif
#if TEST_STD_VER > 20
diff --git a/test/std/language.support/support.limits/support.limits.general/bit.version.pass.cpp b/test/std/language.support/support.limits/support.limits.general/bit.version.pass.cpp
index 4ae4949..3c0f700 100644
--- a/test/std/language.support/support.limits/support.limits.general/bit.version.pass.cpp
+++ b/test/std/language.support/support.limits/support.limits.general/bit.version.pass.cpp
@@ -81,17 +81,11 @@
#elif TEST_STD_VER == 20
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should be defined in c++20"
-# endif
-# if __cpp_lib_bit_cast != 201806L
-# error "__cpp_lib_bit_cast should have the value 201806L in c++20"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_bit_cast
+# error "__cpp_lib_bit_cast should be defined in c++20"
+# endif
+# if __cpp_lib_bit_cast != 201806L
+# error "__cpp_lib_bit_cast should have the value 201806L in c++20"
# endif
# if !defined(_LIBCPP_VERSION)
@@ -123,17 +117,11 @@
#elif TEST_STD_VER > 20
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should be defined in c++2b"
-# endif
-# if __cpp_lib_bit_cast != 201806L
-# error "__cpp_lib_bit_cast should have the value 201806L in c++2b"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_bit_cast
+# error "__cpp_lib_bit_cast should be defined in c++2b"
+# endif
+# if __cpp_lib_bit_cast != 201806L
+# error "__cpp_lib_bit_cast should have the value 201806L in c++2b"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
index b7054f9..784e627 100644
--- a/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
+++ b/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
@@ -2264,17 +2264,11 @@
# error "__cpp_lib_bind_front should have the value 201907L in c++20"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should be defined in c++20"
-# endif
-# if __cpp_lib_bit_cast != 201806L
-# error "__cpp_lib_bit_cast should have the value 201806L in c++20"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_bit_cast
+# error "__cpp_lib_bit_cast should be defined in c++20"
+# endif
+# if __cpp_lib_bit_cast != 201806L
+# error "__cpp_lib_bit_cast should have the value 201806L in c++20"
# endif
# if !defined(_LIBCPP_VERSION)
@@ -3421,17 +3415,11 @@
# error "__cpp_lib_bind_front should have the value 201907L in c++2b"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should be defined in c++2b"
-# endif
-# if __cpp_lib_bit_cast != 201806L
-# error "__cpp_lib_bit_cast should have the value 201806L in c++2b"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_bit_cast
-# error "__cpp_lib_bit_cast should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_bit_cast
+# error "__cpp_lib_bit_cast should be defined in c++2b"
+# endif
+# if __cpp_lib_bit_cast != 201806L
+# error "__cpp_lib_bit_cast should have the value 201806L in c++2b"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/test/std/numerics/bit/bit.cast/bit_cast.compile.pass.cpp b/test/std/numerics/bit/bit.cast/bit_cast.compile.pass.cpp
new file mode 100644
index 0000000..b237515
--- /dev/null
+++ b/test/std/numerics/bit/bit.cast/bit_cast.compile.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: libcpp-no-concepts
+
+// <bit>
+//
+// template<class To, class From>
+// constexpr To bit_cast(const From& from) noexcept; // C++20
+
+// This test makes sure that std::bit_cast fails when any of the following
+// constraints are violated:
+//
+// (1.1) sizeof(To) == sizeof(From) is true;
+// (1.2) is_trivially_copyable_v<To> is true;
+// (1.3) is_trivially_copyable_v<From> is true.
+//
+// Also check that it's ill-formed when the return type would be
+// ill-formed, even though that is not explicitly mentioned in the
+// specification (but it can be inferred from the synopsis).
+
+#include <bit>
+#include <concepts>
+
+template<class To, class From>
+concept bit_cast_is_valid = requires(From from) {
+ { std::bit_cast<To>(from) } -> std::same_as<To>;
+};
+
+// Types are not the same size
+namespace ns1 {
+ struct To { char a; };
+ struct From { char a; char b; };
+ static_assert(!bit_cast_is_valid<To, From>);
+ static_assert(!bit_cast_is_valid<From&, From>);
+}
+
+// To is not trivially copyable
+namespace ns2 {
+ struct To { char a; To(To const&); };
+ struct From { char a; };
+ static_assert(!bit_cast_is_valid<To, From>);
+}
+
+// From is not trivially copyable
+namespace ns3 {
+ struct To { char a; };
+ struct From { char a; From(From const&); };
+ static_assert(!bit_cast_is_valid<To, From>);
+}
+
+// The return type is ill-formed
+namespace ns4 {
+ struct From { char a; char b; };
+ static_assert(!bit_cast_is_valid<char[2], From>);
+ static_assert(!bit_cast_is_valid<int(), From>);
+}
diff --git a/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp b/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp
new file mode 100644
index 0000000..06a8154
--- /dev/null
+++ b/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp
@@ -0,0 +1,263 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <bit>
+//
+// template<class To, class From>
+// constexpr To bit_cast(const From& from) noexcept; // C++20
+
+#include <array>
+#include <bit>
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+
+// std::bit_cast does not preserve padding bits, so if T has padding bits,
+// the results might not memcmp cleanly.
+template<bool HasUniqueObjectRepresentations = true, typename T>
+void test_roundtrip_through_buffer(T from) {
+ struct Buffer { char buffer[sizeof(T)]; };
+ Buffer middle = std::bit_cast<Buffer>(from);
+ T to = std::bit_cast<T>(middle);
+ Buffer middle2 = std::bit_cast<Buffer>(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<bool HasUniqueObjectRepresentations = true, typename T>
+void test_roundtrip_through_nested_T(T from) {
+ struct Nested { T x; };
+ static_assert(sizeof(Nested) == sizeof(T));
+
+ Nested middle = std::bit_cast<Nested>(from);
+ T to = std::bit_cast<T>(middle);
+ Nested middle2 = std::bit_cast<Nested>(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 <typename Intermediate, bool HasUniqueObjectRepresentations = true, typename T>
+void test_roundtrip_through(T from) {
+ static_assert(sizeof(Intermediate) == sizeof(T));
+
+ Intermediate middle = std::bit_cast<Intermediate>(from);
+ T to = std::bit_cast<T>(middle);
+ Intermediate middle2 = std::bit_cast<Intermediate>(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 <typename T>
+constexpr std::array<T, 10> generate_signed_integral_values() {
+ return {std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::min() + 1,
+ static_cast<T>(-2), static_cast<T>(-1),
+ static_cast<T>(0), static_cast<T>(1),
+ static_cast<T>(2), static_cast<T>(3),
+ std::numeric_limits<T>::max() - 1,
+ std::numeric_limits<T>::max()};
+}
+
+template <typename T>
+constexpr std::array<T, 6> generate_unsigned_integral_values() {
+ return {static_cast<T>(0), static_cast<T>(1),
+ static_cast<T>(2), static_cast<T>(3),
+ std::numeric_limits<T>::max() - 1,
+ std::numeric_limits<T>::max()};
+}
+
+bool tests() {
+ for (bool b : {false, true}) {
+ test_roundtrip_through_nested_T(b);
+ test_roundtrip_through_buffer(b);
+ test_roundtrip_through<char>(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<signed char>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ }
+
+ for (short i : generate_signed_integral_values<short>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ }
+
+ for (int i : generate_signed_integral_values<int>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<float>(i);
+ }
+
+ for (long i : generate_signed_integral_values<long>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ }
+
+ for (long long i : generate_signed_integral_values<long long>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<double>(i);
+ }
+
+ // Fundamental unsigned integer types
+ for (unsigned char i : generate_unsigned_integral_values<unsigned char>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ }
+
+ for (unsigned short i : generate_unsigned_integral_values<unsigned short>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ }
+
+ for (unsigned int i : generate_unsigned_integral_values<unsigned int>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<float>(i);
+ }
+
+ for (unsigned long i : generate_unsigned_integral_values<unsigned long>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ }
+
+ for (unsigned long long i : generate_unsigned_integral_values<unsigned long long>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<double>(i);
+ }
+
+ // Fixed width signed integer types
+ for (std::int32_t i : generate_signed_integral_values<std::int32_t>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<int>(i);
+ test_roundtrip_through<std::uint32_t>(i);
+ test_roundtrip_through<float>(i);
+ }
+
+ for (std::int64_t i : generate_signed_integral_values<std::int64_t>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<long long>(i);
+ test_roundtrip_through<std::uint64_t>(i);
+ test_roundtrip_through<double>(i);
+ }
+
+ // Fixed width unsigned integer types
+ for (std::uint32_t i : generate_unsigned_integral_values<std::uint32_t>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<int>(i);
+ test_roundtrip_through<std::int32_t>(i);
+ test_roundtrip_through<float>(i);
+ }
+
+ for (std::uint64_t i : generate_unsigned_integral_values<std::uint64_t>()) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<long long>(i);
+ test_roundtrip_through<std::int64_t>(i);
+ test_roundtrip_through<double>(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<float>::signaling_NaN(),
+ std::numeric_limits<float>::quiet_NaN()}) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<int>(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<double>::signaling_NaN(),
+ std::numeric_limits<double>::quiet_NaN()}) {
+ test_roundtrip_through_nested_T(i);
+ test_roundtrip_through_buffer(i);
+ test_roundtrip_through<long long>(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<long double>::signaling_NaN(),
+ std::numeric_limits<long double>::quiet_NaN()}) {
+ // Note that x86's `long double` has 80 value bits and 48 padding bits.
+ test_roundtrip_through_nested_T<false>(i);
+ test_roundtrip_through_buffer<false>(i);
+
+ // On arm64 on Apple platforms, long double is just double, so we don't
+ // test against int128, but instead against double itself. Otherwise,
+ // we test against int128 if we have those types available.
+#if defined(__aarch64__) && defined(__APPLE__)
+# define LONG_DOUBLE_IS_DOUBLE
+#endif
+
+#if defined(LONG_DOUBLE_IS_DOUBLE)
+ test_roundtrip_through<double, false>(i);
+#elif !defined(_LIBCPP_HAS_NO_INT128)
+ test_roundtrip_through<__int128_t, false>(i);
+ test_roundtrip_through<__uint128_t, false>(i);
+#endif
+ }
+
+ 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<Nested>(from);
+ int to = std::bit_cast<int>(middle);
+ assert(from == to);
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(basic_constexpr_test());
+ return 0;
+}
diff --git a/utils/generate_feature_test_macro_components.py b/utils/generate_feature_test_macro_components.py
index 4153790..f6527a9 100755
--- a/utils/generate_feature_test_macro_components.py
+++ b/utils/generate_feature_test_macro_components.py
@@ -156,7 +156,6 @@
"name": "__cpp_lib_bit_cast",
"values": { "c++20": 201806 },
"headers": ["bit"],
- "unimplemented": True,
}, {
"name": "__cpp_lib_bitops",
"values": { "c++20": 201907 },