[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 },