diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -29,7 +29,7 @@ "`P0019R8 `__","LWG","Atomic Ref","Rapperswil","","" "`P0458R2 `__","LWG","Checking for Existence of an Element in Associative Containers","Rapperswil","|Complete|","" "`P0475R1 `__","LWG","LWG 2511: guaranteed copy elision for piecewise construction","Rapperswil","|Complete|","" -"`P0476R2 `__","LWG","Bit-casting object representations","Rapperswil","","" +"`P0476R2 `__","LWG","Bit-casting object representations","Rapperswil","|Complete|","12.0" "`P0528R3 `__","CWG","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","Rapperswil","","" "`P0542R5 `__","CWG","Support for contract based programming in C++","Rapperswil","*Removed in Cologne*","n/a" "`P0556R3 `__","LWG","Integral power-of-2 operations","Rapperswil","|Complete|","9.0" diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -190,7 +190,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_bind_front`` *unimplemented* ------------------------------------------------- ----------------- - ``__cpp_lib_bit_cast`` *unimplemented* + ``__cpp_lib_bit_cast`` ``201806L`` ------------------------------------------------- ----------------- ``__cpp_lib_bitops`` *unimplemented* ------------------------------------------------- ----------------- diff --git a/libcxx/include/bit b/libcxx/include/bit --- a/libcxx/include/bit +++ b/libcxx/include/bit @@ -14,6 +14,9 @@ bit synopsis namespace std { + // [bit.cast], bit_cast + template + constexpr To bit_cast(const From& from) noexcept; // C++20 // [bit.pow.two], integral powers of 2 template @@ -357,6 +360,17 @@ #if _LIBCPP_STD_VER > 17 +template && + is_trivially_copyable_v<_FromType> +>> +_LIBCPP_INLINE_VISIBILITY _LIBCPP_NODISCARD_EXT +constexpr _ToType bit_cast(_FromType const& __from) noexcept +{ + return __builtin_bit_cast(_ToType, __from); +} + template _LIBCPP_INLINE_VISIBILITY constexpr enable_if_t<__bitop_unsigned_integer<_Tp>::value, _Tp> diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/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_NO_HAS_CHAR8_T) diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/bit.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/bit.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/bit.version.pass.cpp +++ b/libcxx/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/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -2262,17 +2262,11 @@ # endif # 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) @@ -3474,17 +3468,11 @@ # endif # 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/libcxx/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp b/libcxx/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/numerics/bit/bit.cast/bit_cast.pass.cpp @@ -0,0 +1,222 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +template +void test_buffer_roundtrip(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(std::memcmp(&from, &middle, sizeof(T)) == 0); + assert(std::memcmp(&to, &middle, sizeof(T)) == 0); + assert(std::memcmp(&from, &to, sizeof(T)) == 0); + assert(std::memcmp(&middle, &middle2, sizeof(Buffer)) == 0); +} + +template +void test_nested_roundtrip(T from) { + struct Nested { T x; }; + Nested middle = std::bit_cast(from); + T to = std::bit_cast(middle); + Nested middle2 = std::bit_cast(to); + + assert(std::memcmp(&from, &middle, sizeof(T)) == 0); + assert(std::memcmp(&to, &middle, sizeof(T)) == 0); + assert(std::memcmp(&from, &to, sizeof(T)) == 0); + assert(std::memcmp(&middle, &middle2, sizeof(Nested)) == 0); +} + +template +void test_intermediate_roundtrip(T from) { + Intermediate middle = std::bit_cast(from); + T to = std::bit_cast(middle); + Intermediate middle2 = std::bit_cast(to); + + assert(std::memcmp(&from, &middle, sizeof(T)) == 0); + assert(std::memcmp(&to, &middle, sizeof(T)) == 0); + assert(std::memcmp(&from, &to, sizeof(T)) == 0); + assert(std::memcmp(&middle, &middle2, sizeof(Intermediate)) == 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_nested_roundtrip(b); + test_buffer_roundtrip(b); + test_intermediate_roundtrip(b); + } + + for (char c : {'\0', 'a', 'b', 'c', 'd'}) { + test_nested_roundtrip(c); + test_buffer_roundtrip(c); + test_intermediate_roundtrip(c); + } + + // Fundamental signed integer types + for (short i : generate_signed_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + } + + for (int i : generate_signed_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + } + + for (long i : generate_signed_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + } + + for (long long i : generate_signed_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + } + + // Fundamental unsigned integer types + for (unsigned short i : generate_unsigned_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + } + + for (unsigned int i : generate_unsigned_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + } + + for (unsigned long i : generate_unsigned_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + } + + for (unsigned long long i : generate_unsigned_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + } + + // Fixed width signed integer types + for (std::int32_t i : generate_signed_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + test_intermediate_roundtrip(i); + } + + for (std::int64_t i : generate_signed_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + test_intermediate_roundtrip(i); + } + + // Fixed width unsigned integer types + for (std::uint32_t i : generate_unsigned_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + test_intermediate_roundtrip(i); + } + + for (std::uint64_t i : generate_unsigned_integral_values()) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(i); + test_intermediate_roundtrip(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_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(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_nested_roundtrip(i); + test_buffer_roundtrip(i); + test_intermediate_roundtrip(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()}) { + test_nested_roundtrip(i); + test_buffer_roundtrip(i); + } + + 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; +} diff --git a/libcxx/test/std/numerics/bit/bit.cast/bit_cast.verify.cpp b/libcxx/test/std/numerics/bit/bit.cast/bit_cast.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/numerics/bit/bit.cast/bit_cast.verify.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// 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 is true; +// (1.3) is_trivially_copyable_v is true. + +#include +#include +#include + +#include "test_macros.h" + +int main(int, char**) +{ + // Not the same size + { + struct To { char a; }; + struct From { char a; char b; }; + From from; + (void)std::bit_cast(from); // expected-error {{no matching function for call to 'bit_cast'}} + } + + // To is not trivially copyable + { + struct To { char a; To(To const& x) : a(x.a) { } }; + struct From { char a; }; + From from; + (void)std::bit_cast(from); // expected-error {{no matching function for call to 'bit_cast'}} + } + + // From is not trivially copyable + { + struct To { char a; }; + struct From { char a; From() { } From(From const& x) : a(x.a) { } }; + From from; + (void)std::bit_cast(from); // expected-error {{no matching function for call to 'bit_cast'}} + } + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -128,7 +128,6 @@ "name": "__cpp_lib_bit_cast", "values": { "c++20": 201806 }, "headers": ["bit"], - "unimplemented": True, }, { "name": "__cpp_lib_bitops", "values": { "c++20": 201907 },