diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c2c80b44701..1bebe70c3c0 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3993,7 +3993,7 @@ namespace ranges { #endif // __cpp_lib_is_constant_evaluated { const auto _Distance = static_cast(_ULast - _UFirst); - _CSTD memset(_UFirst, static_cast(_Value), _Distance); + _Fill_memset(_UFirst, _Value, _Distance); _UFirst += _Distance; _Seek_wrapped(_First, _UFirst); return _First; @@ -4032,7 +4032,7 @@ namespace ranges { if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - _CSTD memset(_UFirst, static_cast(_Value), static_cast(_Count)); + _Fill_memset(_UFirst, _Value, static_cast(_Count)); _UFirst += _Count; _Seek_wrapped(_First, _UFirst); // no need to move since _UFirst is a pointer return _First; diff --git a/stl/inc/memory b/stl/inc/memory index 67b1cc410d3..02595fa3426 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -134,7 +134,7 @@ _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, if (0 < _Count) { auto _UFirst = _Get_unwrapped_n(_First, _Count); if constexpr (_Fill_memset_is_safe<_Unwrapped_n_t, _Tval>) { - _CSTD memset(_UFirst, static_cast(_Val), _Count); + _Fill_memset(_UFirst, _Val, _Count); _UFirst += _Count; } else { _Uninitialized_backout<_Unwrapped_n_t> _Backout{_UFirst}; @@ -167,7 +167,7 @@ template _NoThrowFwdIt _Uninitialized_fill_n_unchecked1( const _NoThrowFwdIt _First, const _Diff _Count, const _Tval& _Val, true_type) { // copy _Count copies of _Val to raw _First, memset optimization - _CSTD memset(_First, static_cast(_Val), _Count); + _Fill_memset(_First, _Val, _Count); return _First + _Count; } @@ -1653,7 +1653,7 @@ void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, } _Guard._Target = nullptr; } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty>) { - _CSTD memset(_Out, static_cast(_Val), _Size); + _Fill_memset(_Out, _Val, _Size); } else { _Uninitialized_rev_destroying_backout _Backout{_Out}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { @@ -1976,7 +1976,7 @@ void _Uninitialized_fill_multidimensional_n_al(_Ty* const _Out, const size_t _Si } _Guard._Target = nullptr; } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { - _CSTD memset(_Out, static_cast(_Val), _Size); + _Fill_memset(_Out, _Val, _Size); } else { _Uninitialized_rev_destroying_backout_al _Backout{_Out, _Al}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { diff --git a/stl/inc/xmemory b/stl/inc/xmemory index abb94ba7ac0..af502b46e7a 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1705,7 +1705,7 @@ _Alloc_ptr_t<_Alloc> _Uninitialized_fill_n( // copy _Count copies of _Val to raw _First, using _Al using _Ty = typename _Alloc::value_type; if constexpr (_Fill_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { - _CSTD memset(_Unfancy(_First), static_cast(_Val), static_cast(_Count)); + _Fill_memset(_Unfancy(_First), _Val, static_cast(_Count)); return _First + _Count; } else { _Uninitialized_backout_al<_Alloc> _Backout{_First, _Al}; @@ -1733,7 +1733,7 @@ template _Alloc_ptr_t<_Alloc> _Uninit_alloc_fill_n1(_Alloc_ptr_t<_Alloc> _First, _Alloc_size_t<_Alloc> _Count, const typename _Alloc::value_type& _Val, _Alloc&, true_type) { // copy _Count copies of _Val to raw _First, using default _Alloc construct, memset optimization - _CSTD memset(_Unfancy(_First), static_cast(_Val), _Count); + _Fill_memset(_Unfancy(_First), _Val, _Count); return _First + _Count; } @@ -1756,7 +1756,7 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); if constexpr (_Fill_memset_is_safe<_Unwrapped_t, _Tval>) { - _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); + _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); } else { _Uninitialized_backout<_Unwrapped_t> _Backout{_UFirst}; while (_Backout._Last != _ULast) { @@ -1783,7 +1783,7 @@ template void _Uninitialized_fill_unchecked( const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, const _Tval& _Val, true_type) { // copy _Val throughout raw [_First, _Last), memset optimization - _CSTD memset(_First, static_cast(_Val), static_cast(_Last - _First)); + _Fill_memset(_First, _Val, static_cast(_Last - _First)); } template diff --git a/stl/inc/xutility b/stl/inc/xutility index 9c82bb27c55..99041c60cae 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4732,24 +4732,31 @@ struct _Is_character : true_type {}; // UTF-8 code units are sort-of ch #endif // __cpp_char8_t template -struct _Is_character_or_byte : _Is_character<_Ty>::type {}; +struct _Is_character_or_byte_or_bool : _Is_character<_Ty>::type {}; #ifdef __cpp_lib_byte template <> -struct _Is_character_or_byte : true_type {}; +struct _Is_character_or_byte_or_bool : true_type {}; #endif // __cpp_lib_byte +template <> +struct _Is_character_or_byte_or_bool : true_type {}; + // _Fill_memset_is_safe determines if _FwdIt and _Ty are eligible for memset optimization in fill template > -_INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v< - disjunction>, - _Is_character_or_byte<_Unwrap_enum_t<_Iter_value_t<_FwdIt>>>>, - conjunction>, is_same>>>>, +_INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, + _Is_character_or_byte_or_bool<_Unwrap_enum_t>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; template _INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; +template +void _Fill_memset(_DestTy* const _Dest, const _Ty _Val, const size_t _Count) { + _DestTy _Dest_val = _Val; // implicitly convert (a cast would suppress warnings); also handles _DestTy being bool + _CSTD memset(_Dest, static_cast(_Dest_val), _Count); +} + #if _HAS_IF_CONSTEXPR template _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) { @@ -4765,7 +4772,7 @@ _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); + _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); return; } } @@ -4787,7 +4794,7 @@ void _Fill_unchecked1(_FwdIt _First, _FwdIt _Last, const _Ty& _Val, false_type) template void _Fill_unchecked1(_FwdIt _First, _FwdIt _Last, const _Ty& _Val, true_type) { // copy _Val through [_First, _Last), memset optimization - _CSTD memset(_First, static_cast(_Val), static_cast(_Last - _First)); + _Fill_memset(_First, _Val, static_cast(_Last - _First)); } template @@ -4826,7 +4833,7 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - _CSTD memset(_UDest, static_cast(_Val), static_cast(_Count)); + _Fill_memset(_UDest, _Val, static_cast(_Count)); _UDest += _Count; _Seek_wrapped(_Dest, _UDest); return _Dest; @@ -4857,7 +4864,7 @@ _OutIt _Fill_n_unchecked2(_OutIt _Dest, _Diff _Count, const _Ty& _Val, false_typ template _OutIt _Fill_n_unchecked2(_OutIt _Dest, _Diff _Count, const _Ty& _Val, true_type) { // copy _Val _Count times through [_Dest, ...), memset optimization - _CSTD memset(_Dest, static_cast(_Val), static_cast(_Count)); + _Fill_memset(_Dest, _Val, static_cast(_Count)); return _Dest + _Count; } @@ -4892,15 +4899,18 @@ _FwdIt fill_n(_ExPo&&, _FwdIt _Dest, _Diff _Count_raw, const _Ty& _Val) noexcept // 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`.) +#pragma warning(push) +#pragma warning(disable : 4806) // no value of type 'bool' promoted to type 'char' can equal the given constant 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); + && is_integral_v<_Elem1> && !is_volatile_v<_Elem1> // + && is_integral_v<_Elem2> && !is_volatile_v<_Elem2>> +_INLINE_VAR constexpr bool _Can_memcmp_elements = + is_same_v<_Elem1, bool> || is_same_v<_Elem2, bool> || static_cast<_Elem1>(-1) == static_cast<_Elem2>(-1); +#pragma warning(pop) #ifdef __cpp_lib_byte // Allow memcmping std::byte. diff --git a/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp b/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp index 916ac5f73f7..0bea140c685 100644 --- a/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp @@ -31,6 +31,13 @@ struct instantiator { } assert(result == ranges::end(output)); } + { // Validate int is properly converted to bool + bool output[] = {false, true, false}; + fill(ranges::begin(output), ranges::end(output), 5); + for (const bool& elem : output) { + assert(elem == true); + } + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp index ff51fa9f6b2..622794b11c3 100644 --- a/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp @@ -36,6 +36,13 @@ struct instantiator { assert(ranges::equal(output, expected_output)); assert(result == ranges::begin(output)); } + { // Validate int is properly converted to bool + bool output[] = {false, true, false}; + fill_n(ranges::begin(output), ranges::distance(output), 5); + for (const bool& elem : output) { + assert(elem == true); + } + } } }; diff --git a/tests/std/tests/VSO_0180469_fill_family/test.cpp b/tests/std/tests/VSO_0180469_fill_family/test.cpp index 164e0318043..2ec62a9404f 100644 --- a/tests/std/tests/VSO_0180469_fill_family/test.cpp +++ b/tests/std/tests/VSO_0180469_fill_family/test.cpp @@ -19,8 +19,8 @@ using namespace std; // This thing is a workaround for C4309 "truncation of constant value" template -T cast(U i) { - return static_cast(i); +remove_volatile_t cast(U i) { + return static_cast>(i); } // Tests that `fillCall`(buffer, value, startIndex, endIndex) fills [startIndex, endIndex) with `value` @@ -131,9 +131,41 @@ int main() { test_fill(); test_fill(); + test_fill(); // Test GH-1183 + test_uninitialized_fill( [](count_copies* buff, size_t n, const count_copies& src) { uninitialized_fill(buff, buff + n, src); }); test_uninitialized_fill( [](count_copies* buff, size_t n, const count_copies& src) { uninitialized_fill_n(buff, n, src); }); + + // Validate int is properly converted to bool + { + bool output[] = {false, true, false}; + fill(output, output + 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + { + bool output[] = {false, true, false}; + fill_n(output, 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + { + bool output[] = {false, true, false}; + uninitialized_fill(output, output + 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + { + bool output[] = {false, true, false}; + uninitialized_fill_n(output, 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } } diff --git a/tests/std/tests/VSO_0180469_ptr_cat/test.cpp b/tests/std/tests/VSO_0180469_ptr_cat/test.cpp index e0747b48caf..993ac08ecaf 100644 --- a/tests/std/tests/VSO_0180469_ptr_cat/test.cpp +++ b/tests/std/tests/VSO_0180469_ptr_cat/test.cpp @@ -257,23 +257,33 @@ void test_case_Equal_memcmp_is_safe_comparator() { #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); + if constexpr (!is_same_v && !is_same_v) { // vector::iterator is not contiguous + 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); + if constexpr (!is_same_v && !is_same_v) { + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename vector::const_iterator, + Pr> == Expected); + } + + if constexpr (!is_same_v) { + STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + typename vector::const_iterator, Pr> == Expected); + } + + if constexpr (!is_same_v) { + 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); @@ -335,7 +345,8 @@ void test_case_Equal_memcmp_is_safe() { } void equal_safe_test_cases() { - // memcmp is safe for non-bool integral types + // memcmp is safe for integral types + test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); @@ -377,10 +388,10 @@ void equal_safe_test_cases() { 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 - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + // memcmp is safe between bool and other integral types with the same size because we don't care about + // representations other than 0 and 1 + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); // No enums test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe();