diff --git a/stl/inc/algorithm b/stl/inc/algorithm index fe23a33781e..b6a009c14d7 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1363,7 +1363,7 @@ _NODISCARD _CONSTEXPR20 _FwdIt partition_point(_FwdIt _First, _FwdIt _Last, _Pr template _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) { // compare [_First1, ...) to [_First2, _Last2) using _Pred - if constexpr (decltype(_Equal_memcmp_is_safe(_First1, _First2, _Pred))::value) { + if constexpr (_Equal_memcmp_is_safe<_InIt1, _InIt2, _Pr>) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -1384,8 +1384,8 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F return true; } #else // ^^^ _HAS_IF_CONSTEXPR ^^^ // vvv !_HAS_IF_CONSTEXPR vvv -template -bool _Equal_rev_pred_unchecked1(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred, false_type) { +template , int> = 0> +bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) { // compare [_First1, ...) to [_First2, _Last2) using _Pred, no special optimization for (; _First2 != _Last2; ++_First1, (void) ++_First2) { if (!_Pred(*_First1, *_First2)) { @@ -1396,20 +1396,14 @@ bool _Equal_rev_pred_unchecked1(_InIt1 _First1, _InIt2 _First2, const _InIt2 _La return true; } -template -bool _Equal_rev_pred_unchecked1(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr, true_type) { +template , int> = 0> +bool _Equal_rev_pred_unchecked(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr) { // compare [_First1, ...) to [_First2, _Last2), memcmp optimization const auto _First1_ch = reinterpret_cast(_First1); const auto _First2_ch = reinterpret_cast(_First2); const auto _Count = static_cast(reinterpret_cast(_Last2) - _First2_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } - -template -bool _Equal_rev_pred_unchecked(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) { - // compare [_First1, ...) to [_First2, _Last2) using _Pred, choose optimization - return _Equal_rev_pred_unchecked1(_First1, _First2, _Last2, _Pred, _Equal_memcmp_is_safe(_First1, _First2, _Pred)); -} #endif // _HAS_IF_CONSTEXPR // FUNCTION TEMPLATE search @@ -1916,11 +1910,12 @@ namespace ranges { // compare [_First1, ...) to [_First2, _Last2) constexpr bool _Both_identity = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>; // Performance note: teach _Equal_memcmp_is_safe about contiguous iterators and ranges::equal_to - if constexpr (_Both_identity && decltype(_Equal_memcmp_is_safe(_First1, _First2, _Pred))::value) { + if constexpr (_Both_identity && _Equal_memcmp_is_safe<_It1, _It2, _Pr>) { if (!_STD is_constant_evaluated()) { - const auto _First1_ch = reinterpret_cast(_First1); - const auto _First2_ch = reinterpret_cast(_First2); - const auto _Count = static_cast(reinterpret_cast(_Last2) - _First2_ch); + const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); + const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); + const auto _Count = + static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } } diff --git a/stl/inc/xstring b/stl/inc/xstring index 8aaf1dbfd8e..9ec4506e7f4 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -549,9 +549,10 @@ struct _Char_traits_lt { } }; +// library-provided char_traits::eq behaves like equal_to<_Elem> +// TRANSITION: This should not be activated for user-defined specializations of char_traits template -struct _Equal_memcmp_is_safe_helper<_Elem, _Elem, _Char_traits_eq>> - : _Equal_memcmp_is_safe_helper<_Elem, _Elem, equal_to<>>::type {}; // builtin char_traits::eq acts like equal_to<> +_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem, _Elem, _Char_traits_eq>> = true; template using _Traits_ch_t = typename _Traits::char_type; diff --git a/stl/inc/xutility b/stl/inc/xutility index 252c74c7b50..fc469dfcafd 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4657,55 +4657,93 @@ _FwdIt fill_n(_ExPo&&, _FwdIt _Dest, _Diff _Count_raw, const _Ty& _Val) noexcept #endif // _HAS_CXX17 // FUNCTION TEMPLATE equal + +// _Can_memcmp_elements<_Elem1, _Elem2> reports whether `_Elem1 == _Elem2` can be optimized to memcmp. +// Here, _Elem1 and _Elem2 aren't top-level const, because we remove_const_t before using _Can_memcmp_elements. + +// Integral types are eligible for memcmp in very specific cases. +// * They must be the same size. (`int == long` is eligible; `int == long long` isn't.) +// * They can't be bool. (Not even `bool == bool`; we're concerned about representations other than 0 and 1.) +// * They can't be volatile. +// * Finally, the usual arithmetic conversions must preserve bit patterns. (This is true for `int == unsigned int`, +// but false for `short == unsigned short`.) +template && !is_volatile_v<_Elem1> // + && _Is_nonbool_integral<_Elem2> && !is_volatile_v<_Elem2>> +_INLINE_VAR constexpr bool _Can_memcmp_elements = static_cast<_Elem1>(-1) == static_cast<_Elem2>(-1); + +#ifdef __cpp_lib_byte +// Allow memcmping std::byte. +// inline is required here as explicit specializations of variable templates are problematic in C++14. +// However, std::byte is C++17 and above so we are safe. +template <> +inline constexpr bool _Can_memcmp_elements = true; +#endif // __cpp_lib_byte + +// Pointer elements are eligible for memcmp when they point to the same type, ignoring cv-qualification. +// This handles pointers to object types, pointers to void, and pointers to function types. +// Performance note: This doesn't attempt to handle `object* == void*`. +template +_INLINE_VAR constexpr bool _Can_memcmp_elements<_Ty1*, _Ty2*, false> = is_same_v, remove_cv_t<_Ty2>>; + template -struct _Value_equality_is_bitwise_equality : bool_constant(-1) == static_cast<_Elem2>(-1)> { - // Tests whether the usual arithmetic conversions will preserve the bit-pattern when promoting to int - // e.g. short == unsigned short -> false - // int == unsigned int -> true -}; +_INLINE_VAR constexpr bool _Can_memcmp_elements<_Elem1, _Elem2, false> = false; +// _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr> reports whether `_Pr(_Elem1, _Elem2)` returns +// `_Elem1 == _Elem2` without performing any type conversions that could affect the memcmp optimization. +// (It isn't concerned with whether the elements are actually eligible.) +// Again, _Elem1 and _Elem2 aren't top-level const here. template -struct _Equal_memcmp_is_safe_helper : false_type { - // determines whether it is safe to call memcmp to compare things; defaults to false -}; +_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp = false; +// When all of the types are the same, equal_to<_Elem> is effectively transparent. +// Performance note: This doesn't attempt to handle equal_to(int, long) or equal_to(T*, const T*). +template +_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem, _Elem, equal_to<_Elem>> = true; + +// equal_to<> is transparent. template -struct _Equal_memcmp_is_safe_helper<_Elem1, _Elem2, equal_to<>> - : bool_constant && _Is_nonbool_integral<_Elem2>>, - negation>, negation>, - // order matters here: being integral is a precondition of _Value_equality_is_bitwise_equality - _Value_equality_is_bitwise_equality<_Elem1, _Elem2>>> { - // allow memcmping same-size integral non-bool non-volatile bitwise types using equal_to<> -}; +_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, equal_to<>> = true; +#ifdef __cpp_lib_concepts +// ranges::equal_to is also transparent. template -struct _Equal_memcmp_is_safe_helper<_Elem1*, _Elem2*, equal_to<>> - : is_same, remove_cv_t<_Elem2>>::type {}; // allow memcmping pointers-to-cv-T with equal_to<> +_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _RANGES equal_to> = true; +#endif // __cpp_lib_concepts -#ifdef __cpp_lib_byte -template <> -struct _Equal_memcmp_is_safe_helper> : true_type {}; // allow memcmping std::byte -#endif // __cpp_lib_byte +// _Can_memcmp_elements_with_pred<_Elem1, _Elem2, _Pr> reports whether the memcmp optimization is applicable, +// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element types.) +// _Elem1 and _Elem2 aren't top-level const here. +template +_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = _Can_memcmp_elements<_Elem1, _Elem2> // + && _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr>; -template -struct _Equal_memcmp_is_safe_helper<_Elem, _Elem, equal_to<_Elem>> - : _Equal_memcmp_is_safe_helper<_Elem, _Elem, equal_to<>>::type { - // treat equal_to with exact T as equal_to<> this is safe because we only activate the optimization for builtin - // _Elem (and std::byte) -}; +// _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. +// (Without concepts, this detection is limited, which will limit when we can activate the memcmp optimization.) +#ifdef __cpp_lib_concepts +// When concepts are available, we can detect arbitrary contiguous iterators. +template +_INLINE_VAR constexpr bool _Iterators_are_contiguous = contiguous_iterator<_Iter1> // + && contiguous_iterator<_Iter2>; +#else // ^^^ defined(__cpp_lib_concepts) ^^^ / vvv !defined(__cpp_lib_concepts) vvv +// When concepts aren't available, we can detect pointers. (Iterators should be unwrapped before using this.) +template +_INLINE_VAR constexpr bool _Iterators_are_contiguous = conjunction_v, is_pointer<_Iter2>>; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + +// _Equal_memcmp_is_safe<_Iter1, _Iter2, _Pr> reports whether we can activate the memcmp optimization +// for arbitrary iterators and predicates. +// It ignores top-level constness on the iterators and on the elements. template -false_type _Equal_memcmp_is_safe(const _Iter1&, const _Iter2&, const _Pr&) { - // return equal optimization category for arbitrary iterators - return {}; -} +_INLINE_VAR constexpr bool _Equal_memcmp_is_safe_helper = _Iterators_are_contiguous<_Iter1, _Iter2> // + && _Can_memcmp_elements_with_pred>>, + remove_const_t>>, _Pr>; -template -typename _Equal_memcmp_is_safe_helper, remove_const_t<_Obj2>, _Pr>::type _Equal_memcmp_is_safe( - _Obj1* const&, _Obj2* const&, const _Pr&) { // return equal optimization category for pointers - return {}; -} +template +_INLINE_VAR constexpr bool _Equal_memcmp_is_safe = + _Equal_memcmp_is_safe_helper, remove_const_t<_Iter2>, _Pr>; #if _HAS_IF_CONSTEXPR template @@ -4715,7 +4753,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co auto _UFirst1 = _Get_unwrapped(_First1); const auto _ULast1 = _Get_unwrapped(_Last1); auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_InIt1>(_UFirst1, _ULast1)); - if constexpr (decltype(_Equal_memcmp_is_safe(_UFirst1, _UFirst2, _Pred))::value) { + if constexpr (_Equal_memcmp_is_safe) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4736,8 +4774,8 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co return true; } #else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv -template -bool _Equal_unchecked1(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr _Pred, false_type) { +template , int> = 0> +bool _Equal_unchecked(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr _Pred) { // compare [_First1, _Last1) to [_First2, ...) using _Pred, no special optimization for (; _First1 != _Last1; ++_First1, (void) ++_First2) { if (!_Pred(*_First1, *_First2)) { @@ -4748,8 +4786,8 @@ bool _Equal_unchecked1(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr return true; } -template -bool _Equal_unchecked1(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr, true_type) { +template , int> = 0> +bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr) { // compare [_First1, _Last1) to [_First2, ...), memcmp optimization const auto _First1_ch = reinterpret_cast(_First1); const auto _First2_ch = reinterpret_cast(_First2); @@ -4757,12 +4795,6 @@ bool _Equal_unchecked1(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _ return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } -template -bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred) { - // compare [_First1, _Last1) to [_First2, ...) using _Pred, choose optimization - return _Equal_unchecked1(_First1, _Last1, _First2, _Pred, _Equal_memcmp_is_safe(_First1, _First2, _Pred)); -} - template _NODISCARD bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred) { // compare [_First1, _Last1) to [_First2, ...) using _Pred diff --git a/tests/std/tests/VSO_0180469_ptr_cat/test.cpp b/tests/std/tests/VSO_0180469_ptr_cat/test.cpp index 78d268e864d..e0747b48caf 100644 --- a/tests/std/tests/VSO_0180469_ptr_cat/test.cpp +++ b/tests/std/tests/VSO_0180469_ptr_cat/test.cpp @@ -4,11 +4,19 @@ #define _SILENCE_CXX20_IS_POD_DEPRECATION_WARNING #include +#include +#include #include #include #include #include #include +#include + +#ifdef __cpp_lib_concepts +#include +#include +#endif // __cpp_lib_concepts using namespace std; @@ -206,53 +214,124 @@ void ptr_cat_test_cases() { test_ptr_cat<0, bool_enum, bool_enum_class>(); test_ptr_cat<0, bool_enum_class, bool_enum>(); - // Bool always generates code when you inspect it, so special case it to the general thing: + // bool always generates code when you inspect it, so special case it to the general thing: test_ptr_cat<0, bool, char>(); test_ptr_cat<0, char, bool>(); test_ptr_cat<0, bool_enum, char>(); } -template -void test_case_Equal_memcmp_is_safe_helper() { - STATIC_ASSERT(decltype(_Equal_memcmp_is_safe(declval(), declval(), declval()))::value == Expected); +template +void test_case_Equal_memcmp_is_safe_comparator() { + // Default case + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + // Adding const should not change the answer + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + // Top level const should not change the answer + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + // Adding volatile anywhere should explode + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); + +#ifdef __cpp_lib_concepts + // contiguous iterators should not change the answer + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename vector::iterator, Pr> == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, typename vector::const_iterator, + Pr> == Expected); + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename array::iterator, Pr> == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + typename array::const_iterator, Pr> == Expected); + // Mixing contiguous iterators should not change the answer + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename vector::const_iterator, + Pr> == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + typename vector::const_iterator, Pr> == Expected); + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename array::iterator, Pr> == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename array::const_iterator, + Pr> == Expected); + // span iterators are contiguous + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == Expected); + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == Expected); + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, + Pr> == Expected); + // contiguous iterators to volatile should explode + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == false); + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, + Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, + Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, + Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, + Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + typename span::iterator, Pr> == false); +#endif // __cpp_lib_concepts + + // Non-contiguous iterators should explode + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename list::iterator, Pr> == false); } template void test_case_Equal_memcmp_is_safe() { - // Default case - test_case_Equal_memcmp_is_safe_helper>(); - // Adding const should not change the answer - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - // Adding volatile anywhere should explode - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); - - // Iterators that are not pointers should explode - test_case_Equal_memcmp_is_safe_helper::iterator, typename list::iterator, - equal_to<>>(); + test_case_Equal_memcmp_is_safe_comparator, Elem1, Elem2, equal_to>(); + test_case_Equal_memcmp_is_safe_comparator>(); +#ifdef __cpp_lib_concepts + test_case_Equal_memcmp_is_safe_comparator(); +#endif // __cpp_lib_concepts // equal_to< some other T > should explode - test_case_Equal_memcmp_is_safe_helper>>(); + STATIC_ASSERT(_Equal_memcmp_is_safe>> == false); // Non equal_to comparison functions should explode auto lambda = [](Elem1*, Elem2*) { return false; }; - test_case_Equal_memcmp_is_safe_helper(); + STATIC_ASSERT(_Equal_memcmp_is_safe == false); // equal_to should not explode - test_case_Equal_memcmp_is_safe_helper, Elem1*, Elem2*, equal_to>(); + STATIC_ASSERT(_Equal_memcmp_is_safe> == (Expected && is_same_v) ); // But again, not volatile - test_case_Equal_memcmp_is_safe_helper>(); - test_case_Equal_memcmp_is_safe_helper>(); + STATIC_ASSERT(_Equal_memcmp_is_safe> == false); + STATIC_ASSERT(_Equal_memcmp_is_safe> == false); } void equal_safe_test_cases() { @@ -277,8 +356,8 @@ void equal_safe_test_cases() { // signedness must be the same if usual arithmetic conversions are not bits-preserving test_case_Equal_memcmp_is_safe, char, signed char>(); test_case_Equal_memcmp_is_safe, signed char, char>(); - test_case_Equal_memcmp_is_safe, char, unsigned char>(); - test_case_Equal_memcmp_is_safe, unsigned char, char>(); + test_case_Equal_memcmp_is_safe, char, unsigned char>(); + test_case_Equal_memcmp_is_safe, unsigned char, char>(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); @@ -288,7 +367,14 @@ void equal_safe_test_cases() { test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); // memcmp is not safe for bool types @@ -305,13 +391,19 @@ void equal_safe_test_cases() { // No user-defined types test_case_Equal_memcmp_is_safe(); +#ifdef __cpp_lib_byte + // memcmp is safe for std::byte, but it can't be compared to integral types + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); +#endif // __cpp_lib_byte + // Pointers to cv T are OK (they *point to* volatile stuff, they aren't volatile themselves) typedef void (*funcptr_t)(int); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe();