From 99ef0d0efc279637472af2cfd01d62d4dba54fac Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 19 Nov 2024 01:38:33 -0800 Subject: [PATCH 01/35] Don't use `__restrict__` for CUDA (#5097) --- stl/inc/yvals_core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index a904ea7d01e..07258c7ec98 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -2016,8 +2016,8 @@ compiler option, or define _ALLOW_RTCc_IN_STL to suppress this error. #define _CONST_CALL_OPERATOR const #endif // ^^^ !defined(__cpp_static_call_operator) ^^^ -#ifdef __CUDACC__ // TRANSITION, CUDA 12.4 doesn't recognize __restrict -#define _RESTRICT __restrict__ +#ifdef __CUDACC__ // TRANSITION, CUDA 12.4 doesn't recognize MSVC __restrict; CUDA __restrict__ is not usable in C++ +#define _RESTRICT #else // ^^^ defined(__CUDACC__) / !defined(__CUDACC__) vvv #define _RESTRICT __restrict #endif // ^^^ !defined(__CUDACC__) ^^^ From 64d143da9502c56399deead3ac2245f7d0df8cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Tue, 19 Nov 2024 10:39:38 +0100 Subject: [PATCH 02/35] Enforce `assign_range()` mandates for sequence containers (#5086) --- stl/inc/deque | 2 ++ stl/inc/forward_list | 2 ++ stl/inc/list | 2 ++ stl/inc/vector | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/stl/inc/deque b/stl/inc/deque index 7d910fe2fde..eed80fc0993 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -832,6 +832,8 @@ public: #if _HAS_CXX23 template <_Container_compatible_range<_Ty> _Rng> void assign_range(_Rng&& _Range) { + static_assert(assignable_from<_Ty&, _RANGES range_reference_t<_Rng>>, + "Elements must be assignable from the range's reference type (N4993 [sequence.reqmts]/60)."); _Assign_range(_RANGES _Ubegin(_Range), _RANGES _Uend(_Range)); } #endif // _HAS_CXX23 diff --git a/stl/inc/forward_list b/stl/inc/forward_list index d213386f2bd..993bb3ac575 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -967,6 +967,8 @@ public: #if _HAS_CXX23 template <_Container_compatible_range<_Ty> _Rng> void assign_range(_Rng&& _Range) { + static_assert(assignable_from<_Ty&, _RANGES range_reference_t<_Rng>>, + "Elements must be assignable from the range's reference type (N4993 [sequence.reqmts]/60)."); _Assign_unchecked(_RANGES _Ubegin(_Range), _RANGES _Uend(_Range)); } #endif // _HAS_CXX23 diff --git a/stl/inc/list b/stl/inc/list index 79a96fd76b5..d98a72d281e 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -1344,6 +1344,8 @@ public: #if _HAS_CXX23 template <_Container_compatible_range<_Ty> _Rng> void assign_range(_Rng&& _Range) { + static_assert(assignable_from<_Ty&, _RANGES range_reference_t<_Rng>>, + "Elements must be assignable from the range's reference type (N4993 [sequence.reqmts]/60)."); _Assign_unchecked(_RANGES _Ubegin(_Range), _RANGES _Uend(_Range)); } #endif // _HAS_CXX23 diff --git a/stl/inc/vector b/stl/inc/vector index ef19788a059..b0913e25128 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1488,6 +1488,8 @@ public: #if _HAS_CXX23 template <_Container_compatible_range<_Ty> _Rng> constexpr void assign_range(_Rng&& _Range) { + static_assert(assignable_from<_Ty&, _RANGES range_reference_t<_Rng>>, + "Elements must be assignable from the range's reference type (N4993 [sequence.reqmts]/60)."); if constexpr (_RANGES sized_range<_Rng> || _RANGES forward_range<_Rng>) { const auto _Length = _To_unsigned_like(_RANGES distance(_Range)); const auto _Count = _Convert_size(_Length); @@ -3329,6 +3331,8 @@ public: template <_Container_compatible_range _Rng> constexpr void assign_range(_Rng&& _Range) { + static_assert(assignable_from>, + "Elements must be assignable from the range's reference type (N4993 [sequence.reqmts]/60)."); clear(); if constexpr (_RANGES forward_range<_Rng> || _RANGES sized_range<_Rng>) { const auto _Length = _To_unsigned_like(_RANGES distance(_Range)); From 1711bc35aa5e8c7f2f70daed833e2e3eeab36138 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 19 Nov 2024 01:44:15 -0800 Subject: [PATCH 03/35] Vectorize `basic_string::rfind` (the single character overload) (#5087) Co-authored-by: Stephan T. Lavavej --- benchmarks/src/find_and_count.cpp | 52 ++++++++++++++----- stl/inc/__msvc_string_view.hpp | 19 ++++++- stl/inc/algorithm | 32 ------------ stl/inc/xutility | 32 ++++++++++++ .../VSO_0000000_vector_algorithms/test.cpp | 17 ++++++ 5 files changed, 107 insertions(+), 45 deletions(-) diff --git a/benchmarks/src/find_and_count.cpp b/benchmarks/src/find_and_count.cpp index 9c608bfe356..b0addf6a97c 100644 --- a/benchmarks/src/find_and_count.cpp +++ b/benchmarks/src/find_and_count.cpp @@ -7,25 +7,38 @@ #include #include #include +#include +#include #include +#include "skewed_allocator.hpp" + enum class Op { FindSized, FindUnsized, Count, + StringFind, + StringRFind, }; using namespace std; -template +template class Alloc, Op Operation> void bm(benchmark::State& state) { const auto size = static_cast(state.range(0)); const auto pos = static_cast(state.range(1)); - vector a(size, T{'0'}); + using Container = conditional_t, Alloc>, vector>>; + + Container a(size, T{'0'}); if (pos < size) { - a[pos] = T{'1'}; + if constexpr (Operation == Op::StringRFind) { + a[size - pos - 1] = T{'1'}; + } else { + a[pos] = T{'1'}; + } } else { if constexpr (Operation == Op::FindUnsized) { abort(); @@ -39,6 +52,10 @@ void bm(benchmark::State& state) { benchmark::DoNotOptimize(ranges::find(a.begin(), unreachable_sentinel, T{'1'})); } else if constexpr (Operation == Op::Count) { benchmark::DoNotOptimize(ranges::count(a.begin(), a.end(), T{'1'})); + } else if constexpr (Operation == Op::StringFind) { + benchmark::DoNotOptimize(a.find(T{'1'})); + } else if constexpr (Operation == Op::StringRFind) { + benchmark::DoNotOptimize(a.rfind(T{'1'})); } } } @@ -50,17 +67,28 @@ void common_args(auto bm) { } -BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); BENCHMARK_MAIN(); diff --git a/stl/inc/__msvc_string_view.hpp b/stl/inc/__msvc_string_view.hpp index 75ac8b62491..aeae9bd2c08 100644 --- a/stl/inc/__msvc_string_view.hpp +++ b/stl/inc/__msvc_string_view.hpp @@ -724,7 +724,24 @@ constexpr size_t _Traits_rfind_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Tra return static_cast(-1); } - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + const size_t _Actual_start_at = (_STD min)(_Start_at, _Hay_size - 1); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Is_implementation_handled_char_traits<_Traits>) { + if (!_STD _Is_constant_evaluated()) { + const auto _End = _Haystack + _Actual_start_at + 1; + const auto _Ptr = _STD _Find_last_vectorized(_Haystack, _End, _Ch); + + if (_Ptr != _End) { + return static_cast(_Ptr - _Haystack); + } else { + return static_cast(-1); + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + for (auto _Match_try = _Haystack + _Actual_start_at;; --_Match_try) { if (_Traits::eq(*_Match_try, _Ch)) { return static_cast(_Match_try - _Haystack); // found a match } diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 40f3f965d2f..a0b9a7afe95 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -54,11 +54,6 @@ _Min_max_element_t __stdcall __std_minmax_element_8(const void* _First, const vo _Min_max_element_t __stdcall __std_minmax_element_f(const void* _First, const void* _Last, bool _Unused) noexcept; _Min_max_element_t __stdcall __std_minmax_element_d(const void* _First, const void* _Last, bool _Unused) noexcept; -const void* __stdcall __std_find_last_trivial_1(const void* _First, const void* _Last, uint8_t _Val) noexcept; -const void* __stdcall __std_find_last_trivial_2(const void* _First, const void* _Last, uint16_t _Val) noexcept; -const void* __stdcall __std_find_last_trivial_4(const void* _First, const void* _Last, uint32_t _Val) noexcept; -const void* __stdcall __std_find_last_trivial_8(const void* _First, const void* _Last, uint64_t _Val) noexcept; - __declspec(noalias) _Min_max_1i __stdcall __std_minmax_1i(const void* _First, const void* _Last) noexcept; __declspec(noalias) _Min_max_1u __stdcall __std_minmax_1u(const void* _First, const void* _Last) noexcept; __declspec(noalias) _Min_max_2i __stdcall __std_minmax_2i(const void* _First, const void* _Last) noexcept; @@ -162,33 +157,6 @@ auto _Minmax_vectorized(_Ty* const _First, _Ty* const _Last) noexcept { } } -template -_Ty* _Find_last_vectorized(_Ty* const _First, _Ty* const _Last, const _TVal _Val) noexcept { - if constexpr (is_pointer_v<_TVal> || is_null_pointer_v<_TVal>) { -#ifdef _WIN64 - return const_cast<_Ty*>( - static_cast(::__std_find_last_trivial_8(_First, _Last, reinterpret_cast(_Val)))); -#else - return const_cast<_Ty*>( - static_cast(::__std_find_last_trivial_4(_First, _Last, reinterpret_cast(_Val)))); -#endif - } else if constexpr (sizeof(_Ty) == 1) { - return const_cast<_Ty*>( - static_cast(::__std_find_last_trivial_1(_First, _Last, static_cast(_Val)))); - } else if constexpr (sizeof(_Ty) == 2) { - return const_cast<_Ty*>( - static_cast(::__std_find_last_trivial_2(_First, _Last, static_cast(_Val)))); - } else if constexpr (sizeof(_Ty) == 4) { - return const_cast<_Ty*>( - static_cast(::__std_find_last_trivial_4(_First, _Last, static_cast(_Val)))); - } else if constexpr (sizeof(_Ty) == 8) { - return const_cast<_Ty*>( - static_cast(::__std_find_last_trivial_8(_First, _Last, static_cast(_Val)))); - } else { - _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size - } -} - template __declspec(noalias) void _Replace_vectorized( _Ty* const _First, _Ty* const _Last, const _TVal1 _Old_val, const _TVal2 _New_val) noexcept { diff --git a/stl/inc/xutility b/stl/inc/xutility index df69b60e6cc..77c8619911f 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -93,6 +93,11 @@ const void* __stdcall __std_find_trivial_2(const void* _First, const void* _Last const void* __stdcall __std_find_trivial_4(const void* _First, const void* _Last, uint32_t _Val) noexcept; const void* __stdcall __std_find_trivial_8(const void* _First, const void* _Last, uint64_t _Val) noexcept; +const void* __stdcall __std_find_last_trivial_1(const void* _First, const void* _Last, uint8_t _Val) noexcept; +const void* __stdcall __std_find_last_trivial_2(const void* _First, const void* _Last, uint16_t _Val) noexcept; +const void* __stdcall __std_find_last_trivial_4(const void* _First, const void* _Last, uint32_t _Val) noexcept; +const void* __stdcall __std_find_last_trivial_8(const void* _First, const void* _Last, uint64_t _Val) noexcept; + const void* __stdcall __std_find_first_of_trivial_1( const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; const void* __stdcall __std_find_first_of_trivial_2( @@ -217,6 +222,33 @@ _Ty* _Find_vectorized(_Ty* const _First, _Ty* const _Last, const _TVal _Val) noe } } +template +_Ty* _Find_last_vectorized(_Ty* const _First, _Ty* const _Last, const _TVal _Val) noexcept { + if constexpr (is_pointer_v<_TVal> || is_null_pointer_v<_TVal>) { +#ifdef _WIN64 + return const_cast<_Ty*>( + static_cast(::__std_find_last_trivial_8(_First, _Last, reinterpret_cast(_Val)))); +#else + return const_cast<_Ty*>( + static_cast(::__std_find_last_trivial_4(_First, _Last, reinterpret_cast(_Val)))); +#endif + } else if constexpr (sizeof(_Ty) == 1) { + return const_cast<_Ty*>( + static_cast(::__std_find_last_trivial_1(_First, _Last, static_cast(_Val)))); + } else if constexpr (sizeof(_Ty) == 2) { + return const_cast<_Ty*>( + static_cast(::__std_find_last_trivial_2(_First, _Last, static_cast(_Val)))); + } else if constexpr (sizeof(_Ty) == 4) { + return const_cast<_Ty*>( + static_cast(::__std_find_last_trivial_4(_First, _Last, static_cast(_Val)))); + } else if constexpr (sizeof(_Ty) == 8) { + return const_cast<_Ty*>( + static_cast(::__std_find_last_trivial_8(_First, _Last, static_cast(_Val)))); + } else { + _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size + } +} + // find_first_of vectorization is likely to be a win after this size (in elements) _INLINE_VAR constexpr ptrdiff_t _Threshold_find_first_of = 16; diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index e7aec69bea8..dcc5c59ec46 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -1128,6 +1128,22 @@ void test_case_string_rfind_str(const basic_string& input_haystack, const bas assert(expected == actual); } +template +void test_case_string_rfind_ch(const basic_string& input_haystack, const T value) { + ptrdiff_t expected; + + const auto expected_iter = last_known_good_find_last(input_haystack.begin(), input_haystack.end(), value); + + if (expected_iter != input_haystack.end()) { + expected = expected_iter - input_haystack.begin(); + } else { + expected = -1; + } + + const auto actual = static_cast(input_haystack.rfind(value)); + assert(expected == actual); +} + template void test_basic_string_dis(mt19937_64& gen, D& dis) { basic_string input_haystack; @@ -1144,6 +1160,7 @@ void test_basic_string_dis(mt19937_64& gen, D& dis) { test_case_string_find_last_of(input_haystack, input_needle); test_case_string_find_str(input_haystack, input_needle); test_case_string_rfind_str(input_haystack, input_needle); + test_case_string_rfind_ch(input_haystack, static_cast(dis(gen))); for (size_t attempts = 0; attempts < needleDataCount; ++attempts) { input_needle.push_back(static_cast(dis(gen))); From fec1c8b6a13e5411edebbddc3ad98258f5e282d2 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Tue, 19 Nov 2024 17:49:28 +0800 Subject: [PATCH 04/35] ``: Fix bogus pointer arithmetic with integer-class (#5091) Co-authored-by: Casey Carter --- stl/inc/algorithm | 49 ++++----- tests/std/include/range_algorithm_support.hpp | 4 +- tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 99 +++++++++++++++++++ 5 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 tests/std/tests/GH_002885_stable_sort_difference_type/env.lst create mode 100644 tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index a0b9a7afe95..5ea4e3db6f2 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -6568,7 +6568,9 @@ namespace ranges { if (_Count1 <= _Count2 && _Count1 <= _Capacity) { // buffer left range, then move parts _Uninitialized_backout*> _Backout{ - _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Temp_ptr, _Temp_ptr + _Count1).out}; + _Temp_ptr, _RANGES _Uninitialized_move_unchecked( + _First, _Mid, _Temp_ptr, _Temp_ptr + static_cast(_Count1)) + .out}; const _It _New_mid = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)).out; _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _New_mid); return _New_mid; @@ -6576,7 +6578,9 @@ namespace ranges { if (_Count2 <= _Capacity) { // buffer right range, then move parts _Uninitialized_backout*> _Backout{ - _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Temp_ptr, _Temp_ptr + _Count2).out}; + _Temp_ptr, _RANGES _Uninitialized_move_unchecked( + _Mid, _Last, _Temp_ptr, _Temp_ptr + static_cast(_Count2)) + .out}; _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); return _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _STD move(_First)).out; } @@ -8856,8 +8860,10 @@ namespace ranges { const iter_difference_t<_It> _Half_count_ceil = _Count - _Half_count; const _It _Mid = _First + _Half_count_ceil; if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer - _Buffered_merge_sort_common(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Pred, _Proj); - _Buffered_merge_sort_common(_Mid, _Last, _Half_count, _Temp_ptr, _Pred, _Proj); + _Buffered_merge_sort_common( + _First, _Mid, static_cast(_Half_count_ceil), _Temp_ptr, _Pred, _Proj); + _Buffered_merge_sort_common( + _Mid, _Last, static_cast(_Half_count), _Temp_ptr, _Pred, _Proj); } else { // temp buffer not big enough, divide and conquer _Stable_sort_common_buffered(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Capacity, _Pred, _Proj); _Stable_sort_common_buffered(_Mid, _Last, _Half_count, _Temp_ptr, _Capacity, _Pred, _Proj); @@ -8869,24 +8875,24 @@ namespace ranges { } template - static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const iter_difference_t<_It> _Count, + static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const ptrdiff_t _Count, iter_value_t<_It>* const _Temp_ptr, _Pr _Pred, _Pj _Proj) { // sort using temp buffer for merges - // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t + // pre: _Count <= capacity of buffer at _Temp_ptr _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_Last - _First == _Count); _Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred, _Proj); // merge adjacent pairs of chunks to and from temp buffer - if (_Count <= _Isort_max<_It>) { + if (_Count <= _ISORT_MAX) { return; } // do the first merge, constructing elements in the temporary buffer _Uninitialized_chunked_merge_common(_First, _Last, _Temp_ptr, _Count, _Pred, _Proj); _Uninitialized_backout*> _Backout{_Temp_ptr, _Temp_ptr + _Count}; - iter_difference_t<_It> _Chunk_size = _Isort_max<_It>; + ptrdiff_t _Chunk_size = _ISORT_MAX; for (;;) { // unconditionally merge elements back into the source buffer _Chunk_size <<= 1; @@ -8902,14 +8908,13 @@ namespace ranges { } template - static void _Insertion_sort_isort_max_chunks( - _It _First, _It _Last, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + static void _Insertion_sort_isort_max_chunks(_It _First, _It _Last, ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) { // insertion sort every chunk of distance _Isort_max<_It> in [_First, _Last) _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); - for (; _Isort_max<_It> < _Count; _Count -= _Isort_max<_It>) { // sort chunks + for (; _ISORT_MAX < _Count; _Count -= _ISORT_MAX) { // sort chunks _First = _RANGES _Insertion_sort_common(_First, _First + _Isort_max<_It>, _Pred, _Proj); } @@ -8918,8 +8923,8 @@ namespace ranges { } template - static void _Uninitialized_chunked_merge_common(_It _First, const _It _Last, iter_value_t<_It>* const _Dest, - iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + static void _Uninitialized_chunked_merge_common( + _It _First, const _It _Last, iter_value_t<_It>* const _Dest, ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) { // move to uninitialized merging adjacent chunks of distance _Isort_max<_It> _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); @@ -8928,14 +8933,14 @@ namespace ranges { _Uninitialized_backout*> _Backout{_Dest}; const auto _Backout_end = _Dest + _Count; - while (_Isort_max<_It> < _Count) { - _Count -= _Isort_max<_It>; - const auto _Chunk2 = (_STD min)(_Isort_max<_It>, _Count); + while (_ISORT_MAX < _Count) { + _Count -= _ISORT_MAX; + const auto _Chunk2 = (_STD min)(static_cast(_ISORT_MAX), _Count); _Count -= _Chunk2; auto _Mid1 = _First + _Isort_max<_It>; - auto _Last1 = _Mid1 + _Chunk2; - auto _Last2 = _Backout._Last + _Isort_max<_It> + _Chunk2; + auto _Last1 = _Mid1 + static_cast>(_Chunk2); + auto _Last2 = _Backout._Last + _ISORT_MAX + _Chunk2; _Backout._Last = _Uninitialized_merge_move( _STD move(_First), _STD move(_Mid1), _Last1, _Backout._Last, _Last2, _Pred, _Proj); _First = _STD move(_Last1); @@ -9015,8 +9020,8 @@ namespace ranges { } template - static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest, - const iter_difference_t<_It1> _Chunk_size, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj _Proj) { + static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest, const ptrdiff_t _Chunk_size, + ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) { // move merging adjacent chunks of distance _Chunk_size _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It1>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It1, _Pr, _Pj>); @@ -9029,8 +9034,8 @@ namespace ranges { const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); _Count -= _Right_chunk_size; - auto _Mid1 = _First + _Chunk_size; - auto _Last1 = _Mid1 + _Right_chunk_size; + auto _Mid1 = _First + static_cast>(_Chunk_size); + auto _Last1 = _Mid1 + static_cast>(_Right_chunk_size); _Dest = _Merge_move_common(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); _First = _STD move(_Last1); } diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index d4f430bfbbb..056b84b2982 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -1163,10 +1163,10 @@ namespace test { if constexpr (is_sized) { const auto sz = to_unsigned(static_cast(ranges::distance(r))); return ranges::subrange{ - rediff_iter{r.begin()}, rediff_sent{r.end()}, sz}; + rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}, sz}; } else { return ranges::subrange{ - rediff_iter{r.begin()}, rediff_sent{r.end()}}; + rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}}; } } } // namespace test diff --git a/tests/std/test.lst b/tests/std/test.lst index 94f80f9dce5..42163db9973 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -221,6 +221,7 @@ tests\GH_002711_Zc_alignedNew- tests\GH_002760_syncstream_memory_leak tests\GH_002769_handle_deque_block_pointers tests\GH_002789_Hash_vec_Tidy +tests\GH_002885_stable_sort_difference_type tests\GH_002989_nothrow_unwrappable tests\GH_002992_unwrappable_iter_sent_pairs tests\GH_003003_format_decimal_point diff --git a/tests/std/tests/GH_002885_stable_sort_difference_type/env.lst b/tests/std/tests/GH_002885_stable_sort_difference_type/env.lst new file mode 100644 index 00000000000..351a8293d9d --- /dev/null +++ b/tests/std/tests/GH_002885_stable_sort_difference_type/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_20_matrix.lst diff --git a/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp new file mode 100644 index 00000000000..34fe497513c --- /dev/null +++ b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include "range_algorithm_support.hpp" + +using namespace std; + +constexpr auto pred = [](int i) { return i <= 42; }; + +template +void test_iota_transform() { + constexpr int orig[]{42, 1729}; + int a[]{42, 1729}; + auto vw = views::iota(I{}, static_cast(ranges::size(a))) + | views::transform([&a](I i) -> auto& { return a[static_cast(i)]; }); + + static_assert(three_way_comparable>>); // TRANSITION, /permissive + + ranges::stable_sort(vw); + assert(ranges::equal(a, orig)); + + ranges::stable_sort(vw.begin(), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::inplace_merge(vw, ranges::next(vw.begin())); + assert(ranges::equal(a, orig)); + ranges::inplace_merge(vw.begin(), ranges::next(vw.begin()), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::stable_partition(vw, pred); + assert(ranges::equal(a, orig)); + ranges::stable_partition(vw.begin(), vw.end(), pred); + assert(ranges::equal(a, orig)); +} + +void test_iota_transform_all() { + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + + test_iota_transform(); +#ifdef __cpp_char8_t + test_iota_transform(); +#endif // defined(__cpp_char8_t) + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); +} + +template +void test_redifference() { + constexpr int orig[]{42, 1729}; + int a[]{42, 1729}; + auto vw = test::make_redifference_subrange(a); + + ranges::stable_sort(vw); + assert(ranges::equal(a, orig)); + + ranges::stable_sort(vw.begin(), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::inplace_merge(vw, ranges::next(vw.begin())); + assert(ranges::equal(a, orig)); + ranges::inplace_merge(vw.begin(), ranges::next(vw.begin()), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::stable_partition(vw, pred); + assert(ranges::equal(a, orig)); + ranges::stable_partition(vw.begin(), vw.end(), pred); + assert(ranges::equal(a, orig)); +} + +void test_redifference_all() { + test_redifference(); + test_redifference(); + test_redifference(); + test_redifference(); + test_redifference(); + test_redifference<_Signed128>(); +} + +int main() { + test_iota_transform_all(); + test_redifference_all(); +} From 9b8a09afdd1cc942e309e495f33b500d6492b449 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 22 Nov 2024 13:24:29 -0800 Subject: [PATCH 05/35] More specific assertion for unlocking mutex not owned by the current thread (#5099) --- stl/src/mutex.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/src/mutex.cpp b/stl/src/mutex.cpp index cd44a0044a7..4a35e8c77aa 100644 --- a/stl/src/mutex.cpp +++ b/stl/src/mutex.cpp @@ -147,8 +147,9 @@ static _Thrd_result mtx_do_lock(_Mtx_t mtx, const _timespec64* target) noexcept } _CRTIMP2_PURE _Thrd_result __cdecl _Mtx_unlock(_Mtx_t mtx) noexcept { // unlock mutex + _THREAD_ASSERT(mtx->_Count > 0, "unlock of unowned mutex"); _THREAD_ASSERT( - 1 <= mtx->_Count && mtx->_Thread_id == static_cast(GetCurrentThreadId()), "unlock of unowned mutex"); + mtx->_Thread_id == static_cast(GetCurrentThreadId()), "unlock of mutex not owned by the current thread"); if (--mtx->_Count == 0) { // leave critical section mtx->_Thread_id = -1; From 8ebb4d6aceddda3e13ae162aff381542266bc7aa Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 22 Nov 2024 13:28:12 -0800 Subject: [PATCH 06/35] Internally remove workaround in `Dev09_056375_locale_cleanup` (#5103) --- tests/std/tests/Dev09_056375_locale_cleanup/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp b/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp index 0e8f1b29da2..4c2c434b6cb 100644 --- a/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp +++ b/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp @@ -83,9 +83,9 @@ void test_dll() { TheFuncProc pFunc = reinterpret_cast(GetProcAddress(hLibrary, "DllTest")); assert(pFunc != nullptr); pFunc(); -#if defined(_DLL) || !defined(__SANITIZE_ADDRESS__) // TRANSITION, VSO-2046190 +#if defined(_MSVC_INTERNAL_TESTING) || defined(_DLL) || !defined(__SANITIZE_ADDRESS__) // TRANSITION, vs17.13p2 FreeLibrary(hLibrary); -#endif // defined(_DLL) || !defined(__SANITIZE_ADDRESS__) +#endif // ^^^ no workaround ^^^ #endif // ^^^ !defined(_M_CEE) ^^^ } From 5e0ddadefd38b9ab50f2b8772ad4cb001133fd9d Mon Sep 17 00:00:00 2001 From: "S. B. Tam" Date: Sat, 23 Nov 2024 05:32:13 +0800 Subject: [PATCH 07/35] Move `system_clock` from `<__msvc_chrono.hpp>` to `` (#5105) --- stl/inc/__msvc_chrono.hpp | 97 ------------------------------- stl/inc/chrono | 100 +++++++++++++++++++++++++++++++- stl/inc/experimental/filesystem | 2 +- 3 files changed, 99 insertions(+), 100 deletions(-) diff --git a/stl/inc/__msvc_chrono.hpp b/stl/inc/__msvc_chrono.hpp index 226df1df848..7f5adad907e 100644 --- a/stl/inc/__msvc_chrono.hpp +++ b/stl/inc/__msvc_chrono.hpp @@ -646,33 +646,6 @@ namespace chrono { return time_point<_Clock, _To>(_CHRONO round<_To>(_Time.time_since_epoch())); } - _EXPORT_STD struct system_clock { // wraps GetSystemTimePreciseAsFileTime - using rep = long long; - using period = ratio<1, 10'000'000>; // 100 nanoseconds - using duration = _CHRONO duration; - using time_point = _CHRONO time_point; - static constexpr bool is_steady = false; - - _NODISCARD static time_point now() noexcept { // get current time - return time_point(duration(_Xtime_get_ticks())); - } - - _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept { // convert to __time64_t - return duration_cast(_Time.time_since_epoch()).count(); - } - - _NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept { // convert from __time64_t - return time_point{seconds{_Tm}}; - } - }; - -#if _HAS_CXX20 - _EXPORT_STD template - using sys_time = time_point; - _EXPORT_STD using sys_seconds = sys_time; - _EXPORT_STD using sys_days = sys_time; -#endif // _HAS_CXX20 - _EXPORT_STD struct steady_clock { // wraps QueryPerformanceCounter using rep = long long; using period = nano; @@ -726,76 +699,6 @@ namespace chrono { #undef _LIKELY_ARM_ARM64 #undef _LIKELY_X86_X64 }; - - _EXPORT_STD using high_resolution_clock = steady_clock; -} // namespace chrono - -inline namespace literals { - inline namespace chrono_literals { - _EXPORT_STD _NODISCARD constexpr _CHRONO hours operator""h(unsigned long long _Val) noexcept - /* strengthened */ { - return _CHRONO hours(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO duration> operator""h(long double _Val) noexcept - /* strengthened */ { - return _CHRONO duration>(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO minutes operator""min(unsigned long long _Val) noexcept - /* strengthened */ { - return _CHRONO minutes(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO duration> operator""min(long double _Val) noexcept - /* strengthened */ { - return _CHRONO duration>(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO seconds operator""s(unsigned long long _Val) noexcept - /* strengthened */ { - return _CHRONO seconds(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""s(long double _Val) noexcept - /* strengthened */ { - return _CHRONO duration(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO milliseconds operator""ms(unsigned long long _Val) noexcept - /* strengthened */ { - return _CHRONO milliseconds(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""ms(long double _Val) noexcept - /* strengthened */ { - return _CHRONO duration(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO microseconds operator""us(unsigned long long _Val) noexcept - /* strengthened */ { - return _CHRONO microseconds(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""us(long double _Val) noexcept - /* strengthened */ { - return _CHRONO duration(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO nanoseconds operator""ns(unsigned long long _Val) noexcept - /* strengthened */ { - return _CHRONO nanoseconds(_Val); - } - - _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""ns(long double _Val) noexcept - /* strengthened */ { - return _CHRONO duration(_Val); - } - } // namespace chrono_literals -} // namespace literals - -namespace chrono { - _EXPORT_STD using namespace literals::chrono_literals; } // namespace chrono _STD_END diff --git a/stl/inc/chrono b/stl/inc/chrono index 20caee89735..a895f59a2c2 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -77,8 +77,37 @@ _NODISCARD basic_string _Convert_w } #endif // _HAS_CXX17 -#if _HAS_CXX20 namespace chrono { + _EXPORT_STD struct system_clock { // wraps GetSystemTimePreciseAsFileTime + using rep = long long; + using period = ratio<1, 10'000'000>; // 100 nanoseconds + using duration = _CHRONO duration; + using time_point = _CHRONO time_point; + static constexpr bool is_steady = false; + + _NODISCARD static time_point now() noexcept { // get current time + return time_point(duration(_Xtime_get_ticks())); + } + + _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept { // convert to __time64_t + return duration_cast(_Time.time_since_epoch()).count(); + } + + _NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept { // convert from __time64_t + return time_point{seconds{_Tm}}; + } + }; + +#if _HAS_CXX20 + _EXPORT_STD template + using sys_time = time_point; + _EXPORT_STD using sys_seconds = sys_time; + _EXPORT_STD using sys_days = sys_time; +#endif // _HAS_CXX20 + + _EXPORT_STD using high_resolution_clock = steady_clock; + +#if _HAS_CXX20 // [time.duration.io] #define _IF_PERIOD_RETURN_SUFFIX_ELSE(_TYPE, _SUFFIX) \ @@ -2758,8 +2787,10 @@ namespace chrono { return gps_time>{_Time.time_since_epoch()} + _Gps_epoch_adjust; } }; +#endif // _HAS_CXX20 } // namespace chrono +#if _HAS_CXX20 namespace filesystem { struct _File_time_clock; } // namespace filesystem @@ -6119,19 +6150,84 @@ namespace chrono { return _STD move(_Os).str(); } } // namespace chrono +#endif // _HAS_CXX20 inline namespace literals { inline namespace chrono_literals { + _EXPORT_STD _NODISCARD constexpr _CHRONO hours operator""h(unsigned long long _Val) noexcept + /* strengthened */ { + return _CHRONO hours(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO duration> operator""h(long double _Val) noexcept + /* strengthened */ { + return _CHRONO duration>(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO minutes operator""min(unsigned long long _Val) noexcept + /* strengthened */ { + return _CHRONO minutes(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO duration> operator""min(long double _Val) noexcept + /* strengthened */ { + return _CHRONO duration>(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO seconds operator""s(unsigned long long _Val) noexcept + /* strengthened */ { + return _CHRONO seconds(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""s(long double _Val) noexcept + /* strengthened */ { + return _CHRONO duration(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO milliseconds operator""ms(unsigned long long _Val) noexcept + /* strengthened */ { + return _CHRONO milliseconds(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""ms(long double _Val) noexcept + /* strengthened */ { + return _CHRONO duration(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO microseconds operator""us(unsigned long long _Val) noexcept + /* strengthened */ { + return _CHRONO microseconds(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""us(long double _Val) noexcept + /* strengthened */ { + return _CHRONO duration(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO nanoseconds operator""ns(unsigned long long _Val) noexcept + /* strengthened */ { + return _CHRONO nanoseconds(_Val); + } + + _EXPORT_STD _NODISCARD constexpr _CHRONO duration operator""ns(long double _Val) noexcept + /* strengthened */ { + return _CHRONO duration(_Val); + } + +#if _HAS_CXX20 _EXPORT_STD _NODISCARD constexpr _CHRONO day operator""d(unsigned long long _Day) noexcept { return _CHRONO day{static_cast(_Day)}; } _EXPORT_STD _NODISCARD constexpr _CHRONO year operator""y(unsigned long long _Year) noexcept { return _CHRONO year{static_cast(_Year)}; } +#endif // _HAS_CXX20 } // namespace chrono_literals } // namespace literals -#endif // _HAS_CXX20 +namespace chrono { + _EXPORT_STD using namespace literals::chrono_literals; +} // namespace chrono _STD_END #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS diff --git a/stl/inc/experimental/filesystem b/stl/inc/experimental/filesystem index f5fb649b79b..70e40056d1f 100644 --- a/stl/inc/experimental/filesystem +++ b/stl/inc/experimental/filesystem @@ -8,8 +8,8 @@ #include #if _STL_COMPILER_PREPROCESSOR -#include <__msvc_chrono.hpp> // for chrono::time_point #include // for replace +#include // for chrono::time_point #include // for codecvt_utf8_* #include // for recursive_directory_iterator stack #include // for wstring_convert From 2cef49117df391d505d87deb028798ccda7e3081 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 4 Dec 2024 22:08:53 -0800 Subject: [PATCH 08/35] Maybe Clang _does_ work with `ALTERNATENAME` (#5098) --- stl/inc/xcall_once.h | 3 +- .../GH_002030_asan_annotate_string/env.lst | 79 ++++++++----------- .../GH_002030_asan_annotate_vector/env.lst | 72 +++++++---------- 3 files changed, 59 insertions(+), 95 deletions(-) diff --git a/stl/inc/xcall_once.h b/stl/inc/xcall_once.h index 5057ca65d18..8874e232f81 100644 --- a/stl/inc/xcall_once.h +++ b/stl/inc/xcall_once.h @@ -40,8 +40,7 @@ union _Immortalizer_impl { // constructs _Ty, never destroys _Ty _Storage; }; -#if defined(_M_CEE) || defined(_M_ARM64EC) || defined(_M_HYBRID) \ - || defined(__clang__) // TRANSITION, Clang doesn't recognize /ALTERNATENAME, not yet reported +#if defined(_M_CEE) || defined(_M_ARM64EC) || defined(_M_HYBRID) #define _WINDOWS_API __stdcall #define _RENAME_WINDOWS_API(_Api) _Api##_clr #else // ^^^ use forwarders / use /ALTERNATENAME vvv diff --git a/tests/std/tests/GH_002030_asan_annotate_string/env.lst b/tests/std/tests/GH_002030_asan_annotate_string/env.lst index f1dd40a650d..08acc3dc792 100644 --- a/tests/std/tests/GH_002030_asan_annotate_string/env.lst +++ b/tests/std/tests/GH_002030_asan_annotate_string/env.lst @@ -1,57 +1,40 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# This test matrix is the usual test matrix, with all currently unsupported options removed, crossed with the ASan flags. +# This test matrix is the usual test matrix, with all currently unsupported options removed, with +# some additional /Zc:char8_t coverage, crossed with the ASan flags. # TRANSITION, google/sanitizers#328: clang-cl does not support /MDd or /MTd with ASan RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST PM_CL="/Zi /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug" RUNALL_CROSSLIST -PM_CL="-fsanitize=address /BE /c /EHsc /MD /std:c++14" -PM_CL="-fsanitize=address /BE /c /EHsc /MDd /std:c++17 /permissive-" -PM_CL="-fsanitize=address /BE /c /EHsc /MT /std:c++20 /permissive-" -PM_CL="-fsanitize=address /BE /c /EHsc /MTd /std:c++latest /permissive-" -PM_CL="-fsanitize=address /EHsc /MD /std:c++14" -PM_CL="-fsanitize=address /EHsc /MD /std:c++17" -PM_CL="-fsanitize=address /EHsc /MD /std:c++20" -PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" -PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++17 /permissive-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++20 /permissive-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive-" -PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive-" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /fp:strict" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MD /std:c++14" -PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MDd /std:c++17 /permissive-" -PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MT /std:c++20 /permissive-" -PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MTd /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++14" -PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++14 /Zc:char8_t" -PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++17" -PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++17 /Zc:char8_t" -PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++20" -PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" -PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor" -PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++17 /permissive-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++20 /permissive-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MT /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive" -PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive- /fp:strict" -PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive-" -# TRANSITION, clang-cl does not support /alternatename so we cannot test /D_ANNOTATE_STRING without -fsanitize=address -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14" -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++17" -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /fp:strict" +PM_CL="-fsanitize=address" +PM_CL="/D_ANNOTATE_STRING" +RUNALL_CROSSLIST +PM_CL="/BE /c /EHsc /MD /std:c++14" +PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive-" +PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_CL="/EHsc /MD /std:c++14" +PM_CL="/EHsc /MD /std:c++14 /Zc:char8_t" +PM_CL="/EHsc /MD /std:c++17" +PM_CL="/EHsc /MD /std:c++17 /Zc:char8_t" +PM_CL="/EHsc /MD /std:c++20" +PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" +PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" +PM_CL="/EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor" +PM_CL="/EHsc /MDd /std:c++17 /permissive-" +PM_CL="/EHsc /MDd /std:c++20 /permissive-" +PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" +PM_CL="/EHsc /MDd /std:c++latest /permissive-" +PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MT /std:c++latest /permissive-" +PM_CL="/EHsc /MTd /std:c++latest /permissive" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /fp:strict" +PM_CL="/EHsc /MTd /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /fp:strict" diff --git a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst index 1e9c4211833..4182c344a2e 100644 --- a/tests/std/tests/GH_002030_asan_annotate_vector/env.lst +++ b/tests/std/tests/GH_002030_asan_annotate_vector/env.lst @@ -8,48 +8,30 @@ RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST PM_CL="/Zi /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug" RUNALL_CROSSLIST -PM_CL="-fsanitize=address /BE /c /EHsc /MD /std:c++14" -PM_CL="-fsanitize=address /BE /c /EHsc /MDd /std:c++17 /permissive-" -PM_CL="-fsanitize=address /BE /c /EHsc /MT /std:c++20 /permissive-" -PM_CL="-fsanitize=address /BE /c /EHsc /MTd /std:c++latest /permissive-" -PM_CL="-fsanitize=address /EHsc /MD /std:c++14" -PM_CL="-fsanitize=address /EHsc /MD /std:c++17" -PM_CL="-fsanitize=address /EHsc /MD /std:c++20" -PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" -PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++17 /permissive-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++20 /permissive-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" -PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive-" -PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive-" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /fp:strict" -PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MD /std:c++14" -PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MDd /std:c++17 /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MT /std:c++20 /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /BE /c /EHsc /MTd /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++14" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++17" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++20" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++17 /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++20 /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MDd /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MT /std:c++latest /permissive-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive- /fp:strict" -PM_CL="/D_ANNOTATE_VECTOR /EHsc /MTd /std:c++latest /permissive-" -# TRANSITION, clang-cl does not support /alternatename so we cannot test /D_ANNOTATE_VECTOR without -fsanitize=address -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14" -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++17" -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /fp:strict" +PM_CL="-fsanitize=address" +PM_CL="/D_ANNOTATE_VECTOR" +RUNALL_CROSSLIST +PM_CL="/BE /c /EHsc /MD /std:c++14" +PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive-" +PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_CL="/EHsc /MD /std:c++14" +PM_CL="/EHsc /MD /std:c++17" +PM_CL="/EHsc /MD /std:c++20" +PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" +PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" +PM_CL="/EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor" +PM_CL="/EHsc /MDd /std:c++17 /permissive-" +PM_CL="/EHsc /MDd /std:c++20 /permissive-" +PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" +PM_CL="/EHsc /MDd /std:c++latest /permissive-" +PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MT /std:c++latest /permissive-" +PM_CL="/EHsc /MTd /std:c++latest /permissive" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /fp:strict" +PM_CL="/EHsc /MTd /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /fp:strict" From 126f4eb51a9138eaf94c1c746bd03eadaef995d6 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 4 Dec 2024 22:15:35 -0800 Subject: [PATCH 09/35] Vectorize `basic_string::find` (#5101) --- stl/inc/__msvc_string_view.hpp | 25 +++++++-- .../VSO_0000000_vector_algorithms/test.cpp | 53 +++++++++++++------ 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/stl/inc/__msvc_string_view.hpp b/stl/inc/__msvc_string_view.hpp index aeae9bd2c08..468378cbc78 100644 --- a/stl/inc/__msvc_string_view.hpp +++ b/stl/inc/__msvc_string_view.hpp @@ -660,12 +660,29 @@ template constexpr size_t _Traits_find_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { // search [_Haystack, _Haystack + _Hay_size) for _Ch, at/after _Start_at - if (_Start_at < _Hay_size) { - const auto _Found_at = _Traits::find(_Haystack + _Start_at, _Hay_size - _Start_at, _Ch); - if (_Found_at) { - return static_cast(_Found_at - _Haystack); + if (_Start_at >= _Hay_size) { + return static_cast(-1); // (npos) no room for match + } + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Is_implementation_handled_char_traits<_Traits>) { + if (!_STD _Is_constant_evaluated()) { + const auto _End = _Haystack + _Hay_size; + const auto _Ptr = _STD _Find_vectorized(_Haystack + _Start_at, _End, _Ch); + + if (_Ptr != _End) { + return static_cast(_Ptr - _Haystack); + } else { + return static_cast(-1); // (npos) no match + } } } +#endif // _USE_STD_VECTOR_ALGORITHMS + + const auto _Found_at = _Traits::find(_Haystack + _Start_at, _Hay_size - _Start_at, _Ch); + if (_Found_at) { + return static_cast(_Found_at - _Haystack); + } return static_cast(-1); // (npos) no match } diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index dcc5c59ec46..27ba7340b3f 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -1090,6 +1090,38 @@ void test_case_string_find_last_of(const basic_string& input_haystack, const assert(expected == actual); } +template +void test_case_string_find_ch(const basic_string& input_haystack, const T value) { + ptrdiff_t expected; + + const auto expected_iter = last_known_good_find(input_haystack.begin(), input_haystack.end(), value); + + if (expected_iter != input_haystack.end()) { + expected = expected_iter - input_haystack.begin(); + } else { + expected = -1; + } + + const auto actual = static_cast(input_haystack.find(value)); + assert(expected == actual); +} + +template +void test_case_string_rfind_ch(const basic_string& input_haystack, const T value) { + ptrdiff_t expected; + + const auto expected_iter = last_known_good_find_last(input_haystack.begin(), input_haystack.end(), value); + + if (expected_iter != input_haystack.end()) { + expected = expected_iter - input_haystack.begin(); + } else { + expected = -1; + } + + const auto actual = static_cast(input_haystack.rfind(value)); + assert(expected == actual); +} + template void test_case_string_find_str(const basic_string& input_haystack, const basic_string& input_needle) { ptrdiff_t expected; @@ -1128,22 +1160,6 @@ void test_case_string_rfind_str(const basic_string& input_haystack, const bas assert(expected == actual); } -template -void test_case_string_rfind_ch(const basic_string& input_haystack, const T value) { - ptrdiff_t expected; - - const auto expected_iter = last_known_good_find_last(input_haystack.begin(), input_haystack.end(), value); - - if (expected_iter != input_haystack.end()) { - expected = expected_iter - input_haystack.begin(); - } else { - expected = -1; - } - - const auto actual = static_cast(input_haystack.rfind(value)); - assert(expected == actual); -} - template void test_basic_string_dis(mt19937_64& gen, D& dis) { basic_string input_haystack; @@ -1154,13 +1170,16 @@ void test_basic_string_dis(mt19937_64& gen, D& dis) { temp.reserve(needleDataCount); for (;;) { + const auto input_element = static_cast(dis(gen)); + test_case_string_find_ch(input_haystack, input_element); + test_case_string_rfind_ch(input_haystack, input_element); + input_needle.clear(); test_case_string_find_first_of(input_haystack, input_needle); test_case_string_find_last_of(input_haystack, input_needle); test_case_string_find_str(input_haystack, input_needle); test_case_string_rfind_str(input_haystack, input_needle); - test_case_string_rfind_ch(input_haystack, static_cast(dis(gen))); for (size_t attempts = 0; attempts < needleDataCount; ++attempts) { input_needle.push_back(static_cast(dis(gen))); From 059a1b0f3741de1bb9261d80fa3e799271d8e230 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 4 Dec 2024 22:20:44 -0800 Subject: [PATCH 10/35] Prefer US English for `system_category()` messages, fall back to system locale, then ID 0 (#5104) Co-authored-by: Jcr-dev --- stl/src/syserror_import_lib.cpp | 34 +++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index e2f2d2b7c30..b111662b4a3 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -40,15 +40,33 @@ extern "C" { // convert to name of Windows error, return 0 for failure, otherwise return number of chars in buffer // __std_system_error_deallocate_message should be called even if 0 is returned // pre: *_Ptr_str == nullptr - DWORD _Lang_id; - const int _Ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, - reinterpret_cast(&_Lang_id), sizeof(_Lang_id) / sizeof(wchar_t)); - if (_Ret == 0) { - _Lang_id = 0; + + // We start by requesting US English for system_category() messages. (See GH-2451 and GH-3254 for the history.) + // This is consistent with generic_category(), which uses a table of US English strings in the STL. + // In general, system_error messages aren't directly useful to end-users - they're meant for programmer-users. + // Of course, the programmer-user might not speak US English, but machine translation of the message + // (and the numeric value of the error code) should help them understand the error. + + constexpr auto _Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + + DWORD _Lang_id = 0; + DWORD _Chars = 0; + + for (int _Attempt = 0; _Attempt < 3 && _Chars == 0; ++_Attempt) { + if (_Attempt == 0) { + _Lang_id = 0x0409; // 1033 decimal, "en-US" locale + } else if (_Attempt == 1) { + const int _Ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, + reinterpret_cast(&_Lang_id), sizeof(_Lang_id) / sizeof(wchar_t)); + if (_Ret == 0) { + continue; // If we can't get the system locale's language ID, skip this attempt + } + } else { + _Lang_id = 0; + } + + _Chars = FormatMessageA(_Flags, nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); } - const unsigned long _Chars = - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, _Message_id, _Lang_id, reinterpret_cast(_Ptr_str), 0, nullptr); return _CSTD __std_get_string_size_without_trailing_whitespace(*_Ptr_str, _Chars); } From b60bb7845b026c06da653893eef79b91d1f668a6 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:33:38 +0800 Subject: [PATCH 11/35] Implement LWG-4169 `std::atomic`'s default constructor should be constrained (#5128) Co-authored-by: Casey Carter --- stl/inc/atomic | 27 +++++++------------ .../test.cpp | 13 ++++++++- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/stl/inc/atomic b/stl/inc/atomic index b9b48316567..9f4da0a47e3 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -586,8 +586,7 @@ struct _Atomic_storage { _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept - : _Storage(_Value) { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage(_Value) { // non-atomically initialize this atomic } @@ -714,13 +713,11 @@ public: template struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -817,13 +814,11 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics template struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -919,13 +914,11 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics template struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -1021,13 +1014,11 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics template struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -1148,7 +1139,8 @@ struct _Atomic_storage<_Ty&, 16> { // lock-free using 16-byte intrinsics _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty&, const _TVal> _Value) noexcept + // TRANSITION, ABI: replace _this_ occurrence of '_Ty&' with 'const _Ty&' + /* implicit */ constexpr _Atomic_storage(_Ty& _Value) noexcept : _Storage{_Value} {} // non-atomically initialize this atomic void store(const _TVal _Value) noexcept { // store with sequential consistency @@ -2121,10 +2113,11 @@ public: using value_type = _Ty; - using _Base::_Base; - + template , int> = 0> constexpr atomic() noexcept(is_nothrow_default_constructible_v<_Ty>) : _Base() {} + /* implicit */ constexpr atomic(const _Ty _Value) noexcept : _Base(_Value) {} + atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; atomic& operator=(const atomic&) volatile = delete; diff --git a/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp b/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp index 52958c67c01..5d74ec0cce8 100644 --- a/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp +++ b/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include - using namespace std; #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) @@ -438,6 +438,17 @@ STATIC_ASSERT(atomic::is_always_lock_free); STATIC_ASSERT(atomic::is_always_lock_free); #endif // _HAS_CXX17 +// Also test LWG-4169 std::atomic's default constructor should be constrained +// (backported to C++14/17 modes as we backported P0883R2) +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(!is_default_constructible_v>>); +STATIC_ASSERT(!is_default_constructible_v>>); +STATIC_ASSERT(!is_default_constructible_v>>); + // Also test P0418R2 atomic compare_exchange memory_order Requirements void test_compare_exchange_relaxed_memory_orders() { From d40e8c01289caed6f9a6e911c7b8585374ff4104 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:36:15 +0800 Subject: [PATCH 12/35] Implement LWG-4140 Useless default constructors for bit reference types (#5129) --- stl/inc/bitset | 2 -- stl/inc/vector | 3 --- .../tests/Dev10_860410_bitset_ctors/test.cpp | 19 +++++++++++++++++++ .../test.compile.pass.cpp | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/stl/inc/bitset b/stl/inc/bitset index 68211c8e0f9..b6e493d957c 100644 --- a/stl/inc/bitset +++ b/stl/inc/bitset @@ -109,8 +109,6 @@ public: } private: - _CONSTEXPR23 reference() noexcept : _Pbitset(nullptr), _Mypos(0) {} - _CONSTEXPR23 reference(bitset<_Bits>& _Bitset, const size_t _Pos) noexcept : _Pbitset(&_Bitset), _Mypos(_Pos) {} bitset<_Bits>* _Pbitset; diff --git a/stl/inc/vector b/stl/inc/vector index b0913e25128..4f79f4986c8 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -2444,9 +2444,6 @@ private: using _Mycont = typename _Mybase::_Mycont; using _Difference_type = typename _Mybase::_Difference_type; - // TRANSITION, ABI: non-trivial constructor - _CONSTEXPR20 _Vb_reference() = default; - public: _CONSTEXPR20 _Vb_reference(const _Vb_reference&) = default; diff --git a/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp b/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp index 283dedc4a51..8121cab24df 100644 --- a/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp +++ b/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp @@ -12,6 +12,25 @@ using namespace std; #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) +// Also test LWG-4140 "Useless default constructors for bit reference types" for bitset::reference. +namespace lwg_4140 { + struct default_constructible_type {}; + + void test_default_constructor(default_constructible_type) {} + void test_default_constructor(bitset<0>::reference) {} + void test_default_constructor(bitset<1>::reference) {} + void test_default_constructor(bitset<8>::reference) {} + void test_default_constructor(bitset<16>::reference) {} + void test_default_constructor(bitset<32>::reference) {} + void test_default_constructor(bitset<48>::reference) {} + void test_default_constructor(bitset<64>::reference) {} + void test_default_constructor(bitset<96>::reference) {} + + void test() { // COMPILE-ONLY + test_default_constructor({}); + } +} // namespace lwg_4140 + const char parsedStr[] = "1000110111110011110111111111111111010111110111100101010100001001" "1111111111111111111111111111111111111111111111111111111111111111" "0111111111111111111111111111111111111111111111111111111111111111" diff --git a/tests/std/tests/Dev11_0437519_container_requirements/test.compile.pass.cpp b/tests/std/tests/Dev11_0437519_container_requirements/test.compile.pass.cpp index bfce3726daf..97ba3180c29 100644 --- a/tests/std/tests/Dev11_0437519_container_requirements/test.compile.pass.cpp +++ b/tests/std/tests/Dev11_0437519_container_requirements/test.compile.pass.cpp @@ -3031,6 +3031,23 @@ void assert_vector_bool_noexcept() { assert_vector_bool_noexcept_impl>(); } +// Also test LWG-4140 "Useless default constructors for bit reference types" for vector::reference. +namespace lwg_4140 { + struct default_constructible_type {}; + + void test_default_constructor(default_constructible_type) {} + void test_default_constructor(std::vector::reference) {} + void test_default_constructor(std::vector>::reference) {} + void test_default_constructor(std::vector>::reference) {} +#if _HAS_CXX17 + void test_default_constructor(std::pmr::vector::reference) {} +#endif // _HAS_CXX17 + + void test() { + test_default_constructor({}); + } +} // namespace lwg_4140 + template void assert_container() { check_all_container_requirements(); From 0053a14dd91f35cbcdd136c52589c73ae5d1ae4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Thu, 5 Dec 2024 07:38:35 +0100 Subject: [PATCH 13/35] Fix false positives by `filesystem::equivalent` on file systems with transient file IDs (#5130) Co-authored-by: Stephan T. Lavavej --- stl/inc/filesystem | 31 +++++++------------------------ stl/inc/xfilesystem_abi.h | 14 +++++++------- stl/src/filesys.cpp | 39 +++++++++++++++++++++++---------------- stl/src/filesystem.cpp | 39 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 75 insertions(+), 48 deletions(-) diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 5c321608490..d9a4a547b12 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -3431,38 +3431,21 @@ namespace filesystem { return _STD filesystem::copy_file(_From, _To, copy_options::none); } - _NODISCARD inline pair<__std_win_error, bool> _Equivalent( - const wchar_t* const _Lhs, const wchar_t* const _Rhs) noexcept { - __std_fs_file_id _Left_id; - __std_fs_file_id _Right_id; - auto _Last_error = __std_fs_get_file_id(&_Left_id, _Lhs); - if (_Last_error != __std_win_error::_Success) { - return {_Last_error, false}; - } - - _Last_error = __std_fs_get_file_id(&_Right_id, _Rhs); - if (_Last_error != __std_win_error::_Success) { - return {_Last_error, false}; - } - - return {__std_win_error::_Success, _CSTD memcmp(&_Left_id, &_Right_id, sizeof(__std_fs_file_id)) == 0}; - } - _EXPORT_STD _NODISCARD inline bool equivalent(const path& _Lhs, const path& _Rhs) { // test if the paths _Lhs and _Rhs refer to the same file - const auto _Result = _Equivalent(_Lhs.c_str(), _Rhs.c_str()); - if (_Result.first != __std_win_error::_Success) { - _Throw_fs_error("equivalent", _Result.first, _Lhs, _Rhs); + const auto _Result = __std_fs_equivalent(_Lhs.c_str(), _Rhs.c_str()); + if (_Result._Error != __std_win_error::_Success) { + _Throw_fs_error("equivalent", _Result._Error, _Lhs, _Rhs); } - return _Result.second; + return _Result._Equivalent; } _EXPORT_STD _NODISCARD inline bool equivalent(const path& _Lhs, const path& _Rhs, error_code& _Ec) noexcept { // test if the paths _Lhs and _Rhs refer to the same file - const auto _Result = _Equivalent(_Lhs.c_str(), _Rhs.c_str()); - _Ec = _Make_ec(_Result.first); - return _Result.second; + const auto _Result = __std_fs_equivalent(_Lhs.c_str(), _Rhs.c_str()); + _Ec = _Make_ec(_Result._Error); + return _Result._Equivalent; } _EXPORT_STD _NODISCARD inline file_status status(const path& _Path); diff --git a/stl/inc/xfilesystem_abi.h b/stl/inc/xfilesystem_abi.h index 2813e99951a..f00eaceedb8 100644 --- a/stl/inc/xfilesystem_abi.h +++ b/stl/inc/xfilesystem_abi.h @@ -232,11 +232,6 @@ struct __std_fs_convert_result { __std_win_error _Err; }; -struct __std_fs_file_id { // typedef struct _FILE_ID_INFO { - unsigned long long _Volume_serial_number; // ULONGLONG VolumeSerialNumber; - unsigned char _Id[16]; // FILE_ID_128 FileId; -}; // } FILE_ID_INFO, ...; - enum class __std_fs_copy_options { _None = 0x0, @@ -303,8 +298,13 @@ _NODISCARD __std_fs_convert_result __stdcall __std_fs_convert_wide_to_narrow_rep _In_ __std_code_page _Code_page, _In_reads_(_Input_len) const wchar_t* _Input_str, _In_ int _Input_len, _Out_writes_opt_(_Output_len) char* _Output_str, _In_ int _Output_len) noexcept; -_NODISCARD _Success_(return == __std_win_error::_Success) __std_win_error - __stdcall __std_fs_get_file_id(_Out_ __std_fs_file_id* _Id, _In_z_ const wchar_t* _Path) noexcept; +struct __std_fs_equivalent_result { + bool _Equivalent; + __std_win_error _Error; +}; + +_NODISCARD __std_fs_equivalent_result __stdcall __std_fs_equivalent( + _In_z_ const wchar_t* _Path1, _In_z_ const wchar_t* _Path2) noexcept; _NODISCARD __std_win_error __stdcall __std_fs_set_last_write_time( _In_ long long _Last_write_filetime, _In_z_ const wchar_t* _Path) noexcept; diff --git a/stl/src/filesys.cpp b/stl/src/filesys.cpp index 0bebdc16033..daa5791f577 100644 --- a/stl/src/filesys.cpp +++ b/stl/src/filesys.cpp @@ -320,22 +320,26 @@ _FS_DLL space_info __CLRCALL_PURE_OR_CDECL _Statvfs(const wchar_t* _Fname) noexc _FS_DLL int __CLRCALL_PURE_OR_CDECL _Equivalent( const wchar_t* _Fname1, const wchar_t* _Fname2) noexcept { // test for equivalent file names + // See GH-3571: File IDs are only guaranteed to be unique and stable while handles remain open #ifdef _CRT_APP _FILE_ID_INFO _Info1 = {0}; _FILE_ID_INFO _Info2 = {0}; bool _Ok1 = false; bool _Ok2 = false; - HANDLE _Handle = _FilesysOpenFile(_Fname1, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle != INVALID_HANDLE_VALUE) { // get file1 info - _Ok1 = GetFileInformationByHandleEx(_Handle, FileIdInfo, &_Info1, sizeof(_Info1)) != 0; - CloseHandle(_Handle); + HANDLE _Handle1 = _FilesysOpenFile(_Fname1, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); + if (_Handle1 != INVALID_HANDLE_VALUE) { // get file1 info + _Ok1 = GetFileInformationByHandleEx(_Handle1, FileIdInfo, &_Info1, sizeof(_Info1)) != 0; } - _Handle = _FilesysOpenFile(_Fname2, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle != INVALID_HANDLE_VALUE) { // get file2 info - _Ok2 = GetFileInformationByHandleEx(_Handle, FileIdInfo, &_Info2, sizeof(_Info2)) != 0; - CloseHandle(_Handle); + HANDLE _Handle2 = _FilesysOpenFile(_Fname2, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); + if (_Handle2 != INVALID_HANDLE_VALUE) { // get file2 info + _Ok2 = GetFileInformationByHandleEx(_Handle2, FileIdInfo, &_Info2, sizeof(_Info2)) != 0; + CloseHandle(_Handle2); + } + + if (_Handle1 != INVALID_HANDLE_VALUE) { + CloseHandle(_Handle1); } if (!_Ok1 && !_Ok2) { @@ -351,16 +355,19 @@ _FS_DLL int __CLRCALL_PURE_OR_CDECL _Equivalent( bool _Ok1 = false; bool _Ok2 = false; - HANDLE _Handle = _FilesysOpenFile(_Fname1, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle != INVALID_HANDLE_VALUE) { // get file1 info - _Ok1 = GetFileInformationByHandle(_Handle, &_Info1) != 0; - CloseHandle(_Handle); + HANDLE _Handle1 = _FilesysOpenFile(_Fname1, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); + if (_Handle1 != INVALID_HANDLE_VALUE) { // get file1 info + _Ok1 = GetFileInformationByHandle(_Handle1, &_Info1) != 0; + } + + HANDLE _Handle2 = _FilesysOpenFile(_Fname2, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); + if (_Handle2 != INVALID_HANDLE_VALUE) { // get file2 info + _Ok2 = GetFileInformationByHandle(_Handle2, &_Info2) != 0; + CloseHandle(_Handle2); } - _Handle = _FilesysOpenFile(_Fname2, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle != INVALID_HANDLE_VALUE) { // get file2 info - _Ok2 = GetFileInformationByHandle(_Handle, &_Info2) != 0; - CloseHandle(_Handle); + if (_Handle1 != INVALID_HANDLE_VALUE) { + CloseHandle(_Handle1); } if (!_Ok1 && !_Ok2) { diff --git a/stl/src/filesystem.cpp b/stl/src/filesystem.cpp index ba05446a1d7..25512bdfcd4 100644 --- a/stl/src/filesystem.cpp +++ b/stl/src/filesystem.cpp @@ -434,7 +434,13 @@ void __stdcall __std_fs_directory_iterator_close(_In_ const __std_fs_dir_handle return __vcp_Copyfile(_Source, _Target, /* _Fail_if_exists = */ false); } -_Success_(return == __std_win_error::_Success) __std_win_error +struct __std_fs_file_id { // typedef struct _FILE_ID_INFO { + unsigned long long _Volume_serial_number; // ULONGLONG VolumeSerialNumber; + unsigned char _Id[16]; // FILE_ID_128 FileId; +}; // } FILE_ID_INFO, ...; + +// TRANSITION, ABI: preserved for binary compatibility +[[nodiscard]] _Success_(return == __std_win_error::_Success) __std_win_error __stdcall __std_fs_get_file_id(_Out_ __std_fs_file_id* const _Id, _In_z_ const wchar_t* const _Path) noexcept { __std_win_error _Last_error; const _STD _Fs_file _Handle( @@ -448,6 +454,37 @@ _Success_(return == __std_win_error::_Success) __std_win_error return _Get_file_id_by_handle(_Handle._Get(), reinterpret_cast(_Id)); } +[[nodiscard]] __std_fs_equivalent_result __stdcall __std_fs_equivalent( + _In_z_ const wchar_t* const _Left_path, _In_z_ const wchar_t* const _Right_path) noexcept { + // See GH-3571: File IDs are only guaranteed to be unique and stable while handles remain open + __std_win_error _Last_error; + const _STD _Fs_file _Left_handle( + _Left_path, __std_access_rights::_File_read_attributes, __std_fs_file_flags::_Backup_semantics, &_Last_error); + if (_Last_error != __std_win_error::_Success) { + return {false, _Last_error}; + } + + FILE_ID_INFO _Left_info; + _Last_error = _Get_file_id_by_handle(_Left_handle._Get(), &_Left_info); + if (_Last_error != __std_win_error::_Success) { + return {false, _Last_error}; + } + + const _STD _Fs_file _Right_handle( + _Right_path, __std_access_rights::_File_read_attributes, __std_fs_file_flags::_Backup_semantics, &_Last_error); + if (_Last_error != __std_win_error::_Success) { + return {false, _Last_error}; + } + + FILE_ID_INFO _Right_info; + _Last_error = _Get_file_id_by_handle(_Right_handle._Get(), &_Right_info); + if (_Last_error != __std_win_error::_Success) { + return {false, _Last_error}; + } + + return {_CSTD memcmp(&_Left_info, &_Right_info, sizeof(FILE_ID_INFO)) == 0, __std_win_error::_Success}; +} + [[nodiscard]] __std_win_error __stdcall __std_fs_create_directory_symbolic_link( _In_z_ const wchar_t* const _Symlink_file_name, _In_z_ const wchar_t* const _Target_file_name) noexcept { return _Create_symlink(_Symlink_file_name, _Target_file_name, SYMBOLIC_LINK_FLAG_DIRECTORY); From a8fea5bb98e856a16ee1fe5b14f34bccb952e194 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:42:28 +0800 Subject: [PATCH 14/35] Implement LWG-4135 The helper lambda of `std::erase` for `list` should specify return type as `bool` (#5131) Co-authored-by: Stephan T. Lavavej --- stl/inc/forward_list | 2 +- stl/inc/list | 2 +- tests/std/test.lst | 1 + .../test.cpp | 117 ------------ .../std/tests/P1209R0_erase_if_erase/env.lst | 4 + .../std/tests/P1209R0_erase_if_erase/test.cpp | 174 ++++++++++++++++++ 6 files changed, 181 insertions(+), 119 deletions(-) create mode 100644 tests/std/tests/P1209R0_erase_if_erase/env.lst create mode 100644 tests/std/tests/P1209R0_erase_if_erase/test.cpp diff --git a/stl/inc/forward_list b/stl/inc/forward_list index 993bb3ac575..0d556e5639a 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -1616,7 +1616,7 @@ _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward #if _HAS_CXX20 _EXPORT_STD template forward_list<_Ty, _Alloc>::size_type erase(forward_list<_Ty, _Alloc>& _Cont, const _Uty& _Val) { - return _Cont.remove_if([&](_Ty& _Elem) -> bool { return _Elem == _Val; }); + return _Cont.remove_if([&](const _Ty& _Elem) -> bool { return _Elem == _Val; }); } _EXPORT_STD template diff --git a/stl/inc/list b/stl/inc/list index d98a72d281e..58d87142ffe 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -1921,7 +1921,7 @@ _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Allo #if _HAS_CXX20 _EXPORT_STD template list<_Ty, _Alloc>::size_type erase(list<_Ty, _Alloc>& _Cont, const _Uty& _Val) { - return _Cont.remove_if([&](_Ty& _Elem) -> bool { return _Elem == _Val; }); + return _Cont.remove_if([&](const _Ty& _Elem) -> bool { return _Elem == _Val; }); } _EXPORT_STD template diff --git a/tests/std/test.lst b/tests/std/test.lst index 42163db9973..d8a6919fde3 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -588,6 +588,7 @@ tests\P1206R7_vector_assign_range tests\P1206R7_vector_from_range tests\P1206R7_vector_insert_range tests\P1208R6_source_location +tests\P1209R0_erase_if_erase tests\P1223R5_ranges_alg_find_last tests\P1223R5_ranges_alg_find_last_if tests\P1223R5_ranges_alg_find_last_if_not diff --git a/tests/std/tests/Dev11_0000000_user_defined_literals/test.cpp b/tests/std/tests/Dev11_0000000_user_defined_literals/test.cpp index de11548135a..21fc66fa561 100644 --- a/tests/std/tests/Dev11_0000000_user_defined_literals/test.cpp +++ b/tests/std/tests/Dev11_0000000_user_defined_literals/test.cpp @@ -435,123 +435,6 @@ int main() { assert(!const_us.contains(1)); assert(!const_ums.contains(3)); } - - // P1209R0 erase_if(), erase() - { - // Note that the standard actually requires these to be copyable. As an extension, we want - // to ensure we don't copy them, because copying some functors (e.g. std::function) is comparatively - // expensive, and even for relatively cheap to copy function objects we care (somewhat) about debug - // mode perf. - struct no_copy { - no_copy() = default; - no_copy(const no_copy&) = delete; - no_copy(no_copy&&) = default; - no_copy& operator=(const no_copy&) = delete; - no_copy& operator=(no_copy&&) = delete; - }; - - struct is_vowel : no_copy { - bool operator()(const char c) const { - return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'; - } - }; - - std::string str1{"cute fluffy kittens"}; - const auto str1_removed = std::erase_if(str1, is_vowel{}); - assert(str1 == "ct flffy kttns"); - assert(str1_removed == 5); - - std::string str2{"asynchronous beat"}; - const auto str2_removed = std::erase(str2, 'a'); - assert(str2 == "synchronous bet"); - assert(str2_removed == 2); - - struct is_odd : no_copy { - bool operator()(const int i) const { - return i % 2 != 0; - } - }; - - std::deque d{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1}; - const auto d_removed = std::erase_if(d, is_odd{}); - assert((d == std::deque{2, 4, 6, 6, 4, 2})); - assert(d_removed == 7); - const auto d_removed2 = std::erase(d, 4); - assert((d == std::deque{2, 6, 6, 2})); - assert(d_removed2 == 2); - - std::vector v{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1}; - const auto v_removed = std::erase_if(v, is_odd{}); - assert((v == std::vector{2, 4, 6, 6, 4, 2})); - assert(v_removed == 7); - const auto v_removed2 = std::erase(v, 4); - assert((v == std::vector{2, 6, 6, 2})); - assert(v_removed2 == 2); - - std::forward_list fl{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1}; - const auto fl_removed = std::erase_if(fl, is_odd{}); - assert((fl == std::forward_list{2, 4, 6, 6, 4, 2})); - assert(fl_removed == 7); - const auto fl_removed2 = std::erase(fl, 4); - assert((fl == std::forward_list{2, 6, 6, 2})); - assert(fl_removed2 == 2); - - std::list l{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1}; - const auto l_removed = std::erase_if(l, is_odd{}); - assert((l == std::list{2, 4, 6, 6, 4, 2})); - assert(l_removed == 7); - const auto l_removed2 = std::erase(l, 4); - assert((l == std::list{2, 6, 6, 2})); - assert(l_removed2 == 2); - - struct is_first_odd : no_copy { - bool operator()(const std::pair& p) const { - return p.first % 2 != 0; - } - }; - - std::map m{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; - const auto m_removed = std::erase_if(m, is_first_odd{}); - assert((m == std::map{{2, 20}, {4, 40}, {6, 60}})); - assert(m_removed == 4); - - std::multimap mm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; - const auto mm_removed = std::erase_if(mm, is_first_odd{}); - assert((mm == std::multimap{{2, 20}, {4, 40}, {6, 60}})); - assert(mm_removed == 4); - - std::set s{1, 2, 3, 4, 5, 6, 7}; - const auto s_removed = std::erase_if(s, is_odd{}); - assert((s == std::set{2, 4, 6})); - assert(s_removed == 4); - - std::multiset ms{1, 2, 3, 4, 5, 6, 7}; - const auto ms_removed = std::erase_if(ms, is_odd{}); - assert((ms == std::multiset{2, 4, 6})); - assert(ms_removed == 4); - - // Note that unordered equality considers permutations. - - std::unordered_map um{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; - const auto um_removed = std::erase_if(um, is_first_odd{}); - assert((um == std::unordered_map{{2, 20}, {4, 40}, {6, 60}})); - assert(um_removed == 4); - - std::unordered_multimap umm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; - const auto umm_removed = std::erase_if(umm, is_first_odd{}); - assert((umm == std::unordered_multimap{{2, 20}, {4, 40}, {6, 60}})); - assert(umm_removed == 4); - - std::unordered_set us{1, 2, 3, 4, 5, 6, 7}; - const auto us_removed = std::erase_if(us, is_odd{}); - assert((us == std::unordered_set{2, 4, 6})); - assert(us_removed == 4); - - std::unordered_multiset ums{1, 2, 3, 4, 5, 6, 7}; - const auto ums_removed = std::erase_if(ums, is_odd{}); - assert((ums == std::unordered_multiset{2, 4, 6})); - assert(ums_removed == 4); - } #endif // _HAS_CXX20 // P0007R1 as_const() diff --git a/tests/std/tests/P1209R0_erase_if_erase/env.lst b/tests/std/tests/P1209R0_erase_if_erase/env.lst new file mode 100644 index 00000000000..351a8293d9d --- /dev/null +++ b/tests/std/tests/P1209R0_erase_if_erase/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_20_matrix.lst diff --git a/tests/std/tests/P1209R0_erase_if_erase/test.cpp b/tests/std/tests/P1209R0_erase_if_erase/test.cpp new file mode 100644 index 00000000000..5f28d45b470 --- /dev/null +++ b/tests/std/tests/P1209R0_erase_if_erase/test.cpp @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Note that the standard actually requires these to be copyable. As an extension, we want to ensure we don't copy them, +// because copying some functors (e.g. std::function) is comparatively expensive, and even for relatively cheap to copy +// function objects we care (somewhat) about debug mode perf. +struct no_copy { + no_copy() = default; + no_copy(const no_copy&) = delete; + no_copy(no_copy&&) = default; + no_copy& operator=(const no_copy&) = delete; + no_copy& operator=(no_copy&&) = delete; +}; + +struct is_vowel : no_copy { + constexpr bool operator()(const char c) const { + return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'; + } +}; + +struct is_odd : no_copy { + constexpr bool operator()(const int i) const { + return i % 2 != 0; + } +}; + +struct is_first_odd : no_copy { + bool operator()(const std::pair& p) const { + return p.first % 2 != 0; + } +}; + +constexpr bool test_string() { + std::string str1{"cute fluffy kittens"}; + const auto str1_removed = std::erase_if(str1, is_vowel{}); + assert(str1 == "ct flffy kttns"); + assert(str1_removed == 5); + + std::string str2{"asynchronous beat"}; + const auto str2_removed = std::erase(str2, 'a'); + assert(str2 == "synchronous bet"); + assert(str2_removed == 2); + + return true; +} + +template +constexpr bool test_sequence_container() { + SequenceContainer c{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, + 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0}; + + { + const auto removed1 = std::erase_if(c, is_odd{}); + assert(removed1 == 31); + const SequenceContainer expected1{4, 2, 6, 8, 2, 8, 4, 6, 2, 6, 4, 8, 2, 0, 2, 8, 8, 4, 6, 0}; + assert(c == expected1); + } + + { + const auto removed2 = std::erase(c, 8); + assert(removed2 == 5); + const SequenceContainer expected2{4, 2, 6, 2, 4, 6, 2, 6, 4, 2, 0, 2, 4, 6, 0}; + assert(c == expected2); + } + + return true; +} + +// Also test LWG-4135 "The helper lambda of std::erase for list should specify return type as bool" + +template +struct pinned_condition { + explicit pinned_condition() = default; + pinned_condition(const pinned_condition&) = delete; + pinned_condition& operator=(const pinned_condition&) = delete; + + operator bool() const { + return B; + } + + pinned_condition operator!() const { + return {}; + } +}; + +struct lwg_4135_src { + static constexpr pinned_condition result{}; + + friend void operator==(int&, const lwg_4135_src&) = delete; + friend void operator==(const lwg_4135_src&, int&) = delete; + + friend const pinned_condition& operator==(const lwg_4135_src&, const int&) { + return result; + } + friend const pinned_condition& operator==(const int&, const lwg_4135_src&) { + return result; + } +}; + +template +void test_list_erase() { + ListContainer ls2{42, 1729}; + const auto ls2_removed = std::erase(ls2, lwg_4135_src{}); + assert(ls2.empty()); + assert(ls2_removed == 2); +} + +static_assert(test_string()); +static_assert(test_sequence_container>()); + +int main() { + test_string(); + test_sequence_container>(); + test_sequence_container>(); + test_sequence_container>(); + test_sequence_container>(); + + test_list_erase>(); + test_list_erase>(); + + std::map m{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; + const auto m_removed = std::erase_if(m, is_first_odd{}); + assert((m == std::map{{2, 20}, {4, 40}, {6, 60}})); + assert(m_removed == 4); + + std::multimap mm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; + const auto mm_removed = std::erase_if(mm, is_first_odd{}); + assert((mm == std::multimap{{2, 20}, {4, 40}, {6, 60}})); + assert(mm_removed == 4); + + std::set s{1, 2, 3, 4, 5, 6, 7}; + const auto s_removed = std::erase_if(s, is_odd{}); + assert((s == std::set{2, 4, 6})); + assert(s_removed == 4); + + std::multiset ms{1, 2, 3, 4, 5, 6, 7}; + const auto ms_removed = std::erase_if(ms, is_odd{}); + assert((ms == std::multiset{2, 4, 6})); + assert(ms_removed == 4); + + // Note that unordered equality considers permutations. + + std::unordered_map um{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; + const auto um_removed = std::erase_if(um, is_first_odd{}); + assert((um == std::unordered_map{{2, 20}, {4, 40}, {6, 60}})); + assert(um_removed == 4); + + std::unordered_multimap umm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}}; + const auto umm_removed = std::erase_if(umm, is_first_odd{}); + assert((umm == std::unordered_multimap{{2, 20}, {4, 40}, {6, 60}})); + assert(umm_removed == 4); + + std::unordered_set us{1, 2, 3, 4, 5, 6, 7}; + const auto us_removed = std::erase_if(us, is_odd{}); + assert((us == std::unordered_set{2, 4, 6})); + assert(us_removed == 4); + + std::unordered_multiset ums{1, 2, 3, 4, 5, 6, 7}; + const auto ums_removed = std::erase_if(ums, is_odd{}); + assert((ums == std::unordered_multiset{2, 4, 6})); + assert(ums_removed == 4); +} From abefd5e89559608f4ed54f435aff19a128937dcb Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:44:50 +0800 Subject: [PATCH 15/35] Implement LWG-4014 LWG-3809 changes behavior of some existing `std::subtract_with_carry_engine` code (#5132) Co-authored-by: Stephan T. Lavavej --- stl/inc/random | 2 +- tests/std/tests/Dev11_0577418_random_seed_0/test.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/random b/stl/inc/random index d943cd1305c..09b7418d93e 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -827,7 +827,7 @@ public: void seed(_Seed_t _Value = 0u, bool _Readcy = false) { // set initial values from specified seed value linear_congruential_engine _Lc{ - static_cast(_Value == 0U ? default_seed : _Value)}; + _Value == 0U ? default_seed : static_cast(_Value % 2147483563U)}; _Reset(_Lc, _Readcy); } diff --git a/tests/std/tests/Dev11_0577418_random_seed_0/test.cpp b/tests/std/tests/Dev11_0577418_random_seed_0/test.cpp index 3bfbefecbc8..0abbabcbe6d 100644 --- a/tests/std/tests/Dev11_0577418_random_seed_0/test.cpp +++ b/tests/std/tests/Dev11_0577418_random_seed_0/test.cpp @@ -24,14 +24,14 @@ int main() { // Also test VSO-214595 "subtract_with_carry_engine::seed should accept result_type" subtract_with_carry_engine ull_swc; ull_swc.seed(0x12341234'00000000ULL); - assert(run_10k(ull_swc) == 0x1DD6C263'C41EEED0ULL); // value changed by LWG-3809 + assert(run_10k(ull_swc) == 0x01316AEA'3646F686ULL); // libstdc++ 14.2 and libc++ 19.1 agree, Boost 1.86.0 disagrees assert(minstd_rand0(0) == minstd_rand0()); // N4964 [rand.eng.lcong]/5 assert(mt19937(0) != mt19937()); // N4964 [rand.eng.mers]/6 assert(ranlux24_base(0) == ranlux24_base()); // N4964 [rand.eng.sub]/7 assert(run_10k(minstd_rand0(0)) == 1043618065UL); // QED - assert(run_10k(mt19937(0)) == 1543171712UL); // Boost 1.52.0 agrees + assert(run_10k(mt19937(0)) == 1543171712UL); // Boost 1.86.0 agrees assert(run_10k(ranlux24_base(0)) == 7937952UL); // QED // Also test LWG-3809 Is std::subtract_with_carry_engine supposed to work? From 649a4f2d2b96a330fe93fbbc1f9997f11b017cfa Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:47:45 +0800 Subject: [PATCH 16/35] Implement LWG-3918 `std::uninitialized_move/_n` and guaranteed copy elision (#5135) Co-authored-by: Stephan T. Lavavej --- stl/inc/execution | 5 - stl/inc/memory | 4 +- stl/inc/xmemory | 49 +++++-- .../test.cpp | 125 ++++++++++++++++++ .../tests/P0784R7_library_machinery/test.cpp | 6 + 5 files changed, 168 insertions(+), 21 deletions(-) diff --git a/stl/inc/execution b/stl/inc/execution index a761ef58ed6..9362037496e 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -137,11 +137,6 @@ template <> struct is_execution_policy : true_type {}; #endif // _HAS_CXX20 -template -void _Construct_in_place_by_deref(_Ty& _Val, const _FwdIt& _Iter) { - ::new (static_cast(_STD addressof(_Val))) _Ty(*_Iter); -} - template void _Construct_in_place_by_transform_deref(_Ty& _Val, _UnaryOp _Transform_op, const _FwdIt& _Iter) { ::new (static_cast(_STD addressof(_Val))) _Ty(_Transform_op(*_Iter)); diff --git a/stl/inc/memory b/stl/inc/memory index 67a10039d0b..0e963a59e0a 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -141,7 +141,7 @@ _NoThrowFwdIt uninitialized_copy_n(const _InIt _First, const _Diff _Count_raw, _ _Uninitialized_backout _Backout{_UDest}; for (; _Count > 0; --_Count, (void) ++_UFirst) { - _Backout._Emplace_back(*_UFirst); + _Backout._Emplace_back_deref(_UFirst); } _UDest = _Backout._Release(); @@ -294,7 +294,7 @@ pair<_InIt, _NoThrowFwdIt> uninitialized_move_n(_InIt _First, const _Diff _Count _Uninitialized_backout _Backout{_UDest}; for (; _Count > 0; --_Count, (void) ++_UFirst) { - _Backout._Emplace_back(_STD move(*_UFirst)); + _Backout._Emplace_back_deref_move(_UFirst); } _UDest = _Backout._Release(); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 6060a08635f..a5efe180402 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1601,6 +1601,12 @@ void _Return_temporary_buffer(_Ty* const _Pbuf) noexcept { } } +template +void _Construct_in_place_by_deref(_Ty& _Val, const _InIt& _Iter) + noexcept(noexcept(::new (static_cast(_STD addressof(_Val))) _Ty(*_Iter))) { + ::new (static_cast(_STD addressof(_Val))) _Ty(*_Iter); +} + template struct _NODISCARD _Uninitialized_backout { // struct to undo partially constructed ranges in _Uninitialized_xxx algorithms @@ -1625,6 +1631,25 @@ struct _NODISCARD _Uninitialized_backout { ++_Last; } + template + void _Emplace_back_deref(const _InIt& _Iter) { + // construct a new element at *_Last from the result of dereferencing _Iter and increment. + _STD _Construct_in_place_by_deref(*_Last, _Iter); + ++_Last; + } + + template + void _Emplace_back_deref_move(const _InIt& _Iter) { + // construct a new element at *_Last from the result of dereferencing _Iter and increment, + // with lvalue cast to xvalue if necessary for uninitialized_move(_n). + if constexpr (is_lvalue_reference_v) { + _STD _Construct_in_place(*_Last, _STD move(*_Iter)); + } else { + _STD _Construct_in_place_by_deref(*_Last, _Iter); + } + ++_Last; + } + constexpr _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last _First = _Last; return _Last; @@ -1632,19 +1657,17 @@ struct _NODISCARD _Uninitialized_backout { }; template -_CONSTEXPR20 _NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { +_NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // move [_First, _Last) to raw [_Dest, ...) if constexpr (_Iter_move_cat<_InIt, _NoThrowFwdIt>::_Bitcopy_constructible) { -#if _HAS_CXX20 +#if 0 // TRANSITION, _HAS_CXX26 if (!_STD is_constant_evaluated()) -#endif // _HAS_CXX20 - { - return _STD _Copy_memmove(_First, _Last, _Dest); - } +#endif // _HAS_CXX26 + { return _STD _Copy_memmove(_First, _Last, _Dest); } } _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; for (; _First != _Last; ++_First) { - _Backout._Emplace_back(_STD move(*_First)); + _Backout._Emplace_back_deref_move(_First); } return _Backout._Release(); @@ -1905,20 +1928,18 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_copy_n( } template -_CONSTEXPR20 _NoThrowFwdIt _Uninitialized_copy_unchecked(_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { +_NoThrowFwdIt _Uninitialized_copy_unchecked(_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // copy [_First, _Last) to raw [_Dest, ...) if constexpr (_Iter_copy_cat<_InIt, _NoThrowFwdIt>::_Bitcopy_constructible) { -#if _HAS_CXX20 +#if 0 // TRANSITION, _HAS_CXX26 if (!_STD is_constant_evaluated()) -#endif // _HAS_CXX20 - { - return _STD _Copy_memmove(_First, _Last, _Dest); - } +#endif // _HAS_CXX26 + { return _STD _Copy_memmove(_First, _Last, _Dest); } } _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; for (; _First != _Last; ++_First) { - _Backout._Emplace_back(*_First); + _Backout._Emplace_back_deref(_First); } return _Backout._Release(); diff --git a/tests/std/tests/P0040R3_extending_memory_management_tools/test.cpp b/tests/std/tests/P0040R3_extending_memory_management_tools/test.cpp index 2e122c02853..26b53c0e6e0 100644 --- a/tests/std/tests/P0040R3_extending_memory_management_tools/test.cpp +++ b/tests/std/tests/P0040R3_extending_memory_management_tools/test.cpp @@ -58,6 +58,10 @@ template struct uninitialized_storage { alignas(T) char storage[sizeof(T) * Count]; + uninitialized_storage() { + fill(std::begin(storage), std::end(storage), fillChar); + } + T* begin() { return &reinterpret_cast(storage); } @@ -196,6 +200,122 @@ void test_destroy_n() { assert(g_alive == 0); } +struct copy_elision_dest; + +class pinned { +public: + explicit pinned(int n) : n_{n} {} + + pinned(const pinned&) = delete; + pinned& operator=(const pinned&) = delete; + +private: + friend copy_elision_dest; + + int n_; +}; + +class pinned_ioterator { +private: + struct arrow_proxy { + pinned val_; + + pinned* operator->() { + return &val_; + } + }; + +public: + using iterator_category = input_iterator_tag; + using difference_type = int; + using value_type = pinned; + using pointer = arrow_proxy; + using reference = pinned; + + explicit pinned_ioterator(int n) : n_{n} {} + + pinned operator*() const { + return pinned{n_}; + } + pinned_ioterator& operator++() { + ++n_; + return *this; + } + pinned_ioterator operator++(int) { + auto old = *this; + ++*this; + return old; + } + + arrow_proxy operator->() const { + return arrow_proxy{pinned{n_}}; + } + + friend bool operator==(pinned_ioterator i, pinned_ioterator j) { + return i.n_ == j.n_; + } +#if !_HAS_CXX20 + friend bool operator!=(pinned_ioterator i, pinned_ioterator j) { + return !(i == j); + } +#endif // !_HAS_CXX20 + +private: + int n_; +}; + +struct copy_elision_dest { + explicit copy_elision_dest(pinned x) : n_{x.n_} {} + + int n_; +}; + +// std::uninitialized_copy/_n are required to perform guaranteed copy elision since C++17. +void test_guaranteed_copy_elision_uninitialized_copy() { + constexpr int len = 42; + + uninitialized_storage us; + uninitialized_copy(pinned_ioterator{0}, pinned_ioterator{len}, us.begin()); + for (int i = 0; i != len; ++i) { + assert(us.begin()[i].n_ == i); + } + destroy(us.begin(), us.end()); +} + +void test_guaranteed_copy_elision_uninitialized_copy_n() { + constexpr int len = 42; + + uninitialized_storage us; + uninitialized_copy_n(pinned_ioterator{0}, len, us.begin()); + for (int i = 0; i != len; ++i) { + assert(us.begin()[i].n_ == i); + } + destroy(us.begin(), us.end()); +} + +// Also test LWG-3918 "std::uninitialized_move/_n and guaranteed copy elision". +void test_guaranteed_copy_elision_uninitialized_move() { + constexpr int len = 42; + + uninitialized_storage us; + uninitialized_move(pinned_ioterator{0}, pinned_ioterator{len}, us.begin()); + for (int i = 0; i != len; ++i) { + assert(us.begin()[i].n_ == i); + } + destroy(us.begin(), us.end()); +} + +void test_guaranteed_copy_elision_uninitialized_move_n() { + constexpr int len = 42; + + uninitialized_storage us; + uninitialized_move_n(pinned_ioterator{0}, len, us.begin()); + for (int i = 0; i != len; ++i) { + assert(us.begin()[i].n_ == i); + } + destroy(us.begin(), us.end()); +} + int main() { test_uninitialized_move(); test_uninitialized_move_n(); @@ -206,4 +326,9 @@ int main() { test_destroy_at(); test_destroy(); test_destroy_n(); + + test_guaranteed_copy_elision_uninitialized_copy(); + test_guaranteed_copy_elision_uninitialized_copy_n(); + test_guaranteed_copy_elision_uninitialized_move(); + test_guaranteed_copy_elision_uninitialized_move_n(); } diff --git a/tests/std/tests/P0784R7_library_machinery/test.cpp b/tests/std/tests/P0784R7_library_machinery/test.cpp index 45b50757802..67ec32be837 100644 --- a/tests/std/tests/P0784R7_library_machinery/test.cpp +++ b/tests/std/tests/P0784R7_library_machinery/test.cpp @@ -84,6 +84,9 @@ constexpr bool test() { assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output))); } +#if 1 // TRANSITION, !_HAS_CXX26 + if (!is_constant_evaluated()) +#endif // !_HAS_CXX26 { // _Uninitialized_copy_unchecked int_wrapper_copy input[] = {1, 2, 3, 4}; int_wrapper_copy output[4]; @@ -133,6 +136,9 @@ constexpr bool test() { } } +#if 1 // TRANSITION, !_HAS_CXX26 + if (!is_constant_evaluated()) +#endif // !_HAS_CXX26 { // _Uninitialized_move_unchecked int_wrapper_move input[] = {1, 2, 3, 4}; int_wrapper_move output[4]; From e0b8a116383aa7eca4755acfb8c6090559d625bc Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:51:03 +0800 Subject: [PATCH 17/35] Implement LWG-4084 `std::fixed` ignores `std::uppercase` (#5151) Co-authored-by: Stephan T. Lavavej --- stl/inc/xlocnum | 2 +- tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 104 ++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tests/std/tests/LWG4084_iostream_uppercase_inf_nan/env.lst create mode 100644 tests/std/tests/LWG4084_iostream_uppercase_inf_nan/test.cpp diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 43b8532518f..36682328eb6 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1435,7 +1435,7 @@ private: ios_base::fmtflags _Ffl = _Flags & ios_base::floatfield; if (_Flags & ios_base::uppercase) { if (_Ffl == ios_base::fixed) { - _Ch = 'f'; + _Ch = 'F'; } else if (_Ffl == (ios_base::scientific | ios_base::fixed)) { _Ch = 'A'; } else if (_Ffl == ios_base::scientific) { diff --git a/tests/std/test.lst b/tests/std/test.lst index d8a6919fde3..7b8847181e6 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -265,6 +265,7 @@ tests\LWG3528_make_from_tuple_impl tests\LWG3545_pointer_traits_sfinae tests\LWG3561_discard_block_engine_counter tests\LWG3610_iota_view_size_and_integer_class +tests\LWG4084_iostream_uppercase_inf_nan tests\LWG4105_ranges_ends_with_and_integer_class tests\P0009R18_mdspan_default_accessor tests\P0009R18_mdspan_extents diff --git a/tests/std/tests/LWG4084_iostream_uppercase_inf_nan/env.lst b/tests/std/tests/LWG4084_iostream_uppercase_inf_nan/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/LWG4084_iostream_uppercase_inf_nan/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/LWG4084_iostream_uppercase_inf_nan/test.cpp b/tests/std/tests/LWG4084_iostream_uppercase_inf_nan/test.cpp new file mode 100644 index 00000000000..4f9acdc6213 --- /dev/null +++ b/tests/std/tests/LWG4084_iostream_uppercase_inf_nan/test.cpp @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; + +template , int> = 0> +constexpr const auto& choose_literal(const char (&s)[N], const wchar_t (&)[N]) noexcept { + return s; +} +template , int> = 0> +constexpr const auto& choose_literal(const char (&)[N], const wchar_t (&ws)[N]) noexcept { + return ws; +} + +#define STATICALLY_WIDEN(CharT, S) ::choose_literal(S, L##S) + +template +void test() { + // LWG-4084 "std::fixed ignores std::uppercase" + { + auto s = (basic_ostringstream{} << fixed << uppercase << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "INF")); + } + { + auto s = (basic_ostringstream{} << fixed << uppercase << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "NAN")); + } + // also test other combinations + { + auto s = (basic_ostringstream{} << fixed << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "inf")); + } + { + auto s = (basic_ostringstream{} << fixed << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "nan")); + } + + { + auto s = (basic_ostringstream{} << uppercase << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "INF")); + } + { + auto s = (basic_ostringstream{} << uppercase << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "NAN")); + } + { + auto s = (basic_ostringstream{} << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "inf")); + } + { + auto s = (basic_ostringstream{} << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "nan")); + } + + { + auto s = (basic_ostringstream{} << scientific << uppercase << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "INF")); + } + { + auto s = (basic_ostringstream{} << scientific << uppercase << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "NAN")); + } + { + auto s = (basic_ostringstream{} << scientific << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "inf")); + } + { + auto s = (basic_ostringstream{} << scientific << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "nan")); + } + + { + auto s = (basic_ostringstream{} << hexfloat << uppercase << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "INF")); + } + { + auto s = (basic_ostringstream{} << hexfloat << uppercase << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "NAN")); + } + { + auto s = (basic_ostringstream{} << hexfloat << numeric_limits::infinity()).str(); + assert(s == STATICALLY_WIDEN(CharT, "inf")); + } + { + auto s = (basic_ostringstream{} << hexfloat << numeric_limits::quiet_NaN()).str(); + assert(s == STATICALLY_WIDEN(CharT, "nan")); + } +} + +int main() { + test(); + test(); + test(); + + test(); + test(); + test(); +} From a4ae6c3555401773b70a278026c611900b90d8dc Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:53:26 +0800 Subject: [PATCH 18/35] Implement LWG-4112 `has-arrow` should require `operator->()` to be `const`-qualified (#5152) Co-authored-by: Casey Carter --- stl/inc/ranges | 2 +- tests/libcxx/expected_results.txt | 3 ++ tests/std/tests/P0896R4_views_filter/test.cpp | 28 +++++++++++++++ tests/std/tests/P0896R4_views_join/test.cpp | 35 +++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 37bb02261d7..38289d7320a 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -89,7 +89,7 @@ namespace ranges { && same_as, sentinel_t>; template - concept _Has_arrow = input_iterator<_It> && (is_pointer_v<_It> || _Has_member_arrow<_It&>); + concept _Has_arrow = input_iterator<_It> && (is_pointer_v<_It> || _Has_member_arrow); template using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 28d2fc987aa..56e77fbc616 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -152,6 +152,9 @@ std/ranges/range.factories/range.iota.view/iterator/member_typedefs.compile.pass std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp FAIL std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp FAIL +# libc++ doesn't implement LWG-4112 +std/ranges/range.adaptors/range.join/range.join.iterator/arrow.pass.cpp FAIL + # If any feature-test macro test is failing, this consolidated test will also fail. std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp FAIL diff --git a/tests/std/tests/P0896R4_views_filter/test.cpp b/tests/std/tests/P0896R4_views_filter/test.cpp index 3af705a5198..34f7f04a2dd 100644 --- a/tests/std/tests/P0896R4_views_filter/test.cpp +++ b/tests/std/tests/P0896R4_views_filter/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -347,6 +348,33 @@ using move_only_view = test::range}, test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; +// LWG-4112 "has-arrow should require operator->() to be const-qualified" + +template +concept CanArrow = requires(T&& t) { forward(t).operator->(); }; + +enum class arrow_status : bool { bad, good }; + +template +struct arrowed_iterator { + using difference_type = ptrdiff_t; + using value_type = int; + + int& operator*() const; + int* operator->() + requires (S == arrow_status::bad); + int* operator->() const + requires (S == arrow_status::good); + arrowed_iterator& operator++(); + arrowed_iterator operator++(int); + friend bool operator==(arrowed_iterator, arrowed_iterator); +}; + +static_assert(CanArrow>{} // + | views::filter(is_even))>>); +static_assert(!CanArrow>{} // + | views::filter(is_even))>>); + int main() { // Validate views { // ... copyable diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 46725c14859..d909d97b9c7 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -688,6 +689,40 @@ constexpr bool test_lwg3791() { return true; } +// LWG-4112 "has-arrow should require operator->() to be const-qualified" + +template +concept CanArrow = requires(T&& t) { forward(t).operator->(); }; + +enum class arrow_status : bool { bad, good }; + +template +struct arrowed_iterator { + using difference_type = ptrdiff_t; + using value_type = int; + + int& operator*() const; + int* operator->() + requires (S == arrow_status::bad); + int* operator->() const + requires (S == arrow_status::good); + arrowed_iterator& operator++(); + arrowed_iterator operator++(int); + friend bool operator==(arrowed_iterator, arrowed_iterator); +}; + +void test_lwg_4112() { // COMPILE-ONLY + using good_inner_range = ranges::subrange>; + using good_nested_range = span; + using good_joined_range = decltype(good_nested_range{} | views::join); + static_assert(CanArrow>); + + using bad_inner_range = ranges::subrange>; + using bad_nested_range = span; + using bad_joined_range = decltype(bad_nested_range{} | views::join); + static_assert(!CanArrow>); +} + int main() { // Validate views constexpr string_view expected = "Hello World!"sv; From ac971a3a8a5a72ae6c0c9f71ce70b8c3954b3f3b Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 14:55:43 +0800 Subject: [PATCH 19/35] Implement LWG-4124 Cannot format `zoned_time` with resolution coarser than seconds (#5155) --- stl/inc/chrono | 7 +++---- .../P0355R7_calendars_and_time_zones_formatting/test.cpp | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index a895f59a2c2..f63f8533e89 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5270,7 +5270,7 @@ namespace chrono { basic_ostream<_CharT, _Traits>& operator<<( basic_ostream<_CharT, _Traits>& _Os, const zoned_time<_Duration, _TimeZonePtr>& _Val) { const auto _Info = _Val.get_info(); - return _Os << _Local_time_format_t<_Duration>{_Val.get_local_time(), &_Info.abbrev}; + return _Os << _Local_time_format_t>{_Val.get_local_time(), &_Info.abbrev}; } template @@ -6113,11 +6113,10 @@ constexpr bool enable_nonlocking_formatter_optimization<_CHRONO _Local_time_form template struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT> - : formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT> { - + : formatter<_CHRONO _Local_time_format_t>, _CharT> { template auto format(const _CHRONO zoned_time<_Duration, _TimeZonePtr>& _Val, _FormatContext& _FormatCtx) const { - using _Mybase = formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT>; + using _Mybase = formatter<_CHRONO _Local_time_format_t>, _CharT>; const auto _Info = _Val.get_info(); return _Mybase::format({_Val.get_local_time(), &_Info.abbrev, &_Info.offset}, _FormatCtx); } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index 86734f6e86e..d85b93690d2 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -1020,6 +1020,12 @@ void test_zoned_time_formatter() { == STR("04/19/21 2021-04-19, 2021 20 21, Apr April Apr 04, 19 19, Mon Monday 1 1")); assert(format(STR("{:%H %I %M %S, %r, %R %T %p}"), zt) == STR("08 08 16 17, 08:16:17 AM, 08:16 08:16:17 AM")); assert(format(STR("{:%g %G %U %V %W}"), zt) == STR("21 2021 16 16 16")); + + // LWG-4124 "Cannot format zoned_time with resolution coarser than seconds" + + const zoned_time zoned_minutes_epoch{}; + + empty_braces_helper(zoned_minutes_epoch, STR("1970-01-01 00:00:00 UTC")); } template From 83be0b7b32c1cc01522e8dee4439af129e9aa4e8 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 15:01:45 +0800 Subject: [PATCH 20/35] Avoid arithmetic overflow in the constructors of `weekday` (#5156) Co-authored-by: Cassio Neri Co-authored-by: Casey Carter --- stl/inc/chrono | 6 ++++-- .../tests/P0355R7_calendars_and_time_zones_dates/test.cpp | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index f63f8533e89..564751b0fdb 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -473,10 +473,12 @@ namespace chrono { private: unsigned char _Weekday; - // courtesy of Howard Hinnant + // courtesy of Howard Hinnant (modified to avoid overflow) // https://howardhinnant.github.io/date_algorithms.html#weekday_from_days _NODISCARD static constexpr unsigned int _Weekday_from_days(int _Tp) noexcept { - return static_cast(_Tp >= -4 ? (_Tp + 4) % 7 : (_Tp + 5) % 7 + 6); + _STL_INTERNAL_STATIC_ASSERT(~0u % 7u == 3u); // offset for `_Tp < 0` needs to change + const auto _Before_modulo = static_cast(_Tp) + (_Tp >= 0 ? 4u : 0u); + return _Before_modulo % 7u; // separate expression for MSVC codegen, see GH-5153 } }; diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp index 6a59c863a07..745eb7b3315 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp @@ -281,6 +281,10 @@ constexpr void weekday_test() { assert(Sunday - Monday == days{6}); assert(Sunday - Tuesday == days{5}); assert(Wednesday - Thursday == days{6}); + + // GH-5153 ": integer overflow in weekday::weekday(sys_days::max())" + assert(weekday{sys_days::max()} == weekday{sys_days::max() - days{7}}); + assert(weekday{local_days::max()} == weekday{local_days::max() - days{7}}); } constexpr void weekday_indexed_test() { From 79137b69f0c8d07223f78119aebc00f876b4e16e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 5 Dec 2024 15:05:16 +0800 Subject: [PATCH 21/35] ``: Make `valarray` ADL-proof as required (#5157) Co-authored-by: Casey Carter --- stl/inc/valarray | 12 ++++----- .../test.compile.pass.cpp | 25 +++++++++++++++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/stl/inc/valarray b/stl/inc/valarray index 38303a6419a..3b370470e1d 100644 --- a/stl/inc/valarray +++ b/stl/inc/valarray @@ -80,7 +80,7 @@ public: } valarray(const _Ty& _Val, size_t _Count) { // construct with _Count * _Val - _Grow(_Count, &_Val); + _Grow(_Count, _STD addressof(_Val)); } valarray(const _Ty* _Ptr, size_t _Count) { // construct with [_Ptr, _Ptr + _Count) @@ -162,7 +162,7 @@ public: void resize(size_t _Newsize, _Ty _Val) { // determine new length, filling with _Val elements _Tidy_deallocate(); - _Grow(_Newsize, &_Val, 0); + _Grow(_Newsize, _STD addressof(_Val), 0); } valarray& operator=(const slice_array<_Ty>& _Slicearr); // defined below @@ -538,7 +538,7 @@ private: _Myptr = _Allocate_for_op_delete<_Ty>(_Newsize); _Tidy_deallocate_guard _Guard{this}; for (size_t _Idx = 0; _Idx < _Newsize; ++_Idx) { - _Construct_in_place(_Myptr[_Idx]); + _STD _Construct_in_place(_Myptr[_Idx]); } _Guard._Target = nullptr; @@ -552,7 +552,7 @@ private: _Myptr = _Allocate_for_op_delete<_Ty>(_Newsize); _Tidy_deallocate_guard _Guard{this}; for (size_t _Idx = 0; _Idx < _Newsize; ++_Idx, _Ptr += _Inc) { - _Construct_in_place(_Myptr[_Idx], *_Ptr); + _STD _Construct_in_place(_Myptr[_Idx], *_Ptr); } _Guard._Target = nullptr; @@ -562,7 +562,7 @@ private: void _Tidy_deallocate() noexcept { if (_Myptr) { // destroy elements - _Destroy_range(_Myptr, _Myptr + _Mysize); + _STD _Destroy_range(_Myptr, _Myptr + _Mysize); #ifdef __cpp_aligned_new constexpr bool _Extended_alignment = alignof(_Ty) > __STDCPP_DEFAULT_NEW_ALIGNMENT__; if constexpr (_Extended_alignment) { @@ -2028,7 +2028,7 @@ private: template valarray<_Ty>& valarray<_Ty>::operator=(const slice_array<_Ty>& _Slicearr) { _Tidy_deallocate(); - _Grow(_Slicearr.size(), &_Slicearr._Data(_Slicearr.start()), _Slicearr.stride()); + _Grow(_Slicearr.size(), _STD addressof(_Slicearr._Data(_Slicearr.start())), _Slicearr.stride()); return *this; } diff --git a/tests/std/tests/GH_000140_adl_proof_construction/test.compile.pass.cpp b/tests/std/tests/GH_000140_adl_proof_construction/test.compile.pass.cpp index 0bc1d7ba294..57e78b559cc 100644 --- a/tests/std/tests/GH_000140_adl_proof_construction/test.compile.pass.cpp +++ b/tests/std/tests/GH_000140_adl_proof_construction/test.compile.pass.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #if _HAS_CXX17 #include #endif // _HAS_CXX17 @@ -79,7 +81,7 @@ template struct tagged_identity { template constexpr U&& operator()(U&& u) const noexcept { - return static_cast(u); + return std::forward(u); } }; @@ -87,7 +89,7 @@ template struct tagged_large_identity { template constexpr U&& operator()(U&& u) const noexcept { - return static_cast(u); + return std::forward(u); } alignas(64) unsigned char unused[64]{}; @@ -145,6 +147,25 @@ void test_promise() { promise{allocator_arg, adl_proof_allocator{}}; } +void test_valarray() { + using validator_class = holder; + + valarray valarr1(42); + + validator_class a[1]{}; + valarray valarr2(a, 1); + valarr2.resize(172, a[0]); + + valarray valarr3(a[0], 1); + valarr3 = valarr2[slice{0, 1, 1}]; + + auto valarr4 = valarr1; + valarr4 = valarr1; + + auto valarr5 = std::move(valarr2); + valarr5 = std::move(valarr3); +} + #if _HAS_CXX17 void test_optional() { optional o{}; From e3e65be09f566ab85af476cb7fd16989757c18dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Thu, 5 Dec 2024 08:09:14 +0100 Subject: [PATCH 22/35] ``: Always reject character ranges with set limits (#5158) Co-authored-by: Stephan T. Lavavej --- stl/inc/regex | 51 ++++++++++--------- tests/libcxx/expected_results.txt | 1 - tests/std/include/test_regex_support.hpp | 14 ++--- .../std/tests/VSO_0000000_regex_use/test.cpp | 31 +++++++++++ 4 files changed, 65 insertions(+), 32 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index f5f8e75bf46..0d487ec2ea4 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -4086,37 +4086,38 @@ void _Parser<_FwdIt, _Elem, _RxTraits>::_ClassRanges() { // check for valid clas return; } - if (_Ret != _Prs_set) { - if (_Val == 0 && !(_L_flags & _L_bzr_chr)) { - _Error(regex_constants::error_escape); - } - - if (_Mchar == _Meta_dash) { // check for valid range - _Next(); - _Elem _Chr1 = static_cast<_Elem>(_Val); - if ((_Ret = _ClassAtom()) == _Prs_none) { // treat - as ordinary character - _Nfa._Add_char_to_class(static_cast<_Elem>(_Val)); - _Nfa._Add_char_to_class(_Meta_dash); - return; - } + if (_Ret == _Prs_chr && _Val == 0 && !(_L_flags & _L_bzr_chr)) { + _Error(regex_constants::error_escape); + } - if (_Ret == _Prs_set) { - _Error(regex_constants::error_range); // set follows dash + if (_Mchar == _Meta_dash) { // check for valid range + _Next(); + _Elem _Chr1 = static_cast<_Elem>(_Val); + const bool _Set_preceding = _Ret == _Prs_set; + if ((_Ret = _ClassAtom()) == _Prs_none) { // treat - as ordinary character + if (!_Set_preceding) { + _Nfa._Add_char_to_class(_Chr1); } + _Nfa._Add_char_to_class(_Meta_dash); + return; + } - if (_Flags & regex_constants::collate) { // translate ends of range - _Val = _Traits.translate(static_cast<_Elem>(_Val)); - _Chr1 = _Traits.translate(_Chr1); - } + if (_Set_preceding || _Ret == _Prs_set) { + _Error(regex_constants::error_range); // set precedes or follows dash + } - if (static_cast(_Val) < static_cast(_Chr1)) { - _Error(regex_constants::error_range); - } + if (_Flags & regex_constants::collate) { // translate ends of range + _Val = _Traits.translate(static_cast<_Elem>(_Val)); + _Chr1 = _Traits.translate(_Chr1); + } - _Nfa._Add_range(_Chr1, static_cast<_Elem>(_Val)); - } else { - _Nfa._Add_char_to_class(static_cast<_Elem>(_Val)); + if (static_cast(_Val) < static_cast(_Chr1)) { + _Error(regex_constants::error_range); } + + _Nfa._Add_range(_Chr1, static_cast<_Elem>(_Val)); + } else if (_Ret == _Prs_chr) { + _Nfa._Add_char_to_class(static_cast<_Elem>(_Val)); } } } diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 56e77fbc616..1da736fc4db 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -561,7 +561,6 @@ std/re/re.alg/re.alg.search/no_update_pos.pass.cpp FAIL std/re/re.const/re.synopt/syntax_option_type.pass.cpp FAIL std/re/re.regex/re.regex.construct/bad_backref.pass.cpp FAIL std/re/re.regex/re.regex.construct/bad_escape.pass.cpp FAIL -std/re/re.regex/re.regex.construct/bad_range.pass.cpp FAIL std/re/re.regex/re.regex.construct/default.pass.cpp FAIL std/re/re.regex/re.regex.nonmemb/re.regex.nmswap/swap.pass.cpp FAIL std/re/re.regex/re.regex.swap/swap.pass.cpp FAIL diff --git a/tests/std/include/test_regex_support.hpp b/tests/std/include/test_regex_support.hpp index fc11c74929b..d45b77dec8a 100644 --- a/tests/std/include/test_regex_support.hpp +++ b/tests/std/include/test_regex_support.hpp @@ -160,18 +160,20 @@ class regex_fixture { } } - void should_throw(const std::string& pattern, const std::regex_constants::error_type expectedCode) { + void should_throw(const std::string& pattern, const std::regex_constants::error_type expectedCode, + const std::regex_constants::syntax_option_type syntax = std::regex_constants::ECMAScript) { try { - const std::regex r(pattern); - printf(R"(regex r("%s") succeeded (which is bad).)" + const std::regex r(pattern, syntax); + printf(R"(regex r("%s", 0x%X) succeeded (which is bad).)" "\n", - pattern.c_str()); + pattern.c_str(), static_cast(syntax)); fail_regex(); } catch (const std::regex_error& e) { if (e.code() != expectedCode) { - printf(R"(regex r("%s") threw 0x%X; expected 0x%X)" + printf(R"(regex r("%s", 0x%X) threw 0x%X; expected 0x%X)" "\n", - pattern.c_str(), static_cast(e.code()), static_cast(expectedCode)); + pattern.c_str(), static_cast(syntax), static_cast(e.code()), + static_cast(expectedCode)); fail_regex(); } } diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index fcd27f0beba..823190f9837 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -582,6 +582,36 @@ void test_gh_993() { } } +void test_gh_4995() { + // GH-4995: R"([\d-e])" should be rejected + g_regexTester.should_throw(R"([\d-e])", error_range); + g_regexTester.should_throw(R"([e-\d])", error_range); + g_regexTester.should_throw(R"([\w-\d])", error_range); + g_regexTester.should_throw("[[:digit:]-e]", error_range); + g_regexTester.should_throw("[e-[:digit:]]", error_range); + g_regexTester.should_throw("[[:alpha:]-[:digit:]]", error_range); + g_regexTester.should_throw("[[=a=]-e]", error_range, ECMAScript | regex::collate); + g_regexTester.should_throw("[e-[=a=]]", error_range, ECMAScript | regex::collate); + g_regexTester.should_throw("[[=a=]-[=b=]]", error_range, ECMAScript | regex::collate); + + // Test valid cases: + g_regexTester.should_not_match("b", R"([\d-])"); + g_regexTester.should_match("5", R"([\d-])"); + g_regexTester.should_match("-", R"([\d-])"); + + g_regexTester.should_not_match("b", R"([-\d])"); + g_regexTester.should_match("5", R"([-\d])"); + g_regexTester.should_match("-", R"([-\d])"); + + g_regexTester.should_match("b", R"([a-c\d])"); + g_regexTester.should_match("5", R"([a-c\d])"); + g_regexTester.should_not_match("-", R"([a-c\d])"); + + g_regexTester.should_match("b", R"([\da-c])"); + g_regexTester.should_match("5", R"([\da-c])"); + g_regexTester.should_not_match("-", R"([\da-c])"); +} + void test_gh_5058() { // GH-5058 ": Small cleanups" changed some default constructors to be defaulted. // Verify that types are still const-default-constructible (N4993 [dcl.init.general]/8). @@ -656,6 +686,7 @@ int main() { test_VSO_225160_match_eol_flag(); test_VSO_226914_word_boundaries(); test_gh_993(); + test_gh_4995(); test_gh_5058(); return g_regexTester.result(); From b932cf84b2ff4431a140c2886492dc91e3bc2044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Thu, 5 Dec 2024 08:14:47 +0100 Subject: [PATCH 23/35] ``: Repair character class escapes outside character class definitions (#5160) Co-authored-by: Stephan T. Lavavej --- stl/inc/regex | 17 ++++++++++++----- tests/std/tests/VSO_0000000_regex_use/test.cpp | 11 +++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index 0d487ec2ea4..77efc9d32f3 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -1710,7 +1710,7 @@ private: bool _OctalDigits(); void _Do_ex_class(_Meta_type); bool _CharacterClassEscape(bool); - _Prs_ret _ClassEscape(bool); + _Prs_ret _ClassEscape2(); _Prs_ret _ClassAtom(); void _ClassRanges(); void _CharacterClass(); @@ -4017,22 +4017,29 @@ bool _Parser<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // return false; } + const bool _Negated = _Traits.isctype(_Char, _RxTraits::_Ch_upper); if (_Addit) { _Nfa._Add_class(); + // GH-992: Outside character class definitions, _Cls completely defines the character class + // so negating _Cls and negating the entire character class are equivalent. + // Since the former negation is defective, do the latter instead. + if (_Negated) { + _Nfa._Negate(); + } } - _Nfa._Add_named_class(_Cls, _Traits.isctype(_Char, _RxTraits::_Ch_upper)); + _Nfa._Add_named_class(_Cls, _Negated && !_Addit); _Next(); return true; } template -_Prs_ret _Parser<_FwdIt, _Elem, _RxTraits>::_ClassEscape(bool _Addit) { // check for class escape +_Prs_ret _Parser<_FwdIt, _Elem, _RxTraits>::_ClassEscape2() { // check for class escape if ((_L_flags & _L_esc_bsl) && _Char == _Esc_bsl) { // handle escape backslash if allowed _Val = _Esc_bsl; _Next(); return _Prs_chr; - } else if ((_L_flags & _L_esc_wsd) && _CharacterClassEscape(_Addit)) { + } else if ((_L_flags & _L_esc_wsd) && _CharacterClassEscape(false)) { return _Prs_set; } else if (_DecimalDigits(regex_constants::error_escape)) { // check for invalid value if (_Val != 0) { @@ -4049,7 +4056,7 @@ _Prs_ret _Parser<_FwdIt, _Elem, _RxTraits>::_ClassAtom() { // check for class at if (_Mchar == _Meta_esc) { // check for valid escape sequence _Next(); if (_L_flags & _L_grp_esc) { - return _ClassEscape(false); + return _ClassEscape2(); } else if ((_L_flags & _L_esc_ffn && _Do_ffn(_Char)) || (_L_flags & _L_esc_ffnx && _Do_ffnx(_Char))) { // advance to next character _Next(); diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index 823190f9837..ad2efc0d4a6 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -659,6 +659,16 @@ void test_gh_5058() { } } +void test_gh_5160() { + // GH-5160 fixed mishandled negated character class escapes + // outside character class definitions + const test_wregex neg_regex(&g_regexTester, LR"(Y\S*Z)"); + neg_regex.should_search_match(L"xxxYxx\x0078xxxZxxx", L"Yxx\x0078xxxZ"); // U+0078 LATIN SMALL LETTER X + neg_regex.should_search_match(L"xxxYxx\x03C7xxxZxxx", L"Yxx\x03C7xxxZ"); // U+03C7 GREEK SMALL LETTER CHI + neg_regex.should_search_fail(L"xxxYxx xxxZxxx"); + neg_regex.should_search_fail(L"xxxYxx\x2009xxxZxxx"); // U+2009 THIN SPACE +} + int main() { test_dev10_449367_case_insensitivity_should_work(); test_dev11_462743_regex_collate_should_not_disable_regex_icase(); @@ -688,6 +698,7 @@ int main() { test_gh_993(); test_gh_4995(); test_gh_5058(); + test_gh_5160(); return g_regexTester.result(); } From 89ca073a866d24db42b3f4c5e406be4b28a148f4 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 5 Dec 2024 10:48:58 -0800 Subject: [PATCH 24/35] Globally suppress C5278 (#5163) This is a new warning MSVC is adding to steer users away from attempting to specialize the type traits as forbidden by N4993 [meta.rqmts]/4. Since MSVC implements many of the type traits in the compiler, such specializations are generally ignored to the confusion of users who trigger the UB. This is a mirror of the STL portion of MSVC-PR-596210. --- stl/inc/yvals_core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 07258c7ec98..f3d7a9112a2 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -810,6 +810,7 @@ // warning C5220: a non-static data member with a volatile qualified type no longer implies that compiler generated // copy/move constructors and copy/move assignment operators are not trivial (/Wall) // warning C5246: 'member': the initialization of a subobject should be wrapped in braces (/Wall) +// warning C5278: adding a specialization for 'type trait' has undefined behavior // warning C6294: Ill-defined for-loop: initial condition does not satisfy test. Loop body not executed #ifndef _STL_DISABLED_WARNINGS @@ -817,7 +818,7 @@ #define _STL_DISABLED_WARNINGS \ 4180 4324 4412 4455 4494 4514 4574 4582 4583 4587 \ 4588 4619 4623 4625 4626 4643 4648 4702 4793 4820 \ - 4868 4988 5026 5027 5045 5220 5246 6294 \ + 4868 4988 5026 5027 5045 5220 5246 5278 6294 \ _STL_DISABLED_WARNING_C4577 \ _STL_DISABLED_WARNING_C4984 \ _STL_DISABLED_WARNING_C5053 \ From b85dd2c393a9a19e6b78c514d3ba25d356e3a0f9 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 12 Dec 2024 21:58:45 -0800 Subject: [PATCH 25/35] Toolset update: VS 2022 17.13 Preview 2, F32as_v6 (#5186) --- README.md | 4 ++-- azure-devops/config.yml | 10 +++++----- azure-devops/create-1es-hosted-pool.ps1 | 8 ++++++-- azure-devops/provision-image.ps1 | 4 ++-- tests/std/tests/Dev09_056375_locale_cleanup/test.cpp | 2 -- tests/std/tests/P0323R12_expected/test.cpp | 2 -- .../custom_format.py | 4 +++- tests/std/tests/P2502R2_generator/test.cpp | 6 +++--- 8 files changed, 21 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 354a729e36e..e4d3685cf48 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem # How To Build With The Visual Studio IDE -1. Install Visual Studio 2022 17.13 Preview 1 or later. +1. Install Visual Studio 2022 17.13 Preview 2 or later. * Select "Windows 11 SDK (10.0.22621.0)" in the VS Installer. * Select "MSVC v143 - VS 2022 C++ ARM64/ARM64EC build tools (Latest)" in the VS Installer if you would like to build the ARM64/ARM64EC target. @@ -160,7 +160,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem # How To Build With A Native Tools Command Prompt -1. Install Visual Studio 2022 17.13 Preview 1 or later. +1. Install Visual Studio 2022 17.13 Preview 2 or later. * Select "Windows 11 SDK (10.0.22621.0)" in the VS Installer. * Select "MSVC v143 - VS 2022 C++ ARM64/ARM64EC build tools (Latest)" in the VS Installer if you would like to build the ARM64/ARM64EC target. diff --git a/azure-devops/config.yml b/azure-devops/config.yml index 2c81cfd639c..ca89e088cf8 100644 --- a/azure-devops/config.yml +++ b/azure-devops/config.yml @@ -5,22 +5,22 @@ variables: - name: poolName - value: 'StlBuild-2024-11-12T1255-Pool' + value: 'StlBuild-2024-12-12T1002-Pool' readonly: true - name: poolDemands value: 'EnableSpotVM -equals false' readonly: true - name: tmpDir - value: 'D:\Temp' + value: 'C:\stlTemp' readonly: true - name: buildOutputLocation - value: 'D:\build' + value: 'C:\stlBuild' readonly: true - name: benchmarkBuildOutputLocation - value: 'D:\benchmark' + value: 'C:\stlBenchmark' readonly: true - name: validationBuildOutputLocation - value: 'D:\validation' + value: 'C:\stlValidation' readonly: true - name: Codeql.SkipTaskAutoInjection value: true diff --git a/azure-devops/create-1es-hosted-pool.ps1 b/azure-devops/create-1es-hosted-pool.ps1 index 0ac42dded33..205e17ad75f 100644 --- a/azure-devops/create-1es-hosted-pool.ps1 +++ b/azure-devops/create-1es-hosted-pool.ps1 @@ -14,7 +14,7 @@ $ErrorActionPreference = 'Stop' $CurrentDate = Get-Date $Location = 'eastus2' -$VMSize = 'Standard_D32ads_v5' +$VMSize = 'Standard_F32as_v6' $ProtoVMName = 'PROTOTYPE' $ImagePublisher = 'MicrosoftWindowsServer' $ImageOffer = 'WindowsServer' @@ -141,6 +141,7 @@ Display-ProgressBar -Status 'Creating prototype VM' $VM = New-AzVMConfig ` -VMName $ProtoVMName ` -VMSize $VMSize ` + -DiskControllerType 'NVMe' ` -Priority 'Regular' $VM = Set-AzVMOperatingSystem ` @@ -261,6 +262,9 @@ New-AzRoleAssignment ` Display-ProgressBar -Status 'Creating image definition' $ImageDefinitionName = $ResourceGroupName + '-ImageDefinition' +$FeatureTrustedLaunch = @{ Name = 'SecurityType'; Value = 'TrustedLaunch'; } +$FeatureNVMe = @{ Name = 'DiskControllerTypes'; Value = 'SCSI, NVMe'; } +$ImageDefinitionFeatures = @($FeatureTrustedLaunch, $FeatureNVMe) New-AzGalleryImageDefinition ` -Location $Location ` -ResourceGroupName $ResourceGroupName ` @@ -271,7 +275,7 @@ New-AzGalleryImageDefinition ` -Publisher $ImagePublisher ` -Offer $ImageOffer ` -Sku $ImageSku ` - -Feature @(@{ Name = 'SecurityType'; Value = 'TrustedLaunch'; }) ` + -Feature $ImageDefinitionFeatures ` -HyperVGeneration 'V2' | Out-Null #################################################################################################### diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index 344fbb7b8eb..048bbd65d75 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -43,7 +43,7 @@ foreach ($workload in $VisualStudioWorkloads) { $PowerShellUrl = 'https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x64.msi' $PowerShellArgs = @('/quiet', '/norestart') -$PythonUrl = 'https://www.python.org/ftp/python/3.13.0/python-3.13.0-amd64.exe' +$PythonUrl = 'https://www.python.org/ftp/python/3.13.1/python-3.13.1-amd64.exe' $PythonArgs = @('/quiet', 'InstallAllUsers=1', 'PrependPath=1', 'CompileAll=1', 'Include_doc=0') $CudaUrl = 'https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_551.61_windows.exe' @@ -75,7 +75,7 @@ Function DownloadAndInstall { try { Write-Host "Downloading $Name..." - $tempPath = 'D:\installerTemp' + $tempPath = 'C:\installerTemp' mkdir $tempPath -Force | Out-Null $fileName = [uri]::new($Url).Segments[-1] $installerPath = Join-Path $tempPath $fileName diff --git a/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp b/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp index 4c2c434b6cb..cb66dd98088 100644 --- a/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp +++ b/tests/std/tests/Dev09_056375_locale_cleanup/test.cpp @@ -83,9 +83,7 @@ void test_dll() { TheFuncProc pFunc = reinterpret_cast(GetProcAddress(hLibrary, "DllTest")); assert(pFunc != nullptr); pFunc(); -#if defined(_MSVC_INTERNAL_TESTING) || defined(_DLL) || !defined(__SANITIZE_ADDRESS__) // TRANSITION, vs17.13p2 FreeLibrary(hLibrary); -#endif // ^^^ no workaround ^^^ #endif // ^^^ !defined(_M_CEE) ^^^ } diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index bd318ada308..b964b554c8f 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -2385,10 +2385,8 @@ static_assert( static_assert(!is_assignable_v&, ambiguating_expected_assignment_source>); static_assert(!is_assignable_v&, ambiguating_expected_assignment_source>); #endif // ^^^ no workaround ^^^ -#ifndef __EDG__ // TRANSITION, VSO-2188364 static_assert(!is_assignable_v&, ambiguating_expected_assignment_source>); static_assert(!is_assignable_v&, ambiguating_expected_assignment_source>); -#endif // ^^^ no workaround ^^^ int main() { test_unexpected::test_all(); diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py index bcb09e3ec35..258a680222e 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py +++ b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py @@ -62,7 +62,9 @@ def getBuildSteps(self, test, litConfig, shared): # Generate JSON files that record how these headers depend on one another. if noisyProgress: print('Scanning dependencies...') - cmd = [test.cxx, *test.flags, *test.compileFlags, *clOptions, '/scanDependencies', '.\\', *allHeaders] + cmd = [test.cxx, *test.flags, *test.compileFlags, *clOptions, '/scanDependencies', '.\\', + '/shallowScan', # TRANSITION, VSO-2293247 fixed in VS 2022 17.13 Preview 3 (remove /shallowScan) + *allHeaders] yield TestStep(cmd, shared.execDir, shared.env, False) # The JSON files also record what object files will be produced. diff --git a/tests/std/tests/P2502R2_generator/test.cpp b/tests/std/tests/P2502R2_generator/test.cpp index 48c52412604..5ab4cf1ff3a 100644 --- a/tests/std/tests/P2502R2_generator/test.cpp +++ b/tests/std/tests/P2502R2_generator/test.cpp @@ -152,7 +152,7 @@ void test_weird_reference_types() { assert(pos == r.end()); } -#if !(defined(__EDG__) || (defined(__clang__) && defined(_M_IX86))) // TRANSITION, VSO-2254804 and LLVM-56507 +#if !(defined(__clang__) && defined(_M_IX86)) // TRANSITION, LLVM-56507 { // Test with mutable rvalue reference type constexpr size_t segment_size = 16; auto woof = []() -> generator&&> { @@ -172,7 +172,7 @@ void test_weird_reference_types() { #endif // ^^^ no workaround ^^^ } -#if !(defined(__EDG__) || (defined(__clang__) && defined(_M_IX86))) // TRANSITION, VSO-2254804 and LLVM-56507 +#if !(defined(__clang__) && defined(_M_IX86)) // TRANSITION, LLVM-56507 generator iota_repeater(const int hi, const int depth) { if (depth > 0) { co_yield ranges::elements_of(iota_repeater(hi, depth - 1)); @@ -401,7 +401,7 @@ int main() { assert(ranges::equal(co_upto(6), views::iota(0, 6))); zip_example(); test_weird_reference_types(); -#if !(defined(__EDG__) || (defined(__clang__) && defined(_M_IX86))) // TRANSITION, VSO-2254804 and LLVM-56507 +#if !(defined(__clang__) && defined(_M_IX86)) // TRANSITION, LLVM-56507 recursive_test(); arbitrary_range_test(); From 011998007dd20665f627eb186cde7ef726ef4487 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 12 Dec 2024 22:00:54 -0800 Subject: [PATCH 26/35] Stop using ctest (#5169) --- CMakeLists.txt | 1 - README.md | 62 ++++++++++----------------------- azure-devops/asan-pipeline.yml | 4 +-- azure-devops/build-and-test.yml | 6 ++-- azure-devops/run-tests.yml | 4 +-- tests/CMakeLists.txt | 18 +++------- tests/utils/stl/test/params.py | 6 ++-- 7 files changed, 35 insertions(+), 66 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d6f36924ef..40775a257f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,6 @@ set(VCLIBS_DEBUG_OPTIONS "$<$:/Od>") set(VCLIBS_RELEASE_OPTIONS "$<$:/O2;/Os>") if(BUILD_TESTING) - enable_testing() add_subdirectory(tests) endif() diff --git a/README.md b/README.md index e4d3685cf48..de703daad4e 100644 --- a/README.md +++ b/README.md @@ -269,34 +269,33 @@ C:\Users\username\Desktop>dumpbin /DEPENDENTS .\example.exe | findstr msvcp * Otherwise, use [LLVM's installer][] and choose to add LLVM to your `PATH` during installation. 4. Follow the instructions below. -## Running All The Tests +## Running The Tests -After configuring and building the project, running `ctest` from the build output directory will run all the tests. -CTest will only display the standard error output of tests that failed. In order to get more details from CTest's -`lit` invocations, run the tests with `ctest -V`. +Our tests are currently split across three test suites that are located at `tests\std`, `tests\tr1`, and +`llvm-project\libcxx\test\std`. The test runner `${PROJECT_BINARY_DIR}\tests\utils\stl-lit\stl-lit.py` accepts paths to +directories in the test suites and runs all tests located in the subtree rooted at those paths. This can mean executing +the entirety of a single test suite, running all tests under a category in `libcxx`, or running a single test in `std` +and `tr1`. -## Running A Subset Of The Tests - -`${PROJECT_BINARY_DIR}\tests\utils\stl-lit\stl-lit.py` can be invoked on a subdirectory of a test suite and will execute -all the tests under that subdirectory. This can mean executing the entirety of a single test suite, running all tests -under a category in libcxx, or running a single test in `std` and `tr1`. +Some useful `stl-lit.py` options: +* `-v` (verbose) tells `stl-lit.py` to show us output from failed test cases. +* `-Dnotags=ASAN` disables the "extra ASan configs" that we typically run only in CI. This is useful to limit runtime + for full validation runs, but often omitted when running just a few test cases to enable the extra ASan coverage. ## Examples These examples assume that your current directory is `C:\Dev\STL\out\x64`. -* This command will run all of the test suites with verbose output. - + `ctest -V` -* This command will also run all of the test suites. - + `python tests\utils\stl-lit\stl-lit.py ..\..\llvm-project\libcxx\test ..\..\tests\std ..\..\tests\tr1` -* This command will run all of the std test suite. - + `python tests\utils\stl-lit\stl-lit.py ..\..\tests\std` +* This command will run all of the test suites: + + `python tests\utils\stl-lit\stl-lit.py -Dnotags=ASAN ..\..\llvm-project\libcxx\test ..\..\tests\std ..\..\tests\tr1` +* This command will run only the std test suite. + + `python tests\utils\stl-lit\stl-lit.py -Dnotags=ASAN ..\..\tests\std` * If you want to run a subset of a test suite, you need to point it to the right place in the sources. The following -will run the single test found under VSO_0000000_any_calling_conventions. - + `python tests\utils\stl-lit\stl-lit.py ..\..\tests\std\tests\VSO_0000000_any_calling_conventions` +will run the single test found under `VSO_0000000_any_calling_conventions`. + + `python tests\utils\stl-lit\stl-lit.py -Dnotags=ASAN ..\..\tests\std\tests\VSO_0000000_any_calling_conventions` * You can invoke `stl-lit` with any arbitrary subdirectory of a test suite. In libcxx this allows you to have finer control over what category of tests you would like to run. The following will run all the libcxx map tests. - + `python tests\utils\stl-lit\stl-lit.py ..\..\llvm-project\libcxx\test\std\containers\associative\map` + + `python tests\utils\stl-lit\stl-lit.py -Dnotags=ASAN ..\..\llvm-project\libcxx\test\std\containers\associative\map` * You can also use the `--filter` option to include tests whose names match a regular expression. The following command will run tests with "atomic_wait" in their names in both the std and libcxx test suites. + `python tests\utils\stl-lit\stl-lit.py ..\..\llvm-project\libcxx\test ..\..\tests\std --filter=atomic_wait` @@ -305,31 +304,8 @@ control over what category of tests you would like to run. The following will ru ## Interpreting The Results Of Tests -### CTest - -When running the tests via CTest, all of the test suites are considered to be a single test. If any single test in a -test suite fails, CTest will simply report that the `stl` test failed. - -Example: -``` -0% tests passed, 1 tests failed out of 1 - -Total Test time (real) = 2441.55 sec - -The following tests FAILED: - 1 - stl (Failed) -``` - -The primary utility of CTest in this case is to conveniently invoke `stl-lit.py` with the correct set of arguments. - -CTest will output everything that was sent to stderr for each of the failed test suites, which can be used to identify -which individual test within the test suite failed. It can sometimes be helpful to run CTest with the `-V` option in -order to see the stdout of the tests. - -### stl-lit - -When running the tests directly via the generated `stl-lit.py` script the result of each test will be printed. The -format of each result is `{Result Code}: {Test Suite Name} :: {Test Name}:{Configuration Number}`. +`stl-lit.py` prints the result of each test. The format of each result is +`{Result Code}: {Test Suite Name} :: {Test Name}:{Configuration Number}`. Example: ``` diff --git a/azure-devops/asan-pipeline.yml b/azure-devops/asan-pipeline.yml index 5107cd41b4a..f98953de548 100644 --- a/azure-devops/asan-pipeline.yml +++ b/azure-devops/asan-pipeline.yml @@ -27,7 +27,7 @@ stages: hostArch: x64 targetArch: x64 asanBuild: true - ctestOptions: '--tests-regex stlasan' + testTargets: STL-ASan-CI - stage: Build_And_Test_x86 displayName: 'Build and Test x86' @@ -41,6 +41,6 @@ stages: hostArch: x86 targetArch: x86 asanBuild: true - ctestOptions: '--tests-regex stlasan' + testTargets: STL-ASan-CI # no coverage for ARM and ARM64 diff --git a/azure-devops/build-and-test.yml b/azure-devops/build-and-test.yml index 6798be4d1b7..edc9134cd93 100644 --- a/azure-devops/build-and-test.yml +++ b/azure-devops/build-and-test.yml @@ -15,9 +15,9 @@ parameters: - name: buildBenchmarks type: boolean default: false -- name: ctestOptions +- name: testTargets type: string - default: '--exclude-regex stlasan' + default: 'STL-CI' - name: numShards type: number default: 8 @@ -61,5 +61,5 @@ jobs: parameters: hostArch: ${{ parameters.hostArch }} targetArch: ${{ parameters.targetArch }} - ctestOptions: ${{ parameters.ctestOptions }} + testTargets: ${{ parameters.testTargets }} skipTesting: ${{ parameters.skipTesting }} diff --git a/azure-devops/run-tests.yml b/azure-devops/run-tests.yml index 27ce640f32f..a6e2c1363d3 100644 --- a/azure-devops/run-tests.yml +++ b/azure-devops/run-tests.yml @@ -6,7 +6,7 @@ parameters: type: string - name: targetArch type: string -- name: ctestOptions +- name: testTargets type: string - name: skipTesting type: boolean @@ -14,7 +14,7 @@ steps: - script: | call "%ProgramFiles%\Microsoft Visual Studio\2022\Preview\Common7\Tools\VsDevCmd.bat" ^ -host_arch=${{ parameters.hostArch }} -arch=${{ parameters.targetArch }} -no_logo - ctest --verbose ${{ parameters.ctestOptions }} + ninja --verbose -k 0 ${{ parameters.testTargets }} displayName: 'Build and Run Tests' timeoutInMinutes: 30 condition: and(succeeded(), not(${{ parameters.skipTesting }})) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9dc352a0f25..980ddcf194b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,19 +26,11 @@ add_subdirectory(utils/stl-lit) find_package(Python "3.13" REQUIRED COMPONENTS Interpreter) if(NOT DEFINED LIT_FLAGS) - list(APPEND LIT_FLAGS "-o" "${CMAKE_CURRENT_BINARY_DIR}/test_results.json") + set(LIT_FLAGS "-o" "${CMAKE_CURRENT_BINARY_DIR}/test_results.json") endif() +set(STL_LIT_COMMAND ${Python_EXECUTABLE} ${STL_LIT_OUTPUT} ${LIT_FLAGS}) get_property(STL_LIT_TEST_DIRS GLOBAL PROPERTY STL_LIT_TEST_DIRS) -list(APPEND STL_LIT_COMMAND "${STL_LIT_OUTPUT}" - "${LIT_FLAGS}" - "-D" "notags=ASAN" - "${STL_LIT_TEST_DIRS}") -list(APPEND STLASAN_LIT_COMMAND "${STL_LIT_OUTPUT}" - "${LIT_FLAGS}" - "-D" "tags=ASAN" - "${STL_LIT_TEST_DIRS}") - -add_test(NAME stl COMMAND ${Python_EXECUTABLE} ${STL_LIT_COMMAND} COMMAND_EXPAND_LISTS) -add_test(NAME stlasan COMMAND ${Python_EXECUTABLE} ${STLASAN_LIT_COMMAND} COMMAND_EXPAND_LISTS) -set_tests_properties(stl stlasan PROPERTIES RUN_SERIAL ON) + +add_custom_target(STL-CI COMMAND ${STL_LIT_COMMAND} -Dnotags=ASAN ${STL_LIT_TEST_DIRS} USES_TERMINAL) +add_custom_target(STL-ASan-CI COMMAND ${STL_LIT_COMMAND} -Dtags=ASAN ${STL_LIT_TEST_DIRS} USES_TERMINAL) diff --git a/tests/utils/stl/test/params.py b/tests/utils/stl/test/params.py index d032839beaf..c3dd091d5d5 100644 --- a/tests/utils/stl/test/params.py +++ b/tests/utils/stl/test/params.py @@ -49,8 +49,10 @@ def beNice(prio: str) -> list[ConfigAction]: } psutil.Process().nice(priority_map[prio]) except ImportError: - import sys - print(f'NOTE: Module "psutil" is not installed, so the priority setting "{prio}" has no effect.', file=sys.stderr) + if not hasattr(beNice, 'suppress'): + import sys + print(f'NOTE: Module "psutil" is not installed, so the priority setting "{prio}" has no effect.', file=sys.stderr) + beNice.suppress = True return [] From eaf73552f64be45ef9b38a87137ea8280b9144e2 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 12 Dec 2024 22:06:41 -0800 Subject: [PATCH 27/35] Improve `basic_string::find_first_of` and `basic_string::find_last_of` vectorization for large needles or very large haystacks (#5029) Co-authored-by: Stephan T. Lavavej --- benchmarks/src/find_first_of.cpp | 10 +- stl/inc/__msvc_string_view.hpp | 128 +++-- stl/inc/algorithm | 30 ++ stl/inc/xutility | 30 -- stl/src/vector_algorithms.cpp | 888 ++++++++++++++++++++++++++----- 5 files changed, 850 insertions(+), 236 deletions(-) diff --git a/benchmarks/src/find_first_of.cpp b/benchmarks/src/find_first_of.cpp index b81e94f6edc..41b2089e4ca 100644 --- a/benchmarks/src/find_first_of.cpp +++ b/benchmarks/src/find_first_of.cpp @@ -13,6 +13,8 @@ #include #include +#include "skewed_allocator.hpp" + using namespace std; enum class AlgType { std_func, str_member_first, str_member_last }; @@ -24,7 +26,8 @@ void bm(benchmark::State& state) { const size_t HSize = Pos * 2; const size_t Which = 0; - using container = conditional_t, basic_string>; + using container = conditional_t>, + basic_string, not_highly_aligned_allocator>>; constexpr T HaystackFiller{' '}; static_assert(HaystackFiller < Start, "The following iota() should not produce the haystack filler."); @@ -59,8 +62,9 @@ void bm(benchmark::State& state) { } void common_args(auto bm) { - bm->Args({2, 3})->Args({7, 4})->Args({9, 3})->Args({22, 5})->Args({58, 2})->Args({102, 4}); - bm->Args({325, 1})->Args({400, 50})->Args({1011, 11})->Args({1502, 23})->Args({3056, 7}); + bm->Args({2, 3})->Args({6, 81})->Args({7, 4})->Args({9, 3})->Args({22, 5})->Args({58, 2}); + bm->Args({75, 85})->Args({102, 4})->Args({200, 46})->Args({325, 1})->Args({400, 50}); + bm->Args({1011, 11})->Args({1280, 46})->Args({1502, 23})->Args({2203, 54})->Args({3056, 7}); } BENCHMARK(bm)->Apply(common_args); diff --git a/stl/inc/__msvc_string_view.hpp b/stl/inc/__msvc_string_view.hpp index 468378cbc78..57fc646177e 100644 --- a/stl/inc/__msvc_string_view.hpp +++ b/stl/inc/__msvc_string_view.hpp @@ -29,6 +29,15 @@ extern "C" { // compiler has to assume that the denoted arrays are "globally address taken", and that any later calls to // unanalyzable routines may modify those arrays. +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_1( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept; +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_2( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept; +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_4( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept; +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_8( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept; + __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_1( const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept; __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_2( @@ -38,6 +47,23 @@ __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_2( _STD_BEGIN +template +size_t _Find_first_of_pos_vectorized(const _Ty1* const _Haystack, const size_t _Haystack_length, + const _Ty2* const _Needle, const size_t _Needle_length) noexcept { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Ty1) == sizeof(_Ty2)); + if constexpr (sizeof(_Ty1) == 1) { + return ::__std_find_first_of_trivial_pos_1(_Haystack, _Haystack_length, _Needle, _Needle_length); + } else if constexpr (sizeof(_Ty1) == 2) { + return ::__std_find_first_of_trivial_pos_2(_Haystack, _Haystack_length, _Needle, _Needle_length); + } else if constexpr (sizeof(_Ty1) == 4) { + return ::__std_find_first_of_trivial_pos_4(_Haystack, _Haystack_length, _Needle, _Needle_length); + } else if constexpr (sizeof(_Ty1) == 8) { + return ::__std_find_first_of_trivial_pos_8(_Haystack, _Haystack_length, _Needle, _Needle_length); + } else { + _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size + } +} + template size_t _Find_last_of_pos_vectorized(const _Ty1* const _Haystack, const size_t _Haystack_length, const _Ty2* const _Needle, const size_t _Needle_length) noexcept { @@ -834,48 +860,31 @@ constexpr size_t _Traits_find_first_of(_In_reads_(_Hay_size) const _Traits_ptr_t const auto _Hay_end = _Haystack + _Hay_size; if constexpr (_Is_implementation_handled_char_traits<_Traits>) { - if (!_STD _Is_constant_evaluated()) { - using _Elem = typename _Traits::char_type; - #if _USE_STD_VECTOR_ALGORITHMS - const bool _Try_vectorize = _Hay_size - _Start_at > _Threshold_find_first_of; - - // Additional condition for when the vectorization outperforms the table lookup - constexpr size_t _Find_first_of_bitmap_threshold = sizeof(_Elem) == 1 ? 48 : sizeof(_Elem) == 8 ? 8 : 16; - - const bool _Use_bitmap = !_Try_vectorize || _Needle_size > _Find_first_of_bitmap_threshold; -#else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv - const bool _Use_bitmap = true; -#endif // ^^^ !_USE_STD_VECTOR_ALGORITHMS ^^^ - - if (_Use_bitmap) { - _String_bitmap<_Elem> _Matches; - - if (_Matches._Mark(_Needle, _Needle + _Needle_size)) { - for (auto _Match_try = _Hay_start; _Match_try < _Hay_end; ++_Match_try) { - if (_Matches._Match(*_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - } - return static_cast(-1); // no match + if (!_STD _Is_constant_evaluated()) { + const size_t _Remaining_size = _Hay_size - _Start_at; + if (_Remaining_size + _Needle_size >= _Threshold_find_first_of) { + size_t _Pos = _Find_first_of_pos_vectorized(_Hay_start, _Remaining_size, _Needle, _Needle_size); + if (_Pos != static_cast(-1)) { + _Pos += _Start_at; } - - // couldn't put one of the characters into the bitmap, fall back to vectorized or serial algorithms + return _Pos; } + } +#endif // _USE_STD_VECTOR_ALGORITHMS -#if _USE_STD_VECTOR_ALGORITHMS - if (_Try_vectorize) { - const _Traits_ptr_t<_Traits> _Found = - _STD _Find_first_of_vectorized(_Hay_start, _Hay_end, _Needle, _Needle + _Needle_size); - - if (_Found != _Hay_end) { - return static_cast(_Found - _Haystack); // found a match - } else { - return static_cast(-1); // no match + _String_bitmap _Matches; + + if (_Matches._Mark(_Needle, _Needle + _Needle_size)) { + for (auto _Match_try = _Hay_start; _Match_try < _Hay_end; ++_Match_try) { + if (_Matches._Match(*_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match } } -#endif // _USE_STD_VECTOR_ALGORITHMS + return static_cast(-1); // no match } + + // couldn't put one of the characters into the bitmap, fall back to serial algorithm } for (auto _Match_try = _Hay_start; _Match_try < _Hay_end; ++_Match_try) { @@ -899,47 +908,32 @@ constexpr size_t _Traits_find_last_of(_In_reads_(_Hay_size) const _Traits_ptr_t< const auto _Hay_start = (_STD min)(_Start_at, _Hay_size - 1); if constexpr (_Is_implementation_handled_char_traits<_Traits>) { - if (!_STD _Is_constant_evaluated()) { - using _Elem = typename _Traits::char_type; - - bool _Use_bitmap = true; + using _Elem = typename _Traits::char_type; #if _USE_STD_VECTOR_ALGORITHMS - bool _Try_vectorize = false; - - if constexpr (sizeof(_Elem) <= 2) { - _Try_vectorize = _Hay_start + 1 > _Threshold_find_first_of; - // Additional condition for when the vectorization outperforms the table lookup - constexpr size_t _Find_last_of_bitmap_threshold = sizeof(_Elem) == 1 ? 48 : 8; - - _Use_bitmap = !_Try_vectorize || _Needle_size > _Find_last_of_bitmap_threshold; + if constexpr (sizeof(_Elem) <= 2) { + if (!_STD _Is_constant_evaluated()) { + const size_t _Remaining_size = _Hay_start + 1; + if (_Remaining_size + _Needle_size >= _Threshold_find_first_of) { // same threshold for first/last + return _Find_last_of_pos_vectorized(_Haystack, _Remaining_size, _Needle, _Needle_size); + } } + } #endif // _USE_STD_VECTOR_ALGORITHMS - if (_Use_bitmap) { - _String_bitmap<_Elem> _Matches; - if (_Matches._Mark(_Needle, _Needle + _Needle_size)) { - for (auto _Match_try = _Haystack + _Hay_start;; --_Match_try) { - if (_Matches._Match(*_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - return static_cast(-1); // at beginning, no more chance for match - } - } + _String_bitmap<_Elem> _Matches; + if (_Matches._Mark(_Needle, _Needle + _Needle_size)) { + for (auto _Match_try = _Haystack + _Hay_start;; --_Match_try) { + if (_Matches._Match(*_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match } - // couldn't put one of the characters into the bitmap, fall back to vectorized or serial algorithms - } - -#if _USE_STD_VECTOR_ALGORITHMS - if constexpr (sizeof(_Elem) <= 2) { - if (_Try_vectorize) { - return _STD _Find_last_of_pos_vectorized(_Haystack, _Hay_start + 1, _Needle, _Needle_size); + if (_Match_try == _Haystack) { + return static_cast(-1); // at beginning, no more chance for match } } -#endif // _USE_STD_VECTOR_ALGORITHMS } + + // couldn't put one of the characters into the bitmap, fall back to serial algorithm } for (auto _Match_try = _Haystack + _Hay_start;; --_Match_try) { diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 5ea4e3db6f2..ef3fa2e1f54 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -38,6 +38,15 @@ extern "C" { // functions are in native code objects that the compiler cannot analyze. In the absence of the noalias attribute, the // compiler has to assume that the denoted arrays are "globally address taken", and that any later calls to // unanalyzable routines may modify those arrays. +const void* __stdcall __std_find_first_of_trivial_1( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +const void* __stdcall __std_find_first_of_trivial_2( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +const void* __stdcall __std_find_first_of_trivial_4( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +const void* __stdcall __std_find_first_of_trivial_8( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; + __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_1( const void* _First, const void* _Last, void* _Dest) noexcept; __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_2( @@ -73,6 +82,27 @@ __declspec(noalias) void __stdcall __std_replace_8( } // extern "C" _STD_BEGIN +template +_Ty1* _Find_first_of_vectorized( + _Ty1* const _First1, _Ty1* const _Last1, _Ty2* const _First2, _Ty2* const _Last2) noexcept { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Ty1) == sizeof(_Ty2)); + if constexpr (sizeof(_Ty1) == 1) { + return const_cast<_Ty1*>( + static_cast(::__std_find_first_of_trivial_1(_First1, _Last1, _First2, _Last2))); + } else if constexpr (sizeof(_Ty1) == 2) { + return const_cast<_Ty1*>( + static_cast(::__std_find_first_of_trivial_2(_First1, _Last1, _First2, _Last2))); + } else if constexpr (sizeof(_Ty1) == 4) { + return const_cast<_Ty1*>( + static_cast(::__std_find_first_of_trivial_4(_First1, _Last1, _First2, _Last2))); + } else if constexpr (sizeof(_Ty1) == 8) { + return const_cast<_Ty1*>( + static_cast(::__std_find_first_of_trivial_8(_First1, _Last1, _First2, _Last2))); + } else { + _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size + } +} + template __declspec(noalias) void _Reverse_copy_vectorized(const void* _First, const void* _Last, void* _Dest) noexcept { if constexpr (_Nx == 1) { diff --git a/stl/inc/xutility b/stl/inc/xutility index 77c8619911f..f1f33652ce7 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -98,15 +98,6 @@ const void* __stdcall __std_find_last_trivial_2(const void* _First, const void* const void* __stdcall __std_find_last_trivial_4(const void* _First, const void* _Last, uint32_t _Val) noexcept; const void* __stdcall __std_find_last_trivial_8(const void* _First, const void* _Last, uint64_t _Val) noexcept; -const void* __stdcall __std_find_first_of_trivial_1( - const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; -const void* __stdcall __std_find_first_of_trivial_2( - const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; -const void* __stdcall __std_find_first_of_trivial_4( - const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; -const void* __stdcall __std_find_first_of_trivial_8( - const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; - const void* __stdcall __std_search_1( const void* _First1, const void* _Last1, const void* _First2, size_t _Count2) noexcept; const void* __stdcall __std_search_2( @@ -252,27 +243,6 @@ _Ty* _Find_last_vectorized(_Ty* const _First, _Ty* const _Last, const _TVal _Val // find_first_of vectorization is likely to be a win after this size (in elements) _INLINE_VAR constexpr ptrdiff_t _Threshold_find_first_of = 16; -template -_Ty1* _Find_first_of_vectorized( - _Ty1* const _First1, _Ty1* const _Last1, _Ty2* const _First2, _Ty2* const _Last2) noexcept { - _STL_INTERNAL_STATIC_ASSERT(sizeof(_Ty1) == sizeof(_Ty2)); - if constexpr (sizeof(_Ty1) == 1) { - return const_cast<_Ty1*>( - static_cast(::__std_find_first_of_trivial_1(_First1, _Last1, _First2, _Last2))); - } else if constexpr (sizeof(_Ty1) == 2) { - return const_cast<_Ty1*>( - static_cast(::__std_find_first_of_trivial_2(_First1, _Last1, _First2, _Last2))); - } else if constexpr (sizeof(_Ty1) == 4) { - return const_cast<_Ty1*>( - static_cast(::__std_find_first_of_trivial_4(_First1, _Last1, _First2, _Last2))); - } else if constexpr (sizeof(_Ty1) == 8) { - return const_cast<_Ty1*>( - static_cast(::__std_find_first_of_trivial_8(_First1, _Last1, _First2, _Last2))); - } else { - _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size - } -} - template _Ty1* _Search_vectorized(_Ty1* const _First1, _Ty1* const _Last1, _Ty2* const _First2, const size_t _Count2) noexcept { _STL_INTERNAL_STATIC_ASSERT(sizeof(_Ty1) == sizeof(_Ty2)); diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index e89a0fba919..de8eec5a04e 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -2930,10 +2930,435 @@ namespace { return _Result; } - namespace __std_find_first_of { +#ifndef _M_ARM64EC + namespace __std_find_meow_of_bitmap_details { + __m256i _Bitmap_step(const __m256i _Bitmap, const __m256i _Data) noexcept { + const __m256i _Data_high = _mm256_srli_epi32(_Data, 5); + const __m256i _Bitmap_parts = _mm256_permutevar8x32_epi32(_Bitmap, _Data_high); + const __m256i _Data_low_inv = _mm256_andnot_si256(_Data, _mm256_set1_epi32(0x1F)); + const __m256i _Mask = _mm256_sllv_epi32(_Bitmap_parts, _Data_low_inv); + return _Mask; + } + + template + __m256i _Load_avx_256_8(const _Ty* const _Src) noexcept { + if constexpr (sizeof(_Ty) == 1) { + return _mm256_cvtepu8_epi32(_mm_loadu_si64(_Src)); + } else if constexpr (sizeof(_Ty) == 2) { + return _mm256_cvtepu16_epi32(_mm_loadu_si128(reinterpret_cast(_Src))); + } else if constexpr (sizeof(_Ty) == 4) { + return _mm256_loadu_si256(reinterpret_cast(_Src)); + } else if constexpr (sizeof(_Ty) == 8) { + const __m256i _Low = _mm256_loadu_si256(reinterpret_cast(_Src)); + const __m256i _High = _mm256_loadu_si256(reinterpret_cast(_Src) + 1); + const __m256i _Pack = _mm256_packs_epi32(_Low, _High); + return _mm256_permute4x64_epi64(_Pack, _MM_SHUFFLE(3, 1, 2, 0)); + } else { + static_assert(false, "Unexpected size"); + } + } + + template + __m256i _Load_avx_256_8_last(const _Ty* const _Src, const size_t _Count) noexcept { + if constexpr (sizeof(_Ty) == 1) { + uint8_t _Buf[8]; + memcpy(_Buf, _Src, _Count); + return _mm256_cvtepu8_epi32(_mm_loadu_si64(_Buf)); + } else if constexpr (sizeof(_Ty) == 2) { + uint8_t _Buf[16]; + memcpy(_Buf, _Src, _Count * 2); + return _mm256_cvtepu16_epi32(_mm_loadu_si128(reinterpret_cast(_Buf))); + } else if constexpr (sizeof(_Ty) == 4) { + return _mm256_maskload_epi32(reinterpret_cast(_Src), _Avx2_tail_mask_32(_Count)); + } else if constexpr (sizeof(_Ty) == 8) { + const __m256i _Mask_low = _Avx2_tail_mask_32((_Count > 4 ? 4 : _Count) << 1); + const __m256i _Low = _mm256_maskload_epi32(reinterpret_cast(_Src) + 0, _Mask_low); + const __m256i _Mask_high = _Avx2_tail_mask_32((_Count > 4 ? _Count - 4 : 0) << 1); + const __m256i _High = _mm256_maskload_epi32(reinterpret_cast(_Src) + 8, _Mask_high); + const __m256i _Pack = _mm256_packs_epi32(_Low, _High); + return _mm256_permute4x64_epi64(_Pack, _MM_SHUFFLE(3, 1, 2, 0)); + } else { + static_assert(false, "Unexpected size"); + } + } + + template + __m256i _Mask_out_overflow(const __m256i _Mask, const __m256i _Data) noexcept { + if constexpr (sizeof(_Ty) == 1) { + return _Mask; + } else { + const __m256i _Data_high = _mm256_and_si256(_Data, _mm256_set1_epi32(static_cast(0xFFFF'FF00))); + const __m256i _Fit_mask = _mm256_cmpeq_epi32(_Data_high, _mm256_setzero_si256()); + return _mm256_and_si256(_Mask, _Fit_mask); + } + } + + template + __m256i _Make_bitmap_small(const _Ty* _Needle_ptr, const size_t _Needle_length) noexcept { + __m256i _Bitmap = _mm256_setzero_si256(); + + const _Ty* const _Stop = _Needle_ptr + _Needle_length; + + for (; _Needle_ptr != _Stop; ++_Needle_ptr) { + const _Ty _Val = *_Needle_ptr; + const __m128i _Count_low = _mm_cvtsi32_si128(_Val & 0x3F); + const auto _Count_high_x8 = static_cast((_Val >> 3) & 0x18); + const __m256i _One_1_high = _mm256_cvtepu8_epi64(_mm_cvtsi32_si128(1u << _Count_high_x8)); + const __m256i _One_1 = _mm256_sll_epi64(_One_1_high, _Count_low); + _Bitmap = _mm256_or_si256(_Bitmap, _One_1); + } + + return _Bitmap; + } + + template + __m256i _Make_bitmap_large(const _Ty* _Needle_ptr, const size_t _Needle_length) noexcept { + alignas(32) uint8_t _Table[256] = {}; + + const _Ty* const _Stop = _Needle_ptr + _Needle_length; + + for (; _Needle_ptr != _Stop; ++_Needle_ptr) { + _Table[*_Needle_ptr] = 0xFF; + } + + const auto _Table_as_avx = reinterpret_cast(_Table); + + return _mm256_setr_epi32( // + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 0)), + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 1)), + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 2)), + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 3)), + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 4)), + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 5)), + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 6)), + _mm256_movemask_epi8(_mm256_load_si256(_Table_as_avx + 7))); + } + + template + __m256i _Make_bitmap(const _Ty* const _Needle_ptr, const size_t _Needle_length) noexcept { + if (_Needle_length <= 20) { + return _Make_bitmap_small(_Needle_ptr, _Needle_length); + } else { + return _Make_bitmap_large(_Needle_ptr, _Needle_length); + } + } + } // namespace __std_find_meow_of_bitmap_details +#endif // !_M_ARM64EC + + namespace __std_find_meow_of_bitmap { +#ifndef _M_ARM64EC + template + bool _Use_bitmap_avx(const size_t _Count1, const size_t _Count2) noexcept { + if constexpr (sizeof(_Ty) == 1) { + if (_Count2 <= 16) { + return _Count1 > 1000; + } else if (_Count2 <= 48) { + return _Count1 > 80; + } else if (_Count2 <= 240) { + return _Count1 > 40; + } else if (_Count2 <= 1000) { + return _Count1 > 32; + } else { + return _Count1 > 16; + } + } else if constexpr (sizeof(_Ty) == 2) { + if (_Count2 <= 8) { + return _Count1 > 128; + } else if (_Count2 <= 48) { + return _Count1 > 32; + } else if (_Count2 <= 72) { + return _Count1 > 24; + } else if (_Count2 <= 144) { + return _Count1 > 16; + } else { + return _Count1 > 8; + } + } else if constexpr (sizeof(_Ty) == 4) { + if (_Count2 <= 8) { + return _Count1 > 64; + } else if (_Count2 <= 24) { + return _Count1 > 40; + } else if (_Count2 <= 44) { + return _Count1 > 24; + } else if (_Count2 <= 112) { + return _Count1 > 16; + } else { + return _Count1 > 8; + } + } else if constexpr (sizeof(_Ty) == 8) { + if (_Count2 <= 8) { + return _Count1 > 40; + } else if (_Count2 <= 12) { + return _Count1 > 20; + } else if (_Count2 <= 48) { + return _Count1 > 16; + } else if (_Count2 <= 64) { + return _Count1 > 12; + } else if (_Count2 <= 192) { + return _Count1 > 8; + } else { + return _Count1 > 4; + } + } else { + static_assert(false, "unexpected size"); + } + } + + template + bool _Use_bitmap_scalar(const size_t _Count1, const size_t _Count2) noexcept { + if constexpr (sizeof(_Ty) == 1) { + if (_Count2 <= 32) { + return false; + } else if (_Count2 <= 48) { + return _Count1 > 416; + } else if (_Count2 <= 64) { + return _Count1 > 224; + } else if (_Count2 <= 80) { + return _Count1 > 128; + } else if (_Count2 <= 540) { + return _Count1 > 48; + } else { + return _Count1 > 32; + } + } else if constexpr (sizeof(_Ty) == 2) { + if (_Count2 <= 8) { + return false; + } else if (_Count2 <= 80) { + return _Count1 > 16; + } else { + return _Count1 > 8; + } + } else if constexpr (sizeof(_Ty) == 4) { + if (_Count2 <= 32) { + return false; + } else if (_Count2 <= 112) { + return _Count1 > 16; + } else { + return _Count1 > 8; + } + } else if constexpr (sizeof(_Ty) == 8) { + if (_Count2 <= 16) { + return false; + } else if (_Count2 <= 32) { + return _Count1 > 16; + } else if (_Count2 <= 112) { + return _Count1 > 8; + } else { + return _Count1 > 4; + } + } else { + static_assert(false, "unexpected size"); + } + } + + enum class _Strategy { _No_bitmap, _Scalar_bitmap, _Vector_bitmap }; + + template + _Strategy _Pick_strategy(const size_t _Count1, const size_t _Count2, const bool _Use_avx2_) noexcept { + if (_Use_avx2_ && _Count1 > 48) { + return _Use_bitmap_avx<_Ty>(_Count1, _Count2) ? _Strategy::_Vector_bitmap : _Strategy::_No_bitmap; + } else { + return _Use_bitmap_scalar<_Ty>(_Count1, _Count2) ? _Strategy::_Scalar_bitmap : _Strategy::_No_bitmap; + } + } + + template + bool _Can_fit_256_bits_sse(const _Ty* _Needle_ptr, const size_t _Needle_length) noexcept { + if constexpr (sizeof(_Ty) == 1) { + return true; + } else { + __m128i _Mask = _mm_undefined_si128(); + if constexpr (sizeof(_Ty) == 2) { + _Mask = _mm_set1_epi16(static_cast(0xFF00)); + } else if constexpr (sizeof(_Ty) == 4) { + _Mask = _mm_set1_epi32(static_cast(0xFFFF'FF00)); + } else if constexpr (sizeof(_Ty) == 8) { + _Mask = _mm_set1_epi64x(static_cast(0xFFFF'FFFF'FFFF'FF00)); + } else { + static_assert(false, "Unexpected size"); + } + + const size_t _Byte_size = _Needle_length * sizeof(_Ty); + + const void* _Stop = _Needle_ptr; + _Advance_bytes(_Stop, _Byte_size & ~size_t{0x1F}); + for (; _Needle_ptr != _Stop; _Needle_ptr += 32 / sizeof(_Ty)) { + const __m128i _Data = _mm_loadu_si128(reinterpret_cast(_Needle_ptr)); + if (!_mm_testz_si128(_Mask, _Data)) { + return false; + } + } + + _Advance_bytes(_Stop, _Byte_size & 0x1E); + for (; _Needle_ptr != _Stop; ++_Needle_ptr) { + if ((*_Needle_ptr & ~_Ty{0xFF}) != 0) { + return false; + } + } + + return true; + } + } + + template + size_t _Impl_first_avx(const void* const _Haystack, const size_t _Haystack_length, const void* const _Needle, + const size_t _Needle_length) noexcept { + using namespace __std_find_meow_of_bitmap_details; + + _Zeroupper_on_exit _Guard; // TRANSITION, DevCom-10331414 + + const auto _Haystack_ptr = static_cast(_Haystack); + const auto _Needle_ptr = static_cast(_Needle); + + const __m256i _Bitmap = _Make_bitmap(_Needle_ptr, _Needle_length); + + const size_t _Haystack_length_vec = _Haystack_length & ~size_t{7}; + for (size_t _Ix = 0; _Ix != _Haystack_length_vec; _Ix += 8) { + const __m256i _Data = _Load_avx_256_8(_Haystack_ptr + _Ix); + const __m256i _Mask = _Mask_out_overflow<_Ty>(_Bitmap_step(_Bitmap, _Data), _Data); + const unsigned int _Bingo = _mm256_movemask_ps(_mm256_castsi256_ps(_Mask)); + if (_Bingo != 0) { + return _Ix + _tzcnt_u32(_Bingo); + } + } + + const size_t _Haystack_length_tail = _Haystack_length & 7; + if (_Haystack_length_tail != 0) { + const unsigned int _Tail_bingo_mask = (1 << _Haystack_length_tail) - 1; + const __m256i _Data = _Load_avx_256_8_last(_Haystack_ptr + _Haystack_length_vec, _Haystack_length_tail); + const __m256i _Mask = _Mask_out_overflow<_Ty>(_Bitmap_step(_Bitmap, _Data), _Data); + const unsigned int _Bingo = _mm256_movemask_ps(_mm256_castsi256_ps(_Mask)) & _Tail_bingo_mask; + if (_Bingo != 0) { + return _Haystack_length_vec + _tzcnt_u32(_Bingo); + } + } + return static_cast(-1); + } + + template + size_t _Impl_last_avx(const void* const _Haystack, size_t _Haystack_length, const void* const _Needle, + const size_t _Needle_length) noexcept { + using namespace __std_find_meow_of_bitmap_details; + + _Zeroupper_on_exit _Guard; // TRANSITION, DevCom-10331414 + + const auto _Haystack_ptr = static_cast(_Haystack); + const auto _Needle_ptr = static_cast(_Needle); + + const __m256i _Bitmap = _Make_bitmap(_Needle_ptr, _Needle_length); + + while (_Haystack_length >= 8) { + _Haystack_length -= 8; + const __m256i _Data = _Load_avx_256_8(_Haystack_ptr + _Haystack_length); + const __m256i _Mask = _Mask_out_overflow<_Ty>(_Bitmap_step(_Bitmap, _Data), _Data); + const unsigned int _Bingo = _mm256_movemask_ps(_mm256_castsi256_ps(_Mask)); + if (_Bingo != 0) { + return _Haystack_length + 31 - _lzcnt_u32(_Bingo); + } + } + + const size_t _Haystack_length_tail = _Haystack_length & 7; + if (_Haystack_length_tail != 0) { + const unsigned int _Tail_bingo_mask = (1 << _Haystack_length_tail) - 1; + const __m256i _Data = _Load_avx_256_8_last(_Haystack_ptr, _Haystack_length_tail); + const __m256i _Mask = _Mask_out_overflow<_Ty>(_Bitmap_step(_Bitmap, _Data), _Data); + const unsigned int _Bingo = _mm256_movemask_ps(_mm256_castsi256_ps(_Mask)) & _Tail_bingo_mask; + if (_Bingo != 0) { + return 31 - _lzcnt_u32(_Bingo); + } + } + + return static_cast(-1); + } +#endif // !_M_ARM64EC + + using _Scalar_table_t = bool[256]; + + template + [[nodiscard]] bool _Build_scalar_table( + const void* const _Needle, const size_t _Needle_length, _Scalar_table_t& _Table) noexcept { + auto _Ptr = static_cast(_Needle); + const auto _End = _Ptr + _Needle_length; + + for (; _Ptr != _End; ++_Ptr) { + const _Ty _Val = *_Ptr; + + if constexpr (sizeof(_Val) > 1) { + if (_Val >= 256) { + return false; + } + } + + _Table[_Val] = true; + } + + return true; + } + +#ifndef _M_ARM64EC + template + void _Build_scalar_table_no_check( + const void* const _Needle, const size_t _Needle_length, _Scalar_table_t& _Table) noexcept { + auto _Ptr = static_cast(_Needle); + const auto _End = _Ptr + _Needle_length; + + for (; _Ptr != _End; ++_Ptr) { + _Table[*_Ptr] = true; + } + } +#endif // !_M_ARM64EC + + template + size_t _Impl_first_scalar( + const void* const _Haystack, const size_t _Haystack_length, const _Scalar_table_t& _Table) noexcept { + const auto _Haystack_ptr = static_cast(_Haystack); + + for (size_t _Ix = 0; _Ix != _Haystack_length; ++_Ix) { + const _Ty _Val = _Haystack_ptr[_Ix]; + + if constexpr (sizeof(_Val) > 1) { + if (_Val >= 256) { + continue; + } + } + + if (_Table[_Val]) { + return _Ix; + } + } + + return static_cast(-1); + } + + template + size_t _Impl_last_scalar( + const void* const _Haystack, size_t _Haystack_length, const _Scalar_table_t& _Table) noexcept { + const auto _Haystack_ptr = static_cast(_Haystack); + + while (_Haystack_length != 0) { + --_Haystack_length; + + const _Ty _Val = _Haystack_ptr[_Haystack_length]; + + if constexpr (sizeof(_Val) > 1) { + if (_Val >= 256) { + continue; + } + } + + if (_Table[_Val]) { + return _Haystack_length; + } + } + + return static_cast(-1); + } + } // namespace __std_find_meow_of_bitmap + + namespace __std_find_first_of { template - const void* __stdcall _Fallback(const void* _First1, const void* const _Last1, const void* const _First2, + const void* _Fallback(const void* _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { auto _Ptr_haystack = static_cast(_First1); const auto _Ptr_haystack_end = static_cast(_Last1); @@ -2951,42 +3376,39 @@ namespace { return _Ptr_haystack; } - template - const void* __stdcall _Impl_pcmpestri(const void* _First1, const void* const _Last1, const void* const _First2, - const void* const _Last2) noexcept { #ifndef _M_ARM64EC - if (_Use_sse42()) { - constexpr int _Op = (sizeof(_Ty) == 1 ? _SIDD_UBYTE_OPS : _SIDD_UWORD_OPS) | _SIDD_CMP_EQUAL_ANY - | _SIDD_LEAST_SIGNIFICANT; - constexpr int _Part_size_el = sizeof(_Ty) == 1 ? 16 : 8; - const size_t _Needle_length = _Byte_length(_First2, _Last2); - - const size_t _Haystack_length = _Byte_length(_First1, _Last1); - const void* _Stop_at = _First1; - _Advance_bytes(_Stop_at, _Haystack_length & ~size_t{0xF}); + template + const void* _Impl_pcmpestri(const void* _First1, const size_t _Haystack_length, const void* const _First2, + const size_t _Needle_length) noexcept { + constexpr int _Op = + (sizeof(_Ty) == 1 ? _SIDD_UBYTE_OPS : _SIDD_UWORD_OPS) | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT; + constexpr int _Part_size_el = sizeof(_Ty) == 1 ? 16 : 8; - if (_Needle_length <= 16) { - // Special handling of small needle - // The generic branch could also be modified to handle it, but with slightly worse performance + const void* _Stop_at = _First1; + _Advance_bytes(_Stop_at, _Haystack_length & ~size_t{0xF}); - const int _Needle_length_el = static_cast(_Needle_length / sizeof(_Ty)); + if (_Needle_length <= 16) { + // Special handling of small needle + // The generic branch could also be modified to handle it, but with slightly worse performance - alignas(16) uint8_t _Tmp2[16]; - memcpy(_Tmp2, _First2, _Needle_length); - const __m128i _Data2 = _mm_load_si128(reinterpret_cast(_Tmp2)); + const int _Needle_length_el = static_cast(_Needle_length / sizeof(_Ty)); - while (_First1 != _Stop_at) { - const __m128i _Data1 = _mm_loadu_si128(static_cast(_First1)); - if (_mm_cmpestrc(_Data2, _Needle_length_el, _Data1, _Part_size_el, _Op)) { - const int _Pos = _mm_cmpestri(_Data2, _Needle_length_el, _Data1, _Part_size_el, _Op); - _Advance_bytes(_First1, _Pos * sizeof(_Ty)); - return _First1; - } + alignas(16) uint8_t _Tmp2[16]; + memcpy(_Tmp2, _First2, _Needle_length); + const __m128i _Data2 = _mm_load_si128(reinterpret_cast(_Tmp2)); - _Advance_bytes(_First1, 16); + while (_First1 != _Stop_at) { + const __m128i _Data1 = _mm_loadu_si128(static_cast(_First1)); + if (_mm_cmpestrc(_Data2, _Needle_length_el, _Data1, _Part_size_el, _Op)) { + const int _Pos = _mm_cmpestri(_Data2, _Needle_length_el, _Data1, _Part_size_el, _Op); + _Advance_bytes(_First1, _Pos * sizeof(_Ty)); + return _First1; } - const size_t _Last_part_size = _Haystack_length & 0xF; + _Advance_bytes(_First1, 16); + } + + if (const size_t _Last_part_size = _Haystack_length & 0xF; _Last_part_size != 0) { const int _Last_part_size_el = static_cast(_Last_part_size / sizeof(_Ty)); alignas(16) uint8_t _Tmp1[16]; @@ -3000,60 +3422,62 @@ namespace { } _Advance_bytes(_First1, _Last_part_size); - return _First1; - } else { - const void* _Last_needle = _First2; - _Advance_bytes(_Last_needle, _Needle_length & ~size_t{0xF}); + } - const int _Last_needle_length = static_cast(_Needle_length & 0xF); + return _First1; + } else { + const void* _Last_needle = _First2; + _Advance_bytes(_Last_needle, _Needle_length & ~size_t{0xF}); - alignas(16) uint8_t _Tmp2[16]; - memcpy(_Tmp2, _Last_needle, _Last_needle_length); - const __m128i _Last_needle_val = _mm_load_si128(reinterpret_cast(_Tmp2)); - const int _Last_needle_length_el = _Last_needle_length / sizeof(_Ty); + const int _Last_needle_length = static_cast(_Needle_length & 0xF); - constexpr int _Not_found = 16; // arbitrary value greater than any found value + alignas(16) uint8_t _Tmp2[16]; + memcpy(_Tmp2, _Last_needle, _Last_needle_length); + const __m128i _Last_needle_val = _mm_load_si128(reinterpret_cast(_Tmp2)); + const int _Last_needle_length_el = _Last_needle_length / sizeof(_Ty); - int _Found_pos = _Not_found; + constexpr int _Not_found = 16; // arbitrary value greater than any found value - const auto _Step = [&_Found_pos](const __m128i _Data2, const int _Size2, const __m128i _Data1, - const int _Size1) noexcept { - if (_mm_cmpestrc(_Data2, _Size2, _Data1, _Size1, _Op)) { - const int _Pos = _mm_cmpestri(_Data2, _Size2, _Data1, _Size1, _Op); - if (_Pos < _Found_pos) { - _Found_pos = _Pos; - } + int _Found_pos = _Not_found; + + const auto _Step = [&_Found_pos](const __m128i _Data2, const int _Size2, const __m128i _Data1, + const int _Size1) noexcept { + if (_mm_cmpestrc(_Data2, _Size2, _Data1, _Size1, _Op)) { + const int _Pos = _mm_cmpestri(_Data2, _Size2, _Data1, _Size1, _Op); + if (_Pos < _Found_pos) { + _Found_pos = _Pos; } - }; + } + }; #pragma warning(push) #pragma warning(disable : 4324) // structure was padded due to alignment specifier - const auto _Test_whole_needle = [=](const __m128i _Data1, const int _Size1) noexcept { - const void* _Cur_needle = _First2; - do { - const __m128i _Data2 = _mm_loadu_si128(static_cast(_Cur_needle)); - _Step(_Data2, _Part_size_el, _Data1, _Size1); - _Advance_bytes(_Cur_needle, 16); - } while (_Cur_needle != _Last_needle); - - if (_Last_needle_length_el != 0) { - _Step(_Last_needle_val, _Last_needle_length_el, _Data1, _Size1); - } - }; -#pragma warning(pop) + const auto _Test_whole_needle = [=](const __m128i _Data1, const int _Size1) noexcept { + const void* _Cur_needle = _First2; + do { + const __m128i _Data2 = _mm_loadu_si128(static_cast(_Cur_needle)); + _Step(_Data2, _Part_size_el, _Data1, _Size1); + _Advance_bytes(_Cur_needle, 16); + } while (_Cur_needle != _Last_needle); - while (_First1 != _Stop_at) { - _Test_whole_needle(_mm_loadu_si128(static_cast(_First1)), _Part_size_el); + if (_Last_needle_length_el != 0) { + _Step(_Last_needle_val, _Last_needle_length_el, _Data1, _Size1); + } + }; +#pragma warning(pop) - if (_Found_pos != _Not_found) { - _Advance_bytes(_First1, _Found_pos * sizeof(_Ty)); - return _First1; - } + while (_First1 != _Stop_at) { + _Test_whole_needle(_mm_loadu_si128(static_cast(_First1)), _Part_size_el); - _Advance_bytes(_First1, 16); + if (_Found_pos != _Not_found) { + _Advance_bytes(_First1, _Found_pos * sizeof(_Ty)); + return _First1; } - const size_t _Last_part_size = _Haystack_length & 0xF; + _Advance_bytes(_First1, 16); + } + + if (const size_t _Last_part_size = _Haystack_length & 0xF; _Last_part_size != 0) { const int _Last_part_size_el = static_cast(_Last_part_size / sizeof(_Ty)); alignas(16) uint8_t _Tmp1[16]; @@ -3065,15 +3489,18 @@ namespace { _Test_whole_needle(_Data1, _Last_part_size_el); _Advance_bytes(_First1, _Found_pos * sizeof(_Ty)); - return _First1; } + + return _First1; } -#endif // !_M_ARM64EC - return _Fallback<_Ty>(_First1, _Last1, _First2, _Last2); } +#endif // !_M_ARM64EC + + template + struct _Find_first_of_traits; - struct _Traits_4 : _Find_traits_4 { - using _Ty = uint32_t; + template <> + struct _Find_first_of_traits : _Find_traits_4 { #ifndef _M_ARM64EC template static __m256i _Spread_avx(__m256i _Val, const size_t _Needle_length_el) noexcept { @@ -3118,8 +3545,8 @@ namespace { #endif // !_M_ARM64EC }; - struct _Traits_8 : _Find_traits_8 { - using _Ty = uint64_t; + template <> + struct _Find_first_of_traits : _Find_traits_8 { #ifndef _M_ARM64EC template static __m256i _Spread_avx(const __m256i _Val, const size_t _Needle_length_el) noexcept { @@ -3183,18 +3610,16 @@ namespace { return _Eq; } - template - const void* _Shuffle_impl(const void* _First1, const void* const _Last1, const void* const _First2, + template + const void* _Shuffle_impl(const void* _First1, const size_t _Haystack_length, const void* const _First2, const void* const _Stop2, const size_t _Last2_length_el) noexcept { - using _Ty = _Traits::_Ty; + using _Traits = _Find_first_of_traits<_Ty>; constexpr size_t _Length_el = 32 / sizeof(_Ty); const __m256i _Last2val = _mm256_maskload_epi32( reinterpret_cast(_Stop2), _Avx2_tail_mask_32(_Last2_length_el * (sizeof(_Ty) / 4))); const __m256i _Last2s0 = _Traits::_Spread_avx<_Last2_length_el_magnitude>(_Last2val, _Last2_length_el); - const size_t _Haystack_length = _Byte_length(_First1, _Last1); - const void* _Stop1 = _First1; _Advance_bytes(_Stop1, _Haystack_length & ~size_t{0x1F}); @@ -3240,61 +3665,192 @@ namespace { return _First1; } - template - const void* _Shuffle_impl_dispatch_magnitude(const void* const _First1, const void* const _Last1, + template + const void* _Shuffle_impl_dispatch_magnitude(const void* const _First1, const size_t _Haystack_length, const void* const _First2, const void* const _Stop2, const size_t _Last2_length_el) noexcept { if (_Last2_length_el == 0) { - return _Shuffle_impl<_Traits, _Large, 0>(_First1, _Last1, _First2, _Stop2, _Last2_length_el); + return _Shuffle_impl<_Ty, _Large, 0>(_First1, _Haystack_length, _First2, _Stop2, _Last2_length_el); } else if (_Last2_length_el == 1) { - return _Shuffle_impl<_Traits, _Large, 1>(_First1, _Last1, _First2, _Stop2, _Last2_length_el); + return _Shuffle_impl<_Ty, _Large, 1>(_First1, _Haystack_length, _First2, _Stop2, _Last2_length_el); } else if (_Last2_length_el == 2) { - return _Shuffle_impl<_Traits, _Large, 2>(_First1, _Last1, _First2, _Stop2, _Last2_length_el); + return _Shuffle_impl<_Ty, _Large, 2>(_First1, _Haystack_length, _First2, _Stop2, _Last2_length_el); } else if (_Last2_length_el <= 4) { - return _Shuffle_impl<_Traits, _Large, 4>(_First1, _Last1, _First2, _Stop2, _Last2_length_el); + return _Shuffle_impl<_Ty, _Large, 4>(_First1, _Haystack_length, _First2, _Stop2, _Last2_length_el); } else if (_Last2_length_el <= 8) { - if constexpr (sizeof(_Traits::_Ty) == 4) { - return _Shuffle_impl<_Traits, _Large, 8>(_First1, _Last1, _First2, _Stop2, _Last2_length_el); + if constexpr (sizeof(_Ty) == 4) { + return _Shuffle_impl<_Ty, _Large, 8>(_First1, _Haystack_length, _First2, _Stop2, _Last2_length_el); } } _STL_UNREACHABLE; } + template + const void* _Impl_4_8(const void* const _First1, const size_t _Haystack_length, const void* const _First2, + const size_t _Needle_length) noexcept { + _Zeroupper_on_exit _Guard; // TRANSITION, DevCom-10331414 + + const size_t _Last_needle_length = _Needle_length & 0x1F; + const size_t _Last_needle_length_el = _Last_needle_length / sizeof(_Ty); + + if (const size_t _Needle_length_large = _Needle_length & ~size_t{0x1F}; _Needle_length_large != 0) { + const void* _Stop2 = _First2; + _Advance_bytes(_Stop2, _Needle_length_large); + return _Shuffle_impl_dispatch_magnitude<_Ty, true>( + _First1, _Haystack_length, _First2, _Stop2, _Last_needle_length_el); + } else { + return _Shuffle_impl_dispatch_magnitude<_Ty, false>( + _First1, _Haystack_length, _First2, _First2, _Last_needle_length_el); + } + } #endif // !_M_ARM64EC - template - const void* __stdcall _Impl_4_8(const void* const _First1, const void* const _Last1, const void* const _First2, + template + const void* _Dispatch_ptr(const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { - using _Ty = _Traits::_Ty; #ifndef _M_ARM64EC - if (_Use_avx2()) { - _Zeroupper_on_exit _Guard; // TRANSITION, DevCom-10331414 + if constexpr (sizeof(_Ty) <= 2) { + if (_Use_sse42()) { + return _Impl_pcmpestri<_Ty>( + _First1, _Byte_length(_First1, _Last1), _First2, _Byte_length(_First2, _Last2)); + } + } else { + if (_Use_avx2()) { + return _Impl_4_8<_Ty>( + _First1, _Byte_length(_First1, _Last1), _First2, _Byte_length(_First2, _Last2)); + } + } +#endif // !_M_ARM64EC - const size_t _Needle_length = _Byte_length(_First2, _Last2); - const size_t _Last_needle_length = _Needle_length & 0x1F; - const size_t _Last_needle_length_el = _Last_needle_length / sizeof(_Ty); + return _Fallback<_Ty>(_First1, _Last1, _First2, _Last2); + } - if (const size_t _Needle_length_large = _Needle_length & ~size_t{0x1F}; _Needle_length_large != 0) { - const void* _Stop2 = _First2; - _Advance_bytes(_Stop2, _Needle_length_large); - return _Shuffle_impl_dispatch_magnitude<_Traits, true>( - _First1, _Last1, _First2, _Stop2, _Last_needle_length_el); - } else { - return _Shuffle_impl_dispatch_magnitude<_Traits, false>( - _First1, _Last1, _First2, _First2, _Last_needle_length_el); + template + size_t _Pos_from_ptr(const void* const _Result, const void* const _First1, const void* const _Last1) noexcept { + if (_Result != _Last1) { + return _Byte_length(_First1, _Result) / sizeof(_Ty); + } else { + return static_cast(-1); + } + } + +#ifndef _M_ARM64EC + template + size_t _Dispatch_pos_sse_1_2( + const void* const _First1, const size_t _Count1, const void* const _First2, const size_t _Count2) noexcept { + using namespace __std_find_meow_of_bitmap; + + const _Strategy _Strat = _Pick_strategy<_Ty>(_Count1, _Count2, _Use_avx2()); + + if (_Strat == _Strategy::_Vector_bitmap) { + if (_Can_fit_256_bits_sse(static_cast(_First2), _Count2)) { + return _Impl_first_avx<_Ty>(_First1, _Count1, _First2, _Count2); + } + } else if (_Strat == _Strategy::_Scalar_bitmap) { + if (_Can_fit_256_bits_sse(static_cast(_First2), _Count2)) { + alignas(32) _Scalar_table_t _Table = {}; + _Build_scalar_table_no_check<_Ty>(_First2, _Count2, _Table); + return _Impl_first_scalar<_Ty>(_First1, _Count1, _Table); + } + } + + const void* const _Last1 = static_cast(_First1) + _Count1; + const size_t _Size_bytes_1 = _Count1 * sizeof(_Ty); + const size_t _Size_bytes_2 = _Count2 * sizeof(_Ty); + + return _Pos_from_ptr<_Ty>( + _Impl_pcmpestri<_Ty>(_First1, _Size_bytes_1, _First2, _Size_bytes_2), _First1, _Last1); + } + + template + size_t _Dispatch_pos_avx_4_8( + const void* const _First1, const size_t _Count1, const void* const _First2, const size_t _Count2) noexcept { + using namespace __std_find_meow_of_bitmap; + + const auto _Strat = _Pick_strategy<_Ty>(_Count1, _Count2, true); + + if (_Strat == _Strategy::_Vector_bitmap) { + if (_Can_fit_256_bits_sse(static_cast(_First2), _Count2)) { + return _Impl_first_avx<_Ty>(_First1, _Count1, _First2, _Count2); + } + } else if (_Strat == _Strategy::_Scalar_bitmap) { + if (_Can_fit_256_bits_sse(static_cast(_First2), _Count2)) { + alignas(32) _Scalar_table_t _Table = {}; + _Build_scalar_table_no_check<_Ty>(_First2, _Count2, _Table); + return _Impl_first_scalar<_Ty>(_First1, _Count1, _Table); } } + + const void* const _Last1 = static_cast(_First1) + _Count1; + const size_t _Size_bytes_1 = _Count1 * sizeof(_Ty); + const size_t _Size_bytes_2 = _Count2 * sizeof(_Ty); + + return _Pos_from_ptr<_Ty>(_Impl_4_8<_Ty>(_First1, _Size_bytes_1, _First2, _Size_bytes_2), _First1, _Last1); + } #endif // !_M_ARM64EC - return _Fallback<_Ty>(_First1, _Last1, _First2, _Last2); + + template + size_t _Dispatch_pos_fallback( + const void* const _First1, const size_t _Count1, const void* const _First2, const size_t _Count2) noexcept { + using namespace __std_find_meow_of_bitmap; + + _Scalar_table_t _Table = {}; + if (_Build_scalar_table<_Ty>(_First2, _Count2, _Table)) { + return _Impl_first_scalar<_Ty>(_First1, _Count1, _Table); + } + + const void* const _Last1 = static_cast(_First1) + _Count1; + const void* const _Last2 = static_cast(_First2) + _Count2; + + return _Pos_from_ptr<_Ty>(_Fallback<_Ty>(_First1, _Last1, _First2, _Last2), _First1, _Last1); + } + + template + size_t _Dispatch_pos( + const void* const _First1, const size_t _Count1, const void* const _First2, const size_t _Count2) noexcept { +#ifndef _M_ARM64EC + if constexpr (sizeof(_Ty) <= 2) { + if (_Use_sse42()) { + return _Dispatch_pos_sse_1_2<_Ty>(_First1, _Count1, _First2, _Count2); + } + } else { + if (_Use_avx2()) { + return _Dispatch_pos_avx_4_8<_Ty>(_First1, _Count1, _First2, _Count2); + } + } +#endif // !_M_ARM64EC + return _Dispatch_pos_fallback<_Ty>(_First1, _Count1, _First2, _Count2); } } // namespace __std_find_first_of - template - size_t __stdcall __std_find_last_of_pos_impl(const void* const _Haystack, const size_t _Haystack_length, - const void* const _Needle, const size_t _Needle_length) noexcept { + namespace __std_find_last_of { + template + size_t __stdcall _Fallback(const void* const _Haystack, const size_t _Haystack_length, + const void* const _Needle, const size_t _Needle_length) noexcept { + + const auto _Ptr_haystack = static_cast(_Haystack); + size_t _Pos = _Haystack_length; + const auto _Needle_end = static_cast(_Needle) + _Needle_length; + + while (_Pos != 0) { + --_Pos; + + for (auto _Ptr = static_cast(_Needle); _Ptr != _Needle_end; ++_Ptr) { + if (_Ptr_haystack[_Pos] == *_Ptr) { + return _Pos; + } + } + } + + return static_cast(-1); + } + #ifndef _M_ARM64EC - const size_t _Haystack_length_bytes = _Haystack_length * sizeof(_Ty); - if (_Use_sse42() && _Haystack_length_bytes >= 16) { + template + size_t _Impl(const void* const _Haystack, const size_t _Haystack_length, const void* const _Needle, + const size_t _Needle_length) noexcept { + const size_t _Haystack_length_bytes = _Haystack_length * sizeof(_Ty); + constexpr int _Op = (sizeof(_Ty) == 1 ? _SIDD_UBYTE_OPS : _SIDD_UWORD_OPS) | _SIDD_CMP_EQUAL_ANY | _SIDD_MOST_SIGNIFICANT; constexpr int _Part_size_el = sizeof(_Ty) == 1 ? 16 : 8; @@ -3327,11 +3883,21 @@ namespace { } } - const int _Last_part_size_el = static_cast(_Last_part_size / sizeof(_Ty)); - const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast(_Haystack)); + if (_Last_part_size != 0) { + const int _Last_part_size_el = static_cast(_Last_part_size / sizeof(_Ty)); + __m128i _Data1; + + if (_Haystack_length_bytes >= 16) { + _Data1 = _mm_loadu_si128(reinterpret_cast(_Haystack)); + } else { + alignas(16) uint8_t _Tmp1[16]; + memcpy(_Tmp1, _Haystack, _Haystack_length_bytes); + _Data1 = _mm_load_si128(reinterpret_cast(_Tmp1)); + } - if (_mm_cmpestrc(_Data2, _Needle_length_el, _Data1, _Last_part_size_el, _Op)) { - return _mm_cmpestri(_Data2, _Needle_length_el, _Data1, _Last_part_size_el, _Op); + if (_mm_cmpestrc(_Data2, _Needle_length_el, _Data1, _Last_part_size_el, _Op)) { + return _mm_cmpestri(_Data2, _Needle_length_el, _Data1, _Last_part_size_el, _Op); + } } return static_cast(-1); @@ -3384,30 +3950,60 @@ namespace { } } - const int _Last_part_size_el = static_cast(_Last_part_size / sizeof(_Ty)); - const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast(_Haystack)); - _Test_whole_needle(_Data1, _Last_part_size_el); + if (_Last_part_size != 0) { + const int _Last_part_size_el = static_cast(_Last_part_size / sizeof(_Ty)); + __m128i _Data1; + + if (_Haystack_length_bytes >= 16) { + _Data1 = _mm_loadu_si128(reinterpret_cast(_Haystack)); + } else { + alignas(16) uint8_t _Tmp1[16]; + memcpy(_Tmp1, _Haystack, _Haystack_length_bytes); + _Data1 = _mm_load_si128(reinterpret_cast(_Tmp1)); + } + + _Test_whole_needle(_Data1, _Last_part_size_el); + } return static_cast(_Found_pos); } } #endif // !_M_ARM64EC - const auto _Ptr_haystack = static_cast(_Haystack); - size_t _Pos = _Haystack_length; - const auto _Needle_end = static_cast(_Needle) + _Needle_length; - while (_Pos != 0) { - --_Pos; + template + size_t _Dispatch_pos( + const void* const _First1, const size_t _Count1, const void* const _First2, const size_t _Count2) noexcept { + using namespace __std_find_meow_of_bitmap; + +#ifndef _M_ARM64EC + if (_Use_sse42()) { + const auto _Strat = _Pick_strategy<_Ty>(_Count1, _Count2, _Use_avx2()); - for (auto _Ptr = static_cast(_Needle); _Ptr != _Needle_end; ++_Ptr) { - if (_Ptr_haystack[_Pos] == *_Ptr) { - return _Pos; + if (_Strat == _Strategy::_Vector_bitmap) { + if (_Can_fit_256_bits_sse(static_cast(_First2), _Count2)) { + return _Impl_last_avx<_Ty>(_First1, _Count1, _First2, _Count2); + } + } else if (_Strat == _Strategy::_Scalar_bitmap) { + if (_Can_fit_256_bits_sse(static_cast(_First2), _Count2)) { + alignas(32) _Scalar_table_t _Table = {}; + _Build_scalar_table_no_check<_Ty>(_First2, _Count2, _Table); + return _Impl_last_scalar<_Ty>(_First1, _Count1, _Table); + } } + + return _Impl<_Ty>(_First1, _Count1, _First2, _Count2); + } else +#endif // !_M_ARM64EC + { + alignas(32) _Scalar_table_t _Table = {}; + if (_Build_scalar_table<_Ty>(_First2, _Count2, _Table)) { + return _Impl_last_scalar<_Ty>(_First1, _Count1, _Table); + } + + return _Fallback<_Ty>(_First1, _Count1, _First2, _Count2); } } - - return static_cast(-1); - } + } // namespace __std_find_last_of template __declspec(noalias) size_t __stdcall __std_mismatch_impl( @@ -3965,32 +4561,52 @@ __declspec(noalias) size_t __stdcall __std_count_trivial_8( const void* __stdcall __std_find_first_of_trivial_1( const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { - return __std_find_first_of::_Impl_pcmpestri(_First1, _Last1, _First2, _Last2); + return __std_find_first_of::_Dispatch_ptr(_First1, _Last1, _First2, _Last2); } const void* __stdcall __std_find_first_of_trivial_2( const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { - return __std_find_first_of::_Impl_pcmpestri(_First1, _Last1, _First2, _Last2); + return __std_find_first_of::_Dispatch_ptr(_First1, _Last1, _First2, _Last2); } const void* __stdcall __std_find_first_of_trivial_4( const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { - return __std_find_first_of::_Impl_4_8<__std_find_first_of::_Traits_4>(_First1, _Last1, _First2, _Last2); + return __std_find_first_of::_Dispatch_ptr(_First1, _Last1, _First2, _Last2); } const void* __stdcall __std_find_first_of_trivial_8( const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { - return __std_find_first_of::_Impl_4_8<__std_find_first_of::_Traits_8>(_First1, _Last1, _First2, _Last2); + return __std_find_first_of::_Dispatch_ptr(_First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_1( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept { + return __std_find_first_of::_Dispatch_pos(_Haystack, _Haystack_length, _Needle, _Needle_length); +} + +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_2( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept { + return __std_find_first_of::_Dispatch_pos(_Haystack, _Haystack_length, _Needle, _Needle_length); +} + +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_4( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept { + return __std_find_first_of::_Dispatch_pos(_Haystack, _Haystack_length, _Needle, _Needle_length); +} + +__declspec(noalias) size_t __stdcall __std_find_first_of_trivial_pos_8( + const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept { + return __std_find_first_of::_Dispatch_pos(_Haystack, _Haystack_length, _Needle, _Needle_length); } __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_1(const void* const _Haystack, const size_t _Haystack_length, const void* const _Needle, const size_t _Needle_length) noexcept { - return __std_find_last_of_pos_impl(_Haystack, _Haystack_length, _Needle, _Needle_length); + return __std_find_last_of::_Dispatch_pos(_Haystack, _Haystack_length, _Needle, _Needle_length); } __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_2(const void* const _Haystack, const size_t _Haystack_length, const void* const _Needle, const size_t _Needle_length) noexcept { - return __std_find_last_of_pos_impl(_Haystack, _Haystack_length, _Needle, _Needle_length); + return __std_find_last_of::_Dispatch_pos(_Haystack, _Haystack_length, _Needle, _Needle_length); } const void* __stdcall __std_search_1( From 2378c816cf8d0613d47888509135fcc56820587f Mon Sep 17 00:00:00 2001 From: chandankumar <33762417+kumar80@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:40:31 +0530 Subject: [PATCH 28/35] Update _MSVC_STL_UPDATE to December 2024 (#5162) --- stl/inc/yvals_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index f3d7a9112a2..ea6762d0d2d 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -914,7 +914,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 143 -#define _MSVC_STL_UPDATE 202411L +#define _MSVC_STL_UPDATE 202412L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #if defined(__CUDACC__) && defined(__CUDACC_VER_MAJOR__) From 1a319808deac1ca572cdefea58bf4c9be70d858a Mon Sep 17 00:00:00 2001 From: Hewill Kang Date: Fri, 13 Dec 2024 14:13:01 +0800 Subject: [PATCH 29/35] ``, ``: Add `std::forward` for `append_range` (#5168) --- stl/inc/queue | 8 ++++---- stl/inc/stack | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/queue b/stl/inc/queue index 5e58865e880..b73b8cd1fa7 100644 --- a/stl/inc/queue +++ b/stl/inc/queue @@ -126,8 +126,8 @@ public: #if _HAS_CXX23 template <_Container_compatible_range<_Ty> _Rng> void push_range(_Rng&& _Range) { - if constexpr (requires { c.append_range(_Range); }) { - c.append_range(_Range); + if constexpr (requires { c.append_range(_STD forward<_Rng>(_Range)); }) { + c.append_range(_STD forward<_Rng>(_Range)); } else { _RANGES copy(_Range, back_insert_iterator{c}); } @@ -394,8 +394,8 @@ public: void push_range(_Rng&& _Range) { const size_type _Old_size = c.size(); - if constexpr (requires { c.append_range(_Range); }) { - c.append_range(_Range); + if constexpr (requires { c.append_range(_STD forward<_Rng>(_Range)); }) { + c.append_range(_STD forward<_Rng>(_Range)); } else { _RANGES copy(_Range, back_insert_iterator{c}); } diff --git a/stl/inc/stack b/stl/inc/stack index a50b6ded462..b7ff44d3b80 100644 --- a/stl/inc/stack +++ b/stl/inc/stack @@ -111,8 +111,8 @@ public: #if _HAS_CXX23 template <_Container_compatible_range<_Ty> _Rng> void push_range(_Rng&& _Range) { - if constexpr (requires { c.append_range(_Range); }) { - c.append_range(_Range); + if constexpr (requires { c.append_range(_STD forward<_Rng>(_Range)); }) { + c.append_range(_STD forward<_Rng>(_Range)); } else { _RANGES copy(_Range, back_insert_iterator{c}); } From 0372e788bb5271c96c677436bcba5cfdb97b178b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 12 Dec 2024 22:17:32 -0800 Subject: [PATCH 30/35] ``: Tolerate bogus const-overloading in iterators passed to `uninitialized_meow()` (#5170) --- stl/inc/xmemory | 6 +-- tests/std/test.lst | 1 + .../env.lst | 4 ++ .../test.compile.pass.cpp | 48 +++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 tests/std/tests/VSO_2318081_bogus_const_overloading/env.lst create mode 100644 tests/std/tests/VSO_2318081_bogus_const_overloading/test.compile.pass.cpp diff --git a/stl/inc/xmemory b/stl/inc/xmemory index a5efe180402..462a7465fc5 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1602,7 +1602,7 @@ void _Return_temporary_buffer(_Ty* const _Pbuf) noexcept { } template -void _Construct_in_place_by_deref(_Ty& _Val, const _InIt& _Iter) +void _Construct_in_place_by_deref(_Ty& _Val, _InIt& _Iter) noexcept(noexcept(::new (static_cast(_STD addressof(_Val))) _Ty(*_Iter))) { ::new (static_cast(_STD addressof(_Val))) _Ty(*_Iter); } @@ -1632,14 +1632,14 @@ struct _NODISCARD _Uninitialized_backout { } template - void _Emplace_back_deref(const _InIt& _Iter) { + void _Emplace_back_deref(_InIt& _Iter) { // construct a new element at *_Last from the result of dereferencing _Iter and increment. _STD _Construct_in_place_by_deref(*_Last, _Iter); ++_Last; } template - void _Emplace_back_deref_move(const _InIt& _Iter) { + void _Emplace_back_deref_move(_InIt& _Iter) { // construct a new element at *_Last from the result of dereferencing _Iter and increment, // with lvalue cast to xvalue if necessary for uninitialized_move(_n). if constexpr (is_lvalue_reference_v) { diff --git a/tests/std/test.lst b/tests/std/test.lst index 7b8847181e6..4e38b8fb265 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -782,3 +782,4 @@ tests\VSO_1775715_user_defined_modules tests\VSO_1804139_static_analysis_warning_with_single_element_array tests\VSO_1925201_iter_traits tests\VSO_2252142_wrong_C5046 +tests\VSO_2318081_bogus_const_overloading diff --git a/tests/std/tests/VSO_2318081_bogus_const_overloading/env.lst b/tests/std/tests/VSO_2318081_bogus_const_overloading/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/VSO_2318081_bogus_const_overloading/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/VSO_2318081_bogus_const_overloading/test.compile.pass.cpp b/tests/std/tests/VSO_2318081_bogus_const_overloading/test.compile.pass.cpp new file mode 100644 index 00000000000..06f234bd562 --- /dev/null +++ b/tests/std/tests/VSO_2318081_bogus_const_overloading/test.compile.pass.cpp @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +using namespace std; + +// VSO-2318081 "[RWC][prod/fe][Regression] 4 projects failed with error C2440: +// 'initializing': cannot convert from 'const llvm::Value *' to '_Ty'" + +struct Thing {}; + +struct BogusInIt { + using iterator_category = input_iterator_tag; + using value_type = Thing*; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = Thing*; + + BogusInIt& operator++(); + BogusInIt operator++(int); + friend bool operator==(const BogusInIt&, const BogusInIt&); + friend bool operator!=(const BogusInIt&, const BogusInIt&); + + // This single overload would be conforming: + // Thing* operator*() const; + + // N4993 [iterator.cpp17.general]/1 and [tab:inputiterator] forbid overloading operator*() + // with varying return types, but uninitialized_meow() tolerated this before GH-5135. + + // See LLVM-119084, reported on 2024-12-07. After that has been fixed and propagated throughout the ecosystem, + // we should consider making the STL strictly reject such bogus iterators and removing this test coverage. + Thing* operator*(); + const Thing* operator*() const; +}; + +void test() { + BogusInIt src{}; + Thing** dest{nullptr}; + + uninitialized_copy(src, src, dest); + uninitialized_copy_n(src, 0, dest); +#if _HAS_CXX17 + uninitialized_move(src, src, dest); + uninitialized_move_n(src, 0, dest); +#endif // _HAS_CXX17 +} From 53432ebd1f8d6bc77621fbf9c758508f8390f07e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Fri, 13 Dec 2024 14:21:00 +0800 Subject: [PATCH 31/35] ``: Fix regression with `ranges::to` for ADL-only `begin`/`end` (#5173) Co-authored-by: Hewill Kang <67143766+hewillk@users.noreply.github.com> --- stl/inc/__msvc_ranges_to.hpp | 6 ++++- .../std/tests/P1206R7_ranges_to_misc/test.cpp | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/stl/inc/__msvc_ranges_to.hpp b/stl/inc/__msvc_ranges_to.hpp index ef3bac4ed67..f13f2026a05 100644 --- a/stl/inc/__msvc_ranges_to.hpp +++ b/stl/inc/__msvc_ranges_to.hpp @@ -1121,7 +1121,11 @@ namespace ranges { if constexpr (_Sized_and_reservable<_Rng, _Container>) { _Cont.reserve(static_cast>(_RANGES size(_Range))); } - for (auto&& _Elem : _Range) { + + auto _Iter = _RANGES begin(_Range); + const auto _Sent = _RANGES end(_Range); + for (; _Iter != _Sent; ++_Iter) { + auto&& _Elem = *_Iter; using _ElemTy = decltype(_Elem); if constexpr (_Can_emplace_back<_Container, _ElemTy>) { _Cont.emplace_back(_STD forward<_ElemTy>(_Elem)); diff --git a/tests/std/tests/P1206R7_ranges_to_misc/test.cpp b/tests/std/tests/P1206R7_ranges_to_misc/test.cpp index c788206ffbe..065b8a5dd82 100644 --- a/tests/std/tests/P1206R7_ranges_to_misc/test.cpp +++ b/tests/std/tests/P1206R7_ranges_to_misc/test.cpp @@ -336,6 +336,30 @@ constexpr bool test_lwg4016() { return true; } +struct adl_only_range { + static constexpr int numbers[2]{42, 1729}; + + void begin() const = delete; + void end() const = delete; + + friend constexpr const int* begin(const adl_only_range&) { + return ranges::begin(numbers); + } + friend constexpr const int* end(const adl_only_range&) { + return ranges::end(numbers); + } +}; + +constexpr bool test_lwg4016_regression() { + using vec = restricted_vector; + + ranges::contiguous_range auto r = adl_only_range{}; + auto v = r | ranges::to(); + assert(ranges::equal(v, adl_only_range::numbers)); + + return true; +} + int main() { test_reservable(); static_assert(test_reservable()); @@ -356,4 +380,7 @@ int main() { test_lwg4016(); static_assert(test_lwg4016()); + + test_lwg4016_regression(); + static_assert(test_lwg4016_regression()); } From d4b57c777a02f540f3f77213da63e6dea67be32a Mon Sep 17 00:00:00 2001 From: Hewill Kang Date: Fri, 13 Dec 2024 14:24:16 +0800 Subject: [PATCH 32/35] ``: Fix formatting ranges for ADL-only ranges (#5178) Co-authored-by: A. Jiang --- stl/inc/format | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 3e11d596dc5..6026f34202d 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -3267,8 +3267,11 @@ void _Range_formatter_format_as_sequence(const formatter<_Ty, _CharT>& _Underlyi const basic_string_view<_CharT> _Separator, const basic_string_view<_CharT> _Opening_bracket, const basic_string_view<_CharT> _Closing_bracket, _Range&& _Rng, _FormatContext& _Ctx) { _Ctx.advance_to(_STD _Fmt_write(_Ctx.out(), _Opening_bracket)); - bool _Separate = false; - for (auto&& _Elem : _Rng) { + bool _Separate = false; + auto _Iter = _RANGES begin(_Rng); + const auto _Sent = _RANGES end(_Rng); + for (; _Iter != _Sent; ++_Iter) { + auto&& _Elem = *_Iter; if (_Separate) { _Ctx.advance_to(_STD _Fmt_write(_Ctx.out(), _Separator)); } From 56bd1fe9ceb2c1ebdf2fc7f33664240a8a675295 Mon Sep 17 00:00:00 2001 From: Pavel P Date: Fri, 13 Dec 2024 11:28:29 +0500 Subject: [PATCH 33/35] `STL.natvis`: Simplify visualization for `string_view` (#5176) --- stl/debugger/STL.natvis | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/stl/debugger/STL.natvis b/stl/debugger/STL.natvis index 94ec3470789..6c80510733d 100644 --- a/stl/debugger/STL.natvis +++ b/stl/debugger/STL.natvis @@ -1112,8 +1112,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - {_Mydata,[_Mysize]} - _Mydata,[_Mysize] + {_Mydata,[_Mysize]na} + _Mydata,[_Mysize]na size() @@ -1125,7 +1125,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - _Myptr + _Myptr,na _Myptr @@ -1133,11 +1133,12 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - _Mydata + _Myoff + {_Mydata + _Myoff,[_Mysize - _Myoff]na} + _Mydata + _Myoff,[_Mysize - _Myoff]na _Mydata + _Myoff _Myoff - _Mydata,[_Mysize] + _Mydata,[_Mysize]na From ae9d115c1b9a64b2e6bf2edc492f572f5dc8aeb5 Mon Sep 17 00:00:00 2001 From: Pavel P Date: Fri, 13 Dec 2024 11:33:20 +0500 Subject: [PATCH 34/35] `STL.natvis`: Simplify visualization for `string` (#5177) Co-authored-by: Stephan T. Lavavej --- stl/debugger/STL.natvis | 88 ++++------------------------------------- 1 file changed, 7 insertions(+), 81 deletions(-) diff --git a/stl/debugger/STL.natvis b/stl/debugger/STL.natvis index 6c80510733d..905effbbfdb 100644 --- a/stl/debugger/STL.natvis +++ b/stl/debugger/STL.natvis @@ -1001,17 +1001,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * Notes: * Hard coding _BUF_SIZE for clang-cl compatibility; clang-cl as of 7.0.1 does not emit S_CONSTANT to get _BUF_SIZE * - * char = na format - * wchar_t / unsigned short / char16_t = su format - * char32_t = s32 format + * The `na` format specifier means "no address". For more info, see: + * https://learn.microsoft.com/en-us/visualstudio/debugger/format-specifiers-in-cpp?view=vs-2022 --> - - - + - - + + {_Mypair._Myval2._Bx._Buf,na} @@ -1030,85 +1027,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - - - - - - - - - - - {_Mypair._Myval2._Bx._Buf,su} - {_Mypair._Myval2._Bx._Ptr,su} - _Mypair._Myval2._Bx._Buf,su - _Mypair._Myval2._Bx._Ptr,su - - size() - capacity() - _Mypair - - _Mypair._Myval2._Mysize - _Mypair._Myval2._Bx._Buf - _Mypair._Myval2._Bx._Ptr - - - - - - - - - - - - {_Mypair._Myval2._Bx._Buf,s32} - {_Mypair._Myval2._Bx._Ptr,s32} - _Mypair._Myval2._Bx._Buf,s32 - _Mypair._Myval2._Bx._Ptr,s32 - - size() - capacity() - _Mypair - - _Mypair._Myval2._Mysize - _Mypair._Myval2._Bx._Buf - _Mypair._Myval2._Bx._Ptr - - - - - - - - + + _Ptr,na _Ptr - - - - - - - _Ptr,su - - _Ptr - - - - - - _Ptr,s32 - - _Ptr - - - From 7643c270e5bfb1cfad62f8b5ff4045c662bdaf81 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 12 Dec 2024 22:37:03 -0800 Subject: [PATCH 35/35] Mark `expected`, `unexpected`, and ALL exception types as `[[nodiscard]]` (#5174) --- stl/inc/__msvc_ranges_tuple_formatter.hpp | 2 +- stl/inc/any | 2 +- stl/inc/chrono | 4 ++-- stl/inc/exception | 16 ++++++++-------- stl/inc/execution | 2 +- stl/inc/expected | 14 +++++++------- stl/inc/experimental/filesystem | 2 +- stl/inc/filesystem | 2 +- stl/inc/functional | 3 ++- stl/inc/future | 2 +- stl/inc/memory | 3 ++- stl/inc/optional | 2 +- stl/inc/regex | 2 +- stl/inc/stdexcept | 18 +++++++++--------- stl/inc/system_error | 4 ++-- stl/inc/typeinfo | 6 +++--- stl/inc/xiosbase | 2 +- tests/libcxx/expected_results.txt | 6 ++++++ .../test.cpp | 2 -- 19 files changed, 50 insertions(+), 44 deletions(-) diff --git a/stl/inc/__msvc_ranges_tuple_formatter.hpp b/stl/inc/__msvc_ranges_tuple_formatter.hpp index 2f1ea373b8b..8f544704594 100644 --- a/stl/inc/__msvc_ranges_tuple_formatter.hpp +++ b/stl/inc/__msvc_ranges_tuple_formatter.hpp @@ -78,7 +78,7 @@ _NODISCARD constexpr const _CharT* _Choose_literal(const char* const _Str, const // It's defined here, so that both headers can use this definition. #define _STATICALLY_WIDEN(_CharT, _Literal) (_Choose_literal<_CharT>(_Literal, L##_Literal)) -_EXPORT_STD class format_error : public runtime_error { +_EXPORT_STD class _NODISCARD format_error : public runtime_error { using runtime_error::runtime_error; }; diff --git a/stl/inc/any b/stl/inc/any index 933a463e604..9f776996ac0 100644 --- a/stl/inc/any +++ b/stl/inc/any @@ -28,7 +28,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -_EXPORT_STD class bad_any_cast : public bad_cast { // thrown by failed any_cast +_EXPORT_STD class _NODISCARD bad_any_cast : public bad_cast { // thrown by failed any_cast public: _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { return "Bad any_cast"; diff --git a/stl/inc/chrono b/stl/inc/chrono index 564751b0fdb..c017d6b5068 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -1720,7 +1720,7 @@ namespace chrono { sys_info second; }; - _EXPORT_STD class nonexistent_local_time : public runtime_error { + _EXPORT_STD class _NODISCARD nonexistent_local_time : public runtime_error { public: template nonexistent_local_time(const local_time<_Duration>& _Tp, const local_info& _Info) @@ -1736,7 +1736,7 @@ namespace chrono { _THROW(nonexistent_local_time{_Tp, _Info}); } - _EXPORT_STD class ambiguous_local_time : public runtime_error { + _EXPORT_STD class _NODISCARD ambiguous_local_time : public runtime_error { public: template ambiguous_local_time(const local_time<_Duration>& _Tp, const local_info& _Info) diff --git a/stl/inc/exception b/stl/inc/exception index a5bee7ae569..3d07bb824c6 100644 --- a/stl/inc/exception +++ b/stl/inc/exception @@ -69,7 +69,7 @@ _STD_END #undef stdext _STDEXT_BEGIN -class exception; +class _NODISCARD exception; _STDEXT_END _STD_BEGIN @@ -83,7 +83,7 @@ extern _CRTIMP2_PURE_IMPORT _Prhand _Raise_handler; // pointer to raise handler _STD_END _STDEXT_BEGIN -class exception { // base of all library exceptions +class _NODISCARD exception { // base of all library exceptions public: static _STD _Prhand _Set_raise_handler(_STD _Prhand _Pnew) { // register a handler for _Raise calls const _STD _Prhand _Pold = _STD _Raise_handler; @@ -123,7 +123,7 @@ protected: const char* _Ptr; // the message pointer }; -class bad_exception : public exception { // base of all bad exceptions +class _NODISCARD bad_exception : public exception { // base of all bad exceptions public: __CLR_OR_THIS_CALL bad_exception(const char* _Message = "bad exception") noexcept : exception(_Message) {} @@ -135,9 +135,9 @@ protected: } }; -class bad_array_new_length; +class _NODISCARD bad_array_new_length; -class bad_alloc : public exception { // base of all bad allocation exceptions +class _NODISCARD bad_alloc : public exception { // base of all bad allocation exceptions public: __CLR_OR_THIS_CALL bad_alloc() noexcept : exception("bad allocation", 1) {} // construct from message string with no memory allocation @@ -156,7 +156,7 @@ protected: } }; -class bad_array_new_length : public bad_alloc { +class _NODISCARD bad_array_new_length : public bad_alloc { public: bad_array_new_length() noexcept : bad_alloc("bad array new length") {} }; @@ -390,8 +390,8 @@ _EXPORT_STD template void rethrow_if_nested(const _Ty&) = delete; // requires /GR option #endif // ^^^ !defined(_CPPRTTI) ^^^ -_EXPORT_STD class bad_variant_access - : public exception { // exception for visit of a valueless variant or get on a variant with index() != I +_EXPORT_STD class _NODISCARD bad_variant_access : public exception { + // exception for visit of a valueless variant or get on a variant with index() != I public: bad_variant_access() noexcept = default; diff --git a/stl/inc/execution b/stl/inc/execution index 9362037496e..8f9ed7b5501 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -161,7 +161,7 @@ void _Implicitly_construct_in_place_by_binary_op_transform_deref_rhs( _Ty([&]() -> _Ty { return _Reduce_op(_STD forward<_LeftTy>(_Left), _Transform_op(*_Iter)); }()); } -struct _Parallelism_resources_exhausted : exception { +struct _NODISCARD _Parallelism_resources_exhausted : exception { _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { // return pointer to message string return "Insufficient resources were available to use additional parallelism."; diff --git a/stl/inc/expected b/stl/inc/expected index 82c6d61269e..79e934a52cd 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -25,7 +25,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN _EXPORT_STD template -class unexpected; +class _NODISCARD unexpected; template struct _Check_unexpected_argument : true_type { @@ -39,7 +39,7 @@ struct _Check_unexpected_argument : true_type { // [expected.un.general] _EXPORT_STD template -class unexpected { +class _NODISCARD unexpected { static_assert(_Check_unexpected_argument<_Err>::value); template @@ -107,10 +107,10 @@ template unexpected(_Err) -> unexpected<_Err>; _EXPORT_STD template -class bad_expected_access; +class _NODISCARD bad_expected_access; template <> -class bad_expected_access : public exception { +class _NODISCARD bad_expected_access : public exception { public: _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { return "Bad expected access"; @@ -131,7 +131,7 @@ protected: }; _EXPORT_STD template -class bad_expected_access : public bad_expected_access { +class _NODISCARD bad_expected_access : public bad_expected_access { public: explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) // strengthened : _Unexpected(_STD move(_Unex)) {} @@ -205,7 +205,7 @@ concept _Trivially_move_constructible_assignable_destructible = && is_trivially_destructible_v<_Type>; _EXPORT_STD template -class expected { +class _NODISCARD expected { private: static_assert(_Check_expected_argument<_Ty>::value); static_assert(_Check_unexpected_argument<_Err>::value); @@ -1210,7 +1210,7 @@ concept _Expected_unary_move_assignable = is_move_assignable_v<_Err> && is_move_ template requires is_void_v<_Ty> -class expected<_Ty, _Err> { +class _NODISCARD expected<_Ty, _Err> { private: static_assert(_Check_unexpected_argument<_Err>::value); diff --git a/stl/inc/experimental/filesystem b/stl/inc/experimental/filesystem index 70e40056d1f..71f762adaad 100644 --- a/stl/inc/experimental/filesystem +++ b/stl/inc/experimental/filesystem @@ -1284,7 +1284,7 @@ _NODISCARD path u8path(const basic_string& _Str) { // mak return path{_Path_cvt<_Char8_t, _Pchar>::_Cvt(_Str_out, _Str.c_str(), _Str.size())}; } -class filesystem_error : public system_error { // base of all filesystem-error exceptions +class _NODISCARD filesystem_error : public system_error { // base of all filesystem-error exceptions public: explicit filesystem_error( const string& _Message, error_code _Errcode = make_error_code(errc::operation_not_permitted)) diff --git a/stl/inc/filesystem b/stl/inc/filesystem index d9a4a547b12..8a20d566326 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -1790,7 +1790,7 @@ namespace filesystem { return iterator(_Text.cend(), this); } - _EXPORT_STD class filesystem_error : public system_error { // base of all filesystem-error exceptions + _EXPORT_STD class _NODISCARD filesystem_error : public system_error { // base of all filesystem-error exceptions public: filesystem_error(const string& _Message, const error_code _Errcode) : system_error(_Errcode, _Message), _What(runtime_error::what()) {} diff --git a/stl/inc/functional b/stl/inc/functional index c1a7d6fa547..aa5519259c9 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -690,7 +690,8 @@ _NODISCARD _CONSTEXPR20 _Not_fn> not_fn(_Callable&& _Obj) } #endif // _HAS_CXX17 -_EXPORT_STD class bad_function_call : public exception { // exception thrown when an empty std::function is called +_EXPORT_STD class _NODISCARD bad_function_call : public exception { + // exception thrown when an empty std::function is called public: bad_function_call() noexcept {} diff --git a/stl/inc/future b/stl/inc/future index 49c7b443384..0eaef7372b2 100644 --- a/stl/inc/future +++ b/stl/inc/future @@ -113,7 +113,7 @@ _NODISCARD inline const char* _Future_error_map(int _Errcode) noexcept { // conv } } -_EXPORT_STD class future_error : public logic_error { // future exception +_EXPORT_STD class _NODISCARD future_error : public logic_error { // future exception public: explicit future_error(error_code _Errcode) // internal, TRANSITION, will be removed : logic_error(""), _Mycode(_Errcode) {} diff --git a/stl/inc/memory b/stl/inc/memory index 0e963a59e0a..864f44f90af 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -1086,7 +1086,8 @@ public: }; #endif // _HAS_AUTO_PTR_ETC -_EXPORT_STD class bad_weak_ptr : public exception { // exception type for invalid use of expired weak_ptr object +_EXPORT_STD class _NODISCARD bad_weak_ptr : public exception { + // exception type for invalid use of expired weak_ptr object public: bad_weak_ptr() noexcept {} diff --git a/stl/inc/optional b/stl/inc/optional index b65045b016e..2aae7cd4dd3 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -35,7 +35,7 @@ _EXPORT_STD struct nullopt_t { // no-value state indicator }; _EXPORT_STD inline constexpr nullopt_t nullopt{nullopt_t::_Tag{}}; -_EXPORT_STD class bad_optional_access : public exception { +_EXPORT_STD class _NODISCARD bad_optional_access : public exception { public: _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { return "Bad optional access"; diff --git a/stl/inc/regex b/stl/inc/regex index 77efc9d32f3..b5fa6cae395 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -460,7 +460,7 @@ public: } }; -_EXPORT_STD class regex_error : public runtime_error { // type of all regular expression exceptions +_EXPORT_STD class _NODISCARD regex_error : public runtime_error { // type of all regular expression exceptions public: explicit regex_error(regex_constants::error_type _Ex) : runtime_error(_Stringify(_Ex)), _Err(_Ex) {} diff --git a/stl/inc/stdexcept b/stl/inc/stdexcept index 3d3db7b5259..e3c186976e5 100644 --- a/stl/inc/stdexcept +++ b/stl/inc/stdexcept @@ -17,7 +17,7 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new _STD_BEGIN -_EXPORT_STD class logic_error : public exception { // base of all logic-error exceptions +_EXPORT_STD class _NODISCARD logic_error : public exception { // base of all logic-error exceptions public: using _Mybase = exception; @@ -33,7 +33,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class domain_error : public logic_error { // base of all domain-error exceptions +_EXPORT_STD class _NODISCARD domain_error : public logic_error { // base of all domain-error exceptions public: using _Mybase = logic_error; @@ -49,7 +49,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class invalid_argument : public logic_error { // base of all invalid-argument exceptions +_EXPORT_STD class _NODISCARD invalid_argument : public logic_error { // base of all invalid-argument exceptions public: using _Mybase = logic_error; @@ -65,7 +65,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class length_error : public logic_error { // base of all length-error exceptions +_EXPORT_STD class _NODISCARD length_error : public logic_error { // base of all length-error exceptions public: using _Mybase = logic_error; @@ -81,7 +81,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class out_of_range : public logic_error { // base of all out-of-range exceptions +_EXPORT_STD class _NODISCARD out_of_range : public logic_error { // base of all out-of-range exceptions public: using _Mybase = logic_error; @@ -97,7 +97,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class runtime_error : public exception { // base of all runtime-error exceptions +_EXPORT_STD class _NODISCARD runtime_error : public exception { // base of all runtime-error exceptions public: using _Mybase = exception; @@ -113,7 +113,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class overflow_error : public runtime_error { // base of all overflow-error exceptions +_EXPORT_STD class _NODISCARD overflow_error : public runtime_error { // base of all overflow-error exceptions public: using _Mybase = runtime_error; @@ -129,7 +129,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class underflow_error : public runtime_error { // base of all underflow-error exceptions +_EXPORT_STD class _NODISCARD underflow_error : public runtime_error { // base of all underflow-error exceptions public: using _Mybase = runtime_error; @@ -145,7 +145,7 @@ protected: #endif // !_HAS_EXCEPTIONS }; -_EXPORT_STD class range_error : public runtime_error { // base of all range-error exceptions +_EXPORT_STD class _NODISCARD range_error : public runtime_error { // base of all range-error exceptions public: using _Mybase = runtime_error; diff --git a/stl/inc/system_error b/stl/inc/system_error index 51ac72440b7..bcf2ca0d88f 100644 --- a/stl/inc/system_error +++ b/stl/inc/system_error @@ -458,7 +458,7 @@ struct hash { } }; -class _System_error : public runtime_error { // base of all system-error exceptions +class _NODISCARD _System_error : public runtime_error { // base of all system-error exceptions private: static string _Makestr(error_code _Errcode, string _Message) { // compose error message if (!_Message.empty()) { @@ -478,7 +478,7 @@ protected: error_code _Mycode; // the stored error code }; -_EXPORT_STD class system_error : public _System_error { // base of all system-error exceptions +_EXPORT_STD class _NODISCARD system_error : public _System_error { // base of all system-error exceptions private: using _Mybase = _System_error; diff --git a/stl/inc/typeinfo b/stl/inc/typeinfo index e7df437d17a..35e13045dd5 100644 --- a/stl/inc/typeinfo +++ b/stl/inc/typeinfo @@ -29,7 +29,7 @@ _STD_BEGIN _INLINE_VAR constexpr int _Small_object_num_ptrs = 6 + 16 / sizeof(void*); #if !_HAS_EXCEPTIONS -_EXPORT_STD class bad_cast : public exception { // base of all bad cast exceptions +_EXPORT_STD class _NODISCARD bad_cast : public exception { // base of all bad cast exceptions public: bad_cast(const char* _Message = "bad cast") noexcept : exception(_Message) {} @@ -41,7 +41,7 @@ protected: } }; -_EXPORT_STD class bad_typeid : public exception { // base of all bad typeid exceptions +_EXPORT_STD class _NODISCARD bad_typeid : public exception { // base of all bad typeid exceptions public: bad_typeid(const char* _Message = "bad typeid") noexcept : exception(_Message) {} @@ -53,7 +53,7 @@ protected: } }; -class __non_rtti_object : public bad_typeid { // report a non-RTTI object +class _NODISCARD __non_rtti_object : public bad_typeid { // report a non-RTTI object public: __non_rtti_object(const char* _Message) : bad_typeid(_Message) {} }; diff --git a/stl/inc/xiosbase b/stl/inc/xiosbase index 3d19d5969a2..09cd9d407aa 100644 --- a/stl/inc/xiosbase +++ b/stl/inc/xiosbase @@ -108,7 +108,7 @@ public: using seek_dir = unsigned int; #endif // _HAS_OLD_IOSTREAMS_MEMBERS - class failure : public system_error { // base of all iostreams exceptions + class _NODISCARD failure : public system_error { // base of all iostreams exceptions public: explicit failure(const string& _Message, const error_code& _Errcode = _STD make_error_code(io_errc::stream)) : system_error(_Errcode, _Message) {} // construct with message diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 1da736fc4db..3329d34b8f1 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -36,6 +36,12 @@ std/numerics/rand/rand.dist/rand.dist.uni/rand.dist.uni.real/param_ctor.pass.cpp # LLVM-113609: [libc++][test] Non-rebindable test_alloc in string.capacity/deallocate_size.pass.cpp std/strings/basic.string/string.capacity/deallocate_size.pass.cpp FAIL +# LLVM-119174: [libcxx][test] Silence nodiscard warnings for std::expected +std/utilities/expected/expected.expected/monadic/and_then.pass.cpp FAIL +std/utilities/expected/expected.expected/monadic/or_else.pass.cpp FAIL +std/utilities/expected/expected.expected/monadic/transform.pass.cpp FAIL +std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp FAIL + # Non-Standard regex behavior. # "It seems likely that the test is still non-conforming due to how libc++ handles the 'w' character class." std/re/re.traits/lookup_classname.pass.cpp FAIL diff --git a/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp index c59218e8fa1..b075960ef58 100644 --- a/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp +++ b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp @@ -217,8 +217,6 @@ constexpr void test_impl(Expected&& engaged, Expected&& unengaged) { assert(result.value().x == 77); } } - - engaged.transform([](auto...) { return ""; }); } template