diff --git a/stl/inc/bit b/stl/inc/bit index 8bee282c56e..86d4b416842 100644 --- a/stl/inc/bit +++ b/stl/inc/bit @@ -25,6 +25,16 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN enum class endian { little = 0, big = 1, native = little }; +#ifdef __cpp_lib_bit_cast // TRANSITION, VSO-1041044 +template , is_trivially_copyable<_To>, + is_trivially_copyable<_From>>, + int> = 0> +_NODISCARD constexpr _To bit_cast(const _From& _Val) noexcept { + return __builtin_bit_cast(_To, _Val); +} +#endif // TRANSITION, VSO-1041044 + #ifdef __cpp_lib_bitops // TRANSITION, VSO-1020212 template inline constexpr bool _Is_standard_unsigned_integer = diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 0c815149ecd..4198e9499fb 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -140,6 +140,7 @@ // P0457R2 starts_with()/ends_with() For basic_string/basic_string_view // P0458R2 contains() For Ordered And Unordered Associative Containers // P0463R1 endian +// P0476R2 bit_cast // P0482R6 Library Support For char8_t // (mbrtoc8 and c8rtomb not yet implemented) // P0487R1 Fixing operator>>(basic_istream&, CharT*) @@ -1067,6 +1068,10 @@ #define __cpp_lib_atomic_float 201711L #define __cpp_lib_bind_front 201907L +#ifndef __EDG__ // TRANSITION, VSO-1041044 +#define __cpp_lib_bit_cast 201806L +#endif // __EDG__ + #if defined(__clang__) || defined(__EDG__) #define __cpp_lib_bitops 201907L #else // ^^^ Clang and EDG / MSVC vvv diff --git a/tests/std/test.lst b/tests/std/test.lst index 873da5cdf40..9c87885c24c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -209,6 +209,7 @@ tests\P0357R3_supporting_incomplete_types_in_reference_wrapper tests\P0414R2_shared_ptr_for_arrays tests\P0426R1_constexpr_char_traits tests\P0433R2_deduction_guides +tests\P0476R2_bit_cast tests\P0487R1_fixing_operator_shl_basic_istream_char_pointer tests\P0513R0_poisoning_the_hash tests\P0553R4_bit_rotating_and_counting_functions diff --git a/tests/std/tests/P0476R2_bit_cast/env.lst b/tests/std/tests/P0476R2_bit_cast/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0476R2_bit_cast/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0476R2_bit_cast/test.cpp b/tests/std/tests/P0476R2_bit_cast/test.cpp new file mode 100644 index 00000000000..bea934d170e --- /dev/null +++ b/tests/std/tests/P0476R2_bit_cast/test.cpp @@ -0,0 +1,266 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifdef __EDG__ // TRANSITION, VSO-1041044 +int main() {} +#else // __EDG__ ^^^ / vvv !__EDG__ + +#include +#include +#include +#include +#include +#include +#include +#include + +// structure sizing depends on a packing of 8, which is the default + +union test_union_1 { + char a; + int b; +}; +union test_union_2 { + char a; + float b; +}; + +struct middle_class_1 { + double d; + virtual void a_member_function_1() {} +}; + +struct middle_class_2 { + int a; + virtual void a_member_function_2() {} +}; + +struct derived_class : middle_class_1, middle_class_2 { + virtual void a_member_function_2() override {} +}; + +struct test_struct_1 { + char a; + // char[3] + int b; + short c; + // char[6] + double d; + + void a_member_function() {} +}; + +struct test_struct_2 { + short a; + // char[2] + float b; + float c; + int d; + unsigned int e; + char f; + // char[1] + short g; + + void a_member_function() {} +}; + +struct test_struct_3_member_fn_pointer { + char a; + // char[3] + int b; + short c; + char d[2]; + void (test_struct_1::*fn)(); +}; + +struct test_struct_4_large_member_fn_pointer { + char a; + // char[3] + int b; + double c; + double d; + void (derived_class::*fn)(); // "large" member fn pointers are aligned to 8 on both x64 and x86 +}; + +struct test_struct_5_struct { + char a; + // char[3] + int b; + double c; + double d; + void* e; + size_t f; +}; + +struct test_struct_6 { + char a; + // char[3] + int b; + int c; + // char[4] on x64 + void* v; +}; + +struct test_struct_7_member_fn_pointer { + char a; + // char[3] + int b; + int c; + void (test_struct_1::*fn)(); +}; + +struct test_struct_1_not_trivially_copyable { + char a; + // char[3] + int b; + short c; + // char[2] + double d; + + test_struct_1_not_trivially_copyable& operator=(const test_struct_1_not_trivially_copyable&) { + return *this; + } +}; + +#pragma pack(push, 1) +struct test_struct_1_packed { + char a; + int b; + short c; + double d; +}; +struct test_struct_2_packed { + short a; + float b; + float c; + int d; + unsigned int e; + char f; + short g; +}; +#pragma pack(pop) + +static_assert(sizeof(test_struct_1) == sizeof(test_struct_2)); + +template +constexpr bool bit_cast_invocable = false; + +template +constexpr bool bit_cast_invocable(std::declval()))>> = true; + +template (test_union_2{}), zero)> +constexpr bool bit_cast_is_constexpr_union(int) { + return true; +} + +constexpr bool bit_cast_is_constexpr_union(long) { + return false; +} + +template (nullptr), zero)> +constexpr bool bit_cast_is_constexpr_pointer(int) { + return true; +} + +constexpr bool bit_cast_is_constexpr_pointer(long) { + return false; +} + +template (&test_struct_2::a_member_function), zero)> +constexpr bool bit_cast_is_constexpr_member_fn_pointer(int) { + return true; +} + +constexpr bool bit_cast_is_constexpr_member_fn_pointer(long) { + return false; +} + +template (test_struct_3_member_fn_pointer{}), zero)> +constexpr bool bit_cast_is_constexpr_pmf_datamember(int) { + return true; +} + +constexpr bool bit_cast_is_constexpr_pmf_datamember(long) { + return false; +} + +template (test_struct_4_large_member_fn_pointer{}), zero)> +constexpr bool bit_cast_is_constexpr_large_member_fn_pointer(int) { + return true; +} + +constexpr bool bit_cast_is_constexpr_large_member_fn_pointer(long) { + return false; +} + +template +void zero_initialized_round_trip() { + From before{}; + To middle = std::bit_cast(before); + assert(memcmp(&before, &middle, sizeof(From)) == 0); + From after = std::bit_cast(middle); + assert(memcmp(&before, &after, sizeof(From)) == 0); +} + +constexpr bool test_float() { + unsigned int as_int = std::bit_cast(0x0.000002p-126f); + assert(as_int == 1); + assert(std::bit_cast(as_int) == 0x0.000002p-126f); + as_int = std::bit_cast(0x1.1p1f); + assert(as_int == 0x40080000); + assert(std::bit_cast(as_int) == 0x1.1p1f); + as_int = std::bit_cast(0x0.0p0f); + assert(as_int == 0); + assert(std::bit_cast(as_int) == 0x0.0p0f); + if (!std::is_constant_evaluated()) { + assert(std::signbit(std::bit_cast(as_int)) == false); + } + as_int = std::bit_cast(-0x0.0p0f); + assert(as_int == 0x80000000); + assert(std::bit_cast(as_int) == -0x0.0p0f); + if (!std::is_constant_evaluated()) { + assert(std::signbit(std::bit_cast(as_int)) == true); + } + // signaling nan + as_int = 0x7fc00001; + float snan = std::bit_cast(as_int); + assert(as_int == std::bit_cast(snan)); + as_int = std::bit_cast(std::numeric_limits::infinity()); + assert(as_int == 0x7f800000); + assert(std::bit_cast(as_int) == std::numeric_limits::infinity()); + return true; +} + +int main() { + static_assert(!bit_cast_invocable); + static_assert(!bit_cast_invocable); + static_assert(!bit_cast_invocable); + static_assert(!bit_cast_invocable); + static_assert(bit_cast_invocable); + static_assert(bit_cast_invocable); + static_assert(bit_cast_invocable); + static_assert(bit_cast_invocable); + static_assert(bit_cast_invocable); + + // tests for conditions on constexprness + static_assert(!bit_cast_is_constexpr_union(0)); + static_assert(!bit_cast_is_constexpr_pointer(0)); + static_assert(!bit_cast_is_constexpr_member_fn_pointer(0)); + static_assert(!bit_cast_is_constexpr_pmf_datamember(0)); + static_assert(!bit_cast_is_constexpr_large_member_fn_pointer(0)); + + zero_initialized_round_trip(); + zero_initialized_round_trip(); + zero_initialized_round_trip(); + zero_initialized_round_trip(); + zero_initialized_round_trip(); + zero_initialized_round_trip(); + zero_initialized_round_trip(); + zero_initialized_round_trip(); + + assert(test_float()); + static_assert(test_float()); +} +#endif // __EDG__ diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp index 6c17c18ad45..a7be3b63f0d 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp @@ -1513,6 +1513,20 @@ STATIC_ASSERT(__cpp_lib_bind_front == 201907L); #endif #endif +#if CXX20_MODE && !defined(__EDG__) // TRANSITION, VSO-1041044 +#ifndef __cpp_lib_bit_cast +#error BOOM +#elif __cpp_lib_bit_cast != 201806L +#error BOOM +#else +STATIC_ASSERT(__cpp_lib_bit_cast == 201806L); +#endif +#else +#ifdef __cpp_lib_bit_cast +#error BOOM +#endif +#endif + #if CXX20_MODE && (defined(__clang__) || defined(__EDG__)) // TRANSITION, VSO-1020212 #ifndef __cpp_lib_bitops #error BOOM