diff --git a/stl/inc/xstring b/stl/inc/xstring index 8ba06e0de21..46dfaeaca59 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -2512,20 +2512,31 @@ public: } } +private: + _NODISCARD static _CONSTEXPR20 size_type _Find_result_cast(const size_t _Off) noexcept { + if constexpr (sizeof(size_type) > sizeof(size_t)) { // only happens on 32-bit platforms with 64-bit size_type + if (_Off == static_cast(-1)) { + return npos; + } + } + return static_cast(_Off); + } + +public: #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const noexcept(_Is_nothrow_convertible_v>) { // look for _Right beginning at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; - return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type find(const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for _Right beginning at or after _Off - return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } @@ -2533,7 +2544,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning at or after _Off - return static_cast( + return _Find_result_cast( _Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } @@ -2541,13 +2552,13 @@ public: _NODISCARD _CONSTEXPR20 size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning at or after _Off - return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept { // look for _Ch at or after _Off - return static_cast(_Traits_find_ch<_Traits>( + return _Find_result_cast(_Traits_find_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } @@ -2557,14 +2568,14 @@ public: noexcept(_Is_nothrow_convertible_v>) { // look for _Right beginning before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; - return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type rfind(const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for _Right beginning before _Off - return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } @@ -2572,7 +2583,7 @@ public: _NODISCARD _CONSTEXPR20 size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning before _Off - return static_cast( + return _Find_result_cast( _Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } @@ -2580,13 +2591,13 @@ public: _NODISCARD _CONSTEXPR20 size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning before _Off - return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept { // look for _Ch before _Off - return static_cast(_Traits_rfind_ch<_Traits>( + return _Find_result_cast(_Traits_rfind_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } @@ -2596,7 +2607,7 @@ public: noexcept(_Is_nothrow_convertible_v>) { // look for one of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; - return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 @@ -2604,7 +2615,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find_first_of( const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off - return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } @@ -2612,7 +2623,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) at or after _Off - return static_cast( + return _Find_result_cast( _Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } @@ -2620,13 +2631,13 @@ public: _NODISCARD _CONSTEXPR20 size_type find_first_of( _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off - return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { // look for _Ch at or after _Off - return static_cast(_Traits_find_ch<_Traits>( + return _Find_result_cast(_Traits_find_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } @@ -2636,14 +2647,14 @@ public: noexcept(_Is_nothrow_convertible_v>) { // look for one of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; - return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type find_last_of(const basic_string& _Right, size_type _Off = npos) const noexcept { // look for one of _Right before _Off - return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } @@ -2651,7 +2662,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) before _Off - return static_cast( + return _Find_result_cast( _Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } @@ -2659,13 +2670,13 @@ public: _NODISCARD _CONSTEXPR20 size_type find_last_of( _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for one of [_Ptr, ) before _Off - return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { // look for _Ch before _Off - return static_cast(_Traits_rfind_ch<_Traits>( + return _Find_result_cast(_Traits_rfind_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } @@ -2675,7 +2686,7 @@ public: noexcept(_Is_nothrow_convertible_v>) { // look for none of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; - return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 @@ -2683,7 +2694,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find_first_not_of( const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off - return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } @@ -2691,7 +2702,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) at or after _Off - return static_cast( + return _Find_result_cast( _Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } @@ -2699,13 +2710,13 @@ public: _NODISCARD _CONSTEXPR20 size_type find_first_not_of( _In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off - return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { // look for non-_Ch at or after _Off - return static_cast(_Traits_find_not_ch<_Traits>( + return _Find_result_cast(_Traits_find_not_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } @@ -2715,7 +2726,7 @@ public: noexcept(_Is_nothrow_convertible_v>) { // look for none of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; - return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 @@ -2723,7 +2734,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find_last_not_of( const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for none of _Right before _Off - return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } @@ -2731,7 +2742,7 @@ public: _NODISCARD _CONSTEXPR20 size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) before _Off - return static_cast( + return _Find_result_cast( _Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } @@ -2739,13 +2750,13 @@ public: _NODISCARD _CONSTEXPR20 size_type find_last_not_of( _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for none of [_Ptr, ) before _Off - return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), + return _Find_result_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { // look for non-_Ch before _Off - return static_cast(_Traits_rfind_not_ch<_Traits>( + return _Find_result_cast(_Traits_rfind_not_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } diff --git a/tests/std/tests/GH_005546_containers_size_type_cast/test.cpp b/tests/std/tests/GH_005546_containers_size_type_cast/test.cpp index 5116ee9ac55..ab5f5f84564 100644 --- a/tests/std/tests/GH_005546_containers_size_type_cast/test.cpp +++ b/tests/std/tests/GH_005546_containers_size_type_cast/test.cpp @@ -517,10 +517,87 @@ CONSTEXPR20 bool test() { return true; } -#if _HAS_CXX20 -static_assert(test()); -#endif // _HAS_CXX20 +template +struct WideSizeAllocator { + using value_type = T; + using size_type = unsigned long long; + + WideSizeAllocator() = default; + + template + constexpr WideSizeAllocator(const WideSizeAllocator&) noexcept {} + + constexpr T* allocate(size_type n) { + return allocator{}.allocate(static_cast(n)); + } + constexpr void deallocate(T* p, size_type n) noexcept { + allocator{}.deallocate(p, static_cast(n)); + } +}; + +CONSTEXPR20 bool test_LWG4259() { + // On 32-bit platforms, basic_string may use 64-bit size_type (depending on the allocator), while basic_string_view + // uses 32-bit size_t. This makes basic_string_view::npos smaller than basic_string::npos. + using WideSizeString = basic_string, WideSizeAllocator>; + + WideSizeString str = "Hello World"; + WideSizeString not_found_str = "XYZ"; + const char* not_found_cstr = "XYZ"; + assert(str.find(not_found_str) == WideSizeString::npos); + assert(str.find(not_found_cstr, 0, 3) == WideSizeString::npos); + assert(str.find(not_found_cstr) == WideSizeString::npos); + assert(str.find('Z') == WideSizeString::npos); +#if _HAS_CXX17 + string_view sv = "XYZ"; + assert(str.find(sv) == WideSizeString::npos); +#endif + assert(str.rfind(not_found_str) == WideSizeString::npos); + assert(str.rfind(not_found_cstr, WideSizeString::npos, 3) == WideSizeString::npos); + assert(str.rfind(not_found_cstr) == WideSizeString::npos); + assert(str.rfind('Z') == WideSizeString::npos); +#if _HAS_CXX17 + assert(str.rfind(sv) == WideSizeString::npos); +#endif + assert(str.find_first_of(not_found_str) == WideSizeString::npos); + assert(str.find_first_of(not_found_cstr, 0, 3) == WideSizeString::npos); + assert(str.find_first_of(not_found_cstr) == WideSizeString::npos); + assert(str.find_first_of('Z') == WideSizeString::npos); +#if _HAS_CXX17 + assert(str.find_first_of(sv) == WideSizeString::npos); +#endif + WideSizeString all_in_str = "Hello World!"; + WideSizeString search_all = "Hello World"; + assert(search_all.find_first_not_of(all_in_str) == WideSizeString::npos); + assert(search_all.find_first_not_of("Hello World!", 0, 12) == WideSizeString::npos); + assert(search_all.find_first_not_of("Hello World!") == WideSizeString::npos); + WideSizeString repeated = "AAAAA"; + assert(repeated.find_first_not_of('A') == WideSizeString::npos); +#if _HAS_CXX17 + string_view sv_all = "Hello World!"; + assert(search_all.find_first_not_of(sv_all) == WideSizeString::npos); +#endif + assert(str.find_last_of(not_found_str) == WideSizeString::npos); + assert(str.find_last_of(not_found_cstr, WideSizeString::npos, 3) == WideSizeString::npos); + assert(str.find_last_of(not_found_cstr) == WideSizeString::npos); + assert(str.find_last_of('Z') == WideSizeString::npos); +#if _HAS_CXX17 + assert(str.find_last_of(sv) == WideSizeString::npos); +#endif + assert(search_all.find_last_not_of(all_in_str) == WideSizeString::npos); + assert(search_all.find_last_not_of("Hello World!", WideSizeString::npos, 12) == WideSizeString::npos); + assert(search_all.find_last_not_of("Hello World!") == WideSizeString::npos); + assert(repeated.find_last_not_of('A') == WideSizeString::npos); +#if _HAS_CXX17 + assert(search_all.find_last_not_of(sv_all) == WideSizeString::npos); +#endif + return true; +} int main() { +#if _HAS_CXX20 + static_assert(test()); + static_assert(test_LWG4259()); +#endif // _HAS_CXX20 test(); + test_LWG4259(); }