diff --git a/README.md b/README.md index 0a4d9dff453..70e3e9041e7 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem The STL uses boost-math headers to provide P0226R1 Mathematical Special Functions. We recommend using [vcpkg][] to acquire this dependency. -1. Install Visual Studio 2019 16.8 Preview 2 or later. +1. Install Visual Studio 2019 16.8 Preview 3 or later. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. This will ensure that you're using supported versions of CMake and Ninja. * Otherwise, install [CMake][] 3.17 or later, and [Ninja][] 1.8.2 or later. @@ -158,7 +158,7 @@ acquire this dependency. # How To Build With A Native Tools Command Prompt -1. Install Visual Studio 2019 16.8 Preview 2 or later. +1. Install Visual Studio 2019 16.8 Preview 3 or later. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. This will ensure that you're using supported versions of CMake and Ninja. * Otherwise, install [CMake][] 3.17 or later, and [Ninja][] 1.8.2 or later. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 69b79c443a4..69437b81002 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,7 @@ # Build STL targeting x86, x64, arm, arm64 variables: - agentPool: 'StlBuild-2020-08-26' + agentPool: 'StlBuild-2020-09-14' tmpDir: 'D:\Temp' stages: diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 3314aab65d3..74c5f383bfc 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -8,6 +8,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/any ${CMAKE_CURRENT_LIST_DIR}/inc/array ${CMAKE_CURRENT_LIST_DIR}/inc/atomic + ${CMAKE_CURRENT_LIST_DIR}/inc/barrier ${CMAKE_CURRENT_LIST_DIR}/inc/bit ${CMAKE_CURRENT_LIST_DIR}/inc/bitset ${CMAKE_CURRENT_LIST_DIR}/inc/cassert @@ -153,6 +154,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/iso646.h ${CMAKE_CURRENT_LIST_DIR}/inc/istream ${CMAKE_CURRENT_LIST_DIR}/inc/iterator + ${CMAKE_CURRENT_LIST_DIR}/inc/latch ${CMAKE_CURRENT_LIST_DIR}/inc/limits ${CMAKE_CURRENT_LIST_DIR}/inc/list ${CMAKE_CURRENT_LIST_DIR}/inc/locale @@ -171,6 +173,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/ratio ${CMAKE_CURRENT_LIST_DIR}/inc/regex ${CMAKE_CURRENT_LIST_DIR}/inc/scoped_allocator + ${CMAKE_CURRENT_LIST_DIR}/inc/semaphore ${CMAKE_CURRENT_LIST_DIR}/inc/set ${CMAKE_CURRENT_LIST_DIR}/inc/shared_mutex ${CMAKE_CURRENT_LIST_DIR}/inc/span diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index c7aaea945e9..3ba146c97bc 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -105,6 +105,9 @@ #ifndef _M_CEE_PURE #include +#include +#include +#include #endif // _M_CEE_PURE #ifndef _M_CEE diff --git a/stl/inc/algorithm b/stl/inc/algorithm index d52591085a8..9e0d2458f7d 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -274,6 +274,24 @@ namespace ranges { } } + template + _NODISCARD constexpr auto _Get_final_iterator_unwrapped(_Rng& _Range, const _Unwrapped_t>& _Mid) { + // find the (unwrapped) iterator in _Range which equals _Uend(_Range) [possibly O(N)] + // Pre: [ranges::begin(_Range), _Mid) and [_Mid, ranges::end(_Range)) denote ranges + if constexpr (common_range<_Rng>) { + return _Uend(_Range); + } else if constexpr (sized_range<_Rng>) { + const auto _Dist = _RANGES distance(_Range); + if constexpr (sized_sentinel_for<_Unwrapped_t>, _Unwrapped_t>>) { + return _RANGES next(_Mid, _Dist - (_Mid - _Ubegin(_Range))); + } else { + return _RANGES next(_Ubegin(_Range), _Dist); + } + } else { + return _RANGES next(_Mid, _Uend(_Range)); + } + } + #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ @@ -459,52 +477,6 @@ template _Se, class _Pj, indirect_unary_predicate> _Pr> - _NODISCARD constexpr _It _Find_if_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { - for (; _First != _Last; ++_First) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - break; - } - } - - return _First; - } - - class _Find_if_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - template _Se, class _Pj = identity, - indirect_unary_predicate> _Pr> - _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Last); - auto _UResult = _RANGES _Find_if_unchecked( - _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - - _Seek_wrapped(_First, _STD move(_UResult)); - return _First; - } - - template , _Pj>> _Pr> - _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto _UResult = _RANGES _Find_if_unchecked( - _Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - - _Seek_wrapped(_First, _STD move(_UResult)); - return _First; - } - }; - - inline constexpr _Find_if_fn find_if{_Not_quite_object::_Construct_tag{}}; -} // namespace ranges -#endif // __cpp_lib_concepts - // FUNCTION TEMPLATE find_if_not template _NODISCARD _CONSTEXPR20 _InIt find_if_not(_InIt _First, const _InIt _Last, _Pr _Pred) { @@ -3972,7 +3944,7 @@ namespace ranges { #endif // __cpp_lib_is_constant_evaluated { const auto _Distance = static_cast(_ULast - _UFirst); - _CSTD memset(_UFirst, static_cast(_Value), _Distance); + _Fill_memset(_UFirst, _Value, _Distance); _UFirst += _Distance; _Seek_wrapped(_First, _UFirst); return _First; @@ -4011,7 +3983,7 @@ namespace ranges { if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - _CSTD memset(_UFirst, static_cast(_Value), static_cast(_Count)); + _Fill_memset(_UFirst, _Value, static_cast(_Count)); _UFirst += _Count; _Seek_wrapped(_First, _UFirst); // no need to move since _UFirst is a pointer return _First; @@ -8077,6 +8049,85 @@ void partial_sort(_ExPo&&, _RanIt _First, _RanIt _Mid, _RanIt _Last) noexcept /* // parallelism suspected to be infeasible return _STD partial_sort(_First, _Mid, _Last); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::partial_sort + class _Partial_sort_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr _It operator()(_It _First, _It _Mid, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + + if constexpr (is_same_v<_It, _Se>) { + _Partial_sort_common(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), + _Get_unwrapped(_Last), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Last; + } else { + auto _UMid = _Get_unwrapped(_STD move(_Mid)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UMid, _STD move(_Last)); + _Seek_wrapped(_Mid, _ULast); + _Partial_sort_common(_Get_unwrapped(_STD move(_First)), _STD move(_UMid), _STD move(_ULast), + _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Mid; + } + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + constexpr borrowed_iterator_t<_Rng> operator()( + _Rng&& _Range, iterator_t<_Rng> _Mid, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_RANGES begin(_Range), _Mid); + _Adl_verify_range(_Mid, _RANGES end(_Range)); + + if constexpr (common_range<_Rng>) { + _Partial_sort_common( + _Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _RANGES end(_Range); + } else { + auto _UMid = _Get_unwrapped(_STD move(_Mid)); + auto _ULast = _Get_final_iterator_unwrapped(_Range, _UMid); + _Seek_wrapped(_Mid, _ULast); + _Partial_sort_common( + _Ubegin(_Range), _STD move(_UMid), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Mid; + } + } + + private: + template + static constexpr void _Partial_sort_common(_It _First, _It _Mid, const _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + if (_First == _Mid) { + return; // nothing to do + } + + _Make_heap_common(_First, _Mid, _Pred, _Proj); + for (auto _Next = _Mid; _Next != _Last; ++_Next) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { + // replace top with new largest + iter_value_t<_It> _Val = _RANGES iter_move(_Next); + _RANGES _Pop_heap_hole_unchecked(_First, _Mid, _Next, _STD move(_Val), _Pred, _Proj, _Proj); + } + } + + _Sort_heap_common(_STD move(_First), _STD move(_Mid), _Pred, _Proj); + } + }; + + inline constexpr _Partial_sort_fn partial_sort{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE partial_sort_copy @@ -8136,6 +8187,102 @@ _RanIt partial_sort_copy(_ExPo&&, _FwdIt _First1, _FwdIt _Last1, _RanIt _First2, _REQUIRE_PARALLEL_ITERATOR(_FwdIt); return _STD partial_sort_copy(_First1, _Last1, _First2, _Last2); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE partial_sort_copy_result + template + using partial_sort_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::partial_sort_copy + class _Partial_sort_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, random_access_iterator _It2, sentinel_for<_It2> _Se2, + class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires indirectly_copyable<_It1, _It2> + && sortable<_It2, _Pr, _Pj2> + && indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>> + constexpr partial_sort_copy_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + // clang-format on + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + + auto _UResult = _Partial_sort_copy_unchecked(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + + _Seek_wrapped(_First1, _STD move(_UResult.in)); + _Seek_wrapped(_First2, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2)}; + } + + // clang-format off + template + requires indirectly_copyable, iterator_t<_Rng2>> + && sortable, _Pr, _Pj2> + && indirect_strict_weak_order<_Pr, projected, _Pj1>, + projected, _Pj2>> + constexpr partial_sort_copy_result, borrowed_iterator_t<_Rng2>> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + // clang-format on + auto _First = _RANGES begin(_Range1); + auto _UResult = _Partial_sort_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + return {_STD move(_First), _Rewrap_iterator(_Range2, _STD move(_UResult.out))}; + } + + private: + template + _NODISCARD static constexpr partial_sort_copy_result<_It1, _It2> _Partial_sort_copy_unchecked( + _It1 _First1, _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It1, _It2>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It2, _Pr, _Pj2>); + _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + + if (_First1 == _Last1 || _First2 == _Last2) { + _RANGES advance(_First1, _STD move(_Last1)); + return {_STD move(_First1), _STD move(_First2)}; + } + + // copy N = min(distance(_First1, _Last1), distance(_First2, _Last2)) elements + auto _Mid2 = _First2; + do { + *_Mid2 = *_First1; + ++_First1; + ++_Mid2; + } while (_First1 != _Last1 && _Mid2 != _Last2); + + _Make_heap_common(_First2, _Mid2, _Pred, _Proj2); // Make a heap + for (; _First1 != _Last1; ++_First1) { // Scan the remaining elements... + // ... for values less than the largest in the heap ... + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + // ... to replace the largest, after which we restore the heap invariants. + using _Diff = iter_difference_t<_It2>; + _RANGES _Pop_heap_hole_by_index(_First2, static_cast<_Diff>(0), static_cast<_Diff>(_Mid2 - _First2), + *_First1, _Pred, _Proj2, _Proj1); + } + } + + // the heap contains the N smallest elements; sort them + _Sort_heap_common(_STD move(_First2), _Mid2, _Pred, _Proj2); + return {_STD move(_First1), _STD move(_Mid2)}; + } + }; + + inline constexpr _Partial_sort_copy_fn partial_sort_copy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE nth_element diff --git a/stl/inc/barrier b/stl/inc/barrier new file mode 100644 index 00000000000..2258cfe26de --- /dev/null +++ b/stl/inc/barrier @@ -0,0 +1,198 @@ +// barrier standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _BARRIER_ +#define _BARRIER_ +#include +#if _STL_COMPILER_PREPROCESSOR + +#ifdef _M_CEE_PURE +#error is not supported when compiling with /clr:pure. +#endif // _M_CEE_PURE + +#if !_HAS_CXX20 +#pragma message("The contents of are available only with C++20 or later.") +#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv + +#include +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +struct _No_completion_function { + void operator()() noexcept {} +}; + +template +class barrier; + +inline constexpr ptrdiff_t _Barrier_arrival_token_mask = 1; +inline constexpr ptrdiff_t _Barrier_value_mask = ~_Barrier_arrival_token_mask; +inline constexpr ptrdiff_t _Barrier_value_shift = 1; +inline constexpr ptrdiff_t _Barrier_invalid_token = 0; +inline constexpr ptrdiff_t _Barrier_value_step = 1 << _Barrier_value_shift; +inline constexpr ptrdiff_t _Barrier_max = (1ULL << (sizeof(ptrdiff_t) * CHAR_BIT - 2)) - 1; + +template +class _Arrival_token { +public: + _Arrival_token(_Arrival_token&& _Other) noexcept { + _Value = _Other._Value; + _Other._Value = _Barrier_invalid_token; + } + + _Arrival_token& operator=(_Arrival_token&& _Other) noexcept { + _Value = _Other._Value; + _Other._Value = _Barrier_invalid_token; + return *this; + } + +private: + explicit _Arrival_token(ptrdiff_t _Value_) noexcept : _Value(_Value_) {} + friend barrier<_Completion_function>; + + ptrdiff_t _Value; +}; + +template +class barrier { +public: + static_assert( +#ifndef __cpp_noexcept_function_type + is_function_v> || +#endif // __cpp_noexcept_function_type + is_nothrow_invocable_v<_Completion_function&>, + "N4861 [thread.barrier.class]/5: is_nothrow_invocable_v shall be true"); + + using arrival_token = _Arrival_token<_Completion_function>; + + constexpr explicit barrier( + const ptrdiff_t _Expected, _Completion_function _Fn = _Completion_function()) noexcept /* strengthened */ + : _Val(_One_then_variadic_args_t{}, _STD move(_Fn), _Expected << _Barrier_value_shift) { + _STL_VERIFY(_Val._Myval2._Current.load(memory_order_relaxed) >= 0, + "Precondition: expected >= 0 and expected <= max() (N4861 [thread.barrier.class]/9)"); + } + + barrier(const barrier&) = delete; + barrier& operator=(const barrier&) = delete; + + _NODISCARD static constexpr ptrdiff_t(max)() noexcept { + return _Barrier_max; + } + + _NODISCARD arrival_token arrive(ptrdiff_t _Update = 1) noexcept /* strengthened */ { + // Shifting before precondition check, so that exceeding max() will trigger precondition check too + _Update <<= _Barrier_value_shift; + _STL_VERIFY(_Update > 0, "Precondition: update > 0 (N4861 [thread.barrier.class]/12)"); + // TRANSITION, GH-1133: should be memory_order_release + ptrdiff_t _Current = _Val._Myval2._Current.fetch_sub(_Update) - _Update; + _STL_VERIFY(_Current >= 0, "Precondition: update is less than or equal to the expected count " + "for the current barrier phase (N4861 [thread.barrier.class]/12)"); + if ((_Current & _Barrier_value_mask) == 0) { + // TRANSITION, GH-1133: should have this fence: + // atomic_thread_fence(memory_order_acquire); + _Completion(_Current); + } + // Embedding this into the token to provide an additional correctness check that the token is from the same + // barrier and wasn't used. All bits of this fit, as barrier should be aligned to at least the size of an + // atomic counter. + return arrival_token{(_Current & _Barrier_arrival_token_mask) | reinterpret_cast(this)}; + } + + void wait(arrival_token&& _Arrival) const noexcept /* strengthened */ { + _STL_VERIFY((_Arrival._Value & _Barrier_value_mask) == reinterpret_cast(this), + "Preconditions: arrival is associated with the phase synchronization point for the current phase " + "or the immediately preceding phase of the same barrier object (N4861 [thread.barrier.class]/19)"); + const ptrdiff_t _Arrival_value = _Arrival._Value & _Barrier_arrival_token_mask; + _Arrival._Value = _Barrier_invalid_token; + for (;;) { + // TRANSITION, GH-1133: should be memory_order_acquire + const ptrdiff_t _Current = _Val._Myval2._Current.load(); + _STL_VERIFY(_Current >= 0, "Invariant counter >= 0, possibly caused by preconditions violation " + "(N4861 [thread.barrier.class]/12)"); + if ((_Current & _Barrier_arrival_token_mask) != _Arrival_value) { + break; + } + _Val._Myval2._Current.wait(_Current, memory_order_relaxed); + } + } + + void arrive_and_wait() noexcept /* strengthened */ { + // TRANSITION, GH-1133: should be memory_order_acq_rel + ptrdiff_t _Current = _Val._Myval2._Current.fetch_sub(_Barrier_value_step) - _Barrier_value_step; + const ptrdiff_t _Arrival = _Current & _Barrier_arrival_token_mask; + _STL_VERIFY(_Current >= 0, "Precondition: update is less than or equal to the expected count " + "for the current barrier phase (N4861 [thread.barrier.class]/12)"); + if ((_Current & _Barrier_value_mask) == 0) { + _Completion(_Current); + return; + } + + for (;;) { + _Val._Myval2._Current.wait(_Current, memory_order_relaxed); + // TRANSITION, GH-1133: should be memory_order_acquire + _Current = _Val._Myval2._Current.load(); + _STL_VERIFY(_Current >= 0, "Invariant counter >= 0, possibly caused by preconditions violation " + "(N4861 [thread.barrier.class]/12)"); + if ((_Current & _Barrier_arrival_token_mask) != _Arrival) { + break; + } + } + } + + void arrive_and_drop() noexcept /* strengthened */ { + const ptrdiff_t _Rem_count = + _Val._Myval2._Total.fetch_sub(_Barrier_value_step, memory_order_relaxed) - _Barrier_value_step; + _STL_VERIFY(_Rem_count >= 0, "Precondition: The expected count for the current barrier phase " + "is greater than zero (N4861 [thread.barrier.class]/24) " + "(checked initial expected count, which is not less than the current)"); + (void) arrive(1); + } + +private: + void _Completion(const ptrdiff_t _Current) noexcept { + const ptrdiff_t _Rem_count = _Val._Myval2._Total.load(memory_order_relaxed); + _STL_VERIFY(_Rem_count >= 0, "Invariant: initial expected count less than zero, " + "possibly caused by preconditions violation " + "(N4861 [thread.barrier.class]/24)"); + _Val._Get_first()(); + const ptrdiff_t _New_phase_count = _Rem_count | ((_Current + 1) & _Barrier_arrival_token_mask); + // TRANSITION, GH-1133: should be memory_order_release + _Val._Myval2._Current.store(_New_phase_count); + _Val._Myval2._Current.notify_all(); + } + + struct _Counter_t { + constexpr explicit _Counter_t(ptrdiff_t _Initial) : _Current(_Initial), _Total(_Initial) {} + // wait(arrival_token&&) accepts a token from the current phase or the immediately preceding phase; this means + // we can track which phase is the current phase using 1 bit which alternates between each phase. For this + // purpose we use the low order bit of _Current. + atomic _Current; + atomic _Total; + }; + + _Compressed_pair<_Completion_function, _Counter_t> _Val; +}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // ^^^ _HAS_CXX20 ^^^ + +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _BARRIER_ diff --git a/stl/inc/concepts b/stl/inc/concepts index 4da6a23f497..fad9a75caa8 100644 --- a/stl/inc/concepts +++ b/stl/inc/concepts @@ -165,9 +165,10 @@ namespace ranges { }; } // namespace _Swap - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Swap::_Cpo swap; } + using namespace _Cpos; } // namespace ranges // CONCEPT swappable diff --git a/stl/inc/coroutine b/stl/inc/coroutine index 2c939373da9..c041e17463b 100644 --- a/stl/inc/coroutine +++ b/stl/inc/coroutine @@ -126,7 +126,11 @@ _NODISCARD constexpr bool operator==(const coroutine_handle<> _Left, const corou _NODISCARD constexpr strong_ordering operator<=>( const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { - return compare_three_way()(_Left.address(), _Right.address()); +#ifdef __cpp_lib_concepts + return compare_three_way{}(_Left.address(), _Right.address()); +#else // ^^^ no workaround / workaround vvv + return _Left.address() <=> _Right.address(); +#endif // __cpp_lib_concepts } template diff --git a/stl/inc/experimental/generator b/stl/inc/experimental/generator index 60e2a896de7..e19a80142d3 100644 --- a/stl/inc/experimental/generator +++ b/stl/inc/experimental/generator @@ -9,19 +9,19 @@ #include #if _STL_COMPILER_PREPROCESSOR -#if !defined(__cpp_coroutines) && !defined(__cpp_impl_coroutine) -#error requires /std:c++latest or /await compiler options -#endif // !defined(__cpp_coroutines) && !defined(__cpp_impl_coroutine) - #ifdef _CPPUNWIND #include #endif #include -#ifdef __cpp_coroutines -#include -#else // __cpp_coroutines + +#if defined(__cpp_impl_coroutine) #include -#endif // __cpp_coroutines +#elif defined(__cpp_coroutines) +#include +#else // ^^^ legacy coroutines / no coroutine support vvv +#error requires /std:c++latest or /await compiler options +#endif // ^^^ no coroutine support ^^^ + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) diff --git a/stl/inc/iterator b/stl/inc/iterator index 19d67597d39..fa219671771 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -513,6 +513,511 @@ private: }; #ifdef __cpp_lib_concepts +// CLASS TEMPLATE common_iterator +enum class _Variantish_state : unsigned char { _Nothing, _Holds_iter, _Holds_sentinel }; + +struct _Common_iterator_construct_tag { + explicit _Common_iterator_construct_tag() = default; +}; + +template _Se> +class _Variantish { +public: + constexpr explicit _Variantish(_Common_iterator_construct_tag) noexcept : _Contains{_Variantish_state::_Nothing} {} + + constexpr _Variantish() noexcept(is_nothrow_default_constructible_v<_It>) + : _Iterator{}, _Contains{_Variantish_state::_Holds_iter} {} + + template + constexpr explicit _Variantish(in_place_type_t<_It>, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_It, _Types...>) + : _Iterator(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_iter} {} + + template + constexpr explicit _Variantish(in_place_type_t<_Se>, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_Se, _Types...>) + : _Sentinel(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_sentinel} {} + + // clang-format off + template + requires _Not_same_as<_Variantish<_OIter, _OSe>, _Variantish> + _Variantish(const _Variantish<_OIter, _OSe>& _That) noexcept( + is_nothrow_constructible_v<_It, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&>) + : _Contains{_That._Contains} { + // clang-format on + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish(const _Variantish&) requires is_trivially_copy_constructible_v<_It> + && is_trivially_copy_constructible_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish(const _Variantish& _That) noexcept( + noexcept(is_nothrow_copy_constructible_v<_It>&& is_nothrow_copy_constructible_v<_Se>)) + : _Contains{_That._Contains} { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish(_Variantish&&) requires is_trivially_move_constructible_v<_It> + && is_trivially_move_constructible_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish(_Variantish&& _That) noexcept( + is_nothrow_move_constructible_v<_It>&& is_nothrow_move_constructible_v<_Se>) + : _Contains{_That._Contains} { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _STD move(_That._Iterator)); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _STD move(_That._Sentinel)); + break; + case _Variantish_state::_Nothing: + break; + } + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + ~_Variantish() requires is_trivially_destructible_v<_It> && is_trivially_destructible_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + ~_Variantish() { + _Raw_clear(); + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish& operator=(const _Variantish&) requires is_trivially_destructible_v<_It> + && is_trivially_destructible_v<_Se> + && is_trivially_copy_constructible_v<_It> + && is_trivially_copy_constructible_v<_Se> + && is_trivially_copy_assignable_v<_It> + && is_trivially_copy_assignable_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish& operator=(const _Variantish& _That) noexcept( + is_nothrow_copy_constructible_v<_It>&& is_nothrow_copy_constructible_v<_Se>&& + is_nothrow_copy_assignable_v<_It>&& is_nothrow_copy_assignable_v<_Se>) { + if (_Contains == _That._Contains) { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator = _That._Iterator; + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel = _That._Sentinel; + break; + case _Variantish_state::_Nothing: + break; + } + + return *this; + } + + _Clear(); + + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + _Contains = _That._Contains; + + return *this; + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish& operator=(_Variantish&&) requires is_trivially_destructible_v<_It> + && is_trivially_destructible_v<_Se> + && is_trivially_move_constructible_v<_It> + && is_trivially_move_constructible_v<_Se> + && is_trivially_move_assignable_v<_It> + && is_trivially_move_assignable_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish& operator=(_Variantish&& _That) noexcept( + is_nothrow_move_constructible_v<_It>&& is_nothrow_move_constructible_v<_Se>&& + is_nothrow_move_assignable_v<_It>&& is_nothrow_move_assignable_v<_Se>) { + if (_Contains == _That._Contains) { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator = _STD move(_That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel = _STD move(_That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + return *this; + } + + _Clear(); + + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _STD move(_That._Iterator)); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _STD move(_That._Sentinel)); + break; + case _Variantish_state::_Nothing: + break; + } + + _Contains = _That._Contains; + + return *this; + } + + // clang-format off + template + requires _Not_same_as<_Variantish<_OIter, _OSe>, _Variantish> + _Variantish& operator=(const _Variantish<_OIter, _OSe>& _That) noexcept( + is_nothrow_constructible_v<_It, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&> + && is_nothrow_assignable_v<_It&, const _OIter&> && is_nothrow_assignable_v<_Se&, const _OSe&>) { + // clang-format on + if (_Contains == _That._Contains) { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator = _That._Iterator; + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel = _That._Sentinel; + break; + case _Variantish_state::_Nothing: + break; + } + + return *this; + } + + _Clear(); + + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + _Contains = _That._Contains; + + return *this; + } + + // clang-format off + constexpr friend void swap(_Variantish& _Left, _Variantish& _Right) noexcept( + is_nothrow_move_constructible_v<_It> && is_nothrow_move_constructible_v<_Se> + && is_nothrow_swappable_v<_It> && is_nothrow_swappable_v<_Se>) + requires (!_Is_trivially_swappable_v<_It> || !_Is_trivially_swappable_v<_Se>) { + // clang-format on + if (_Left._Contains == _Right._Contains) { + switch (_Left._Contains) { + case _Variantish_state::_Holds_iter: + _RANGES swap(_Left._Iterator, _Right._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _RANGES swap(_Left._Sentinel, _Right._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + return; + } + + auto _Tmp = _STD move(_Left); + _Left = _STD move(_Right); + _Right = _STD move(_Tmp); + } + + void _Raw_clear() noexcept { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator.~_It(); + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel.~_Se(); + break; + case _Variantish_state::_Nothing: + break; + } + } + + void _Clear() noexcept { + _Raw_clear(); + _Contains = _Variantish_state::_Nothing; + } + + union { + _It _Iterator; + _Se _Sentinel; + }; + + _Variantish_state _Contains; +}; + +// clang-format off +template _Se> + requires (!same_as<_Iter, _Se> && copyable<_Iter>) +class common_iterator { + // clang-format on +private: + class _Proxy { + private: + iter_value_t<_Iter> _Keep; + + public: + explicit _Proxy(iter_reference_t<_Iter>&& _Right) noexcept( + is_nothrow_move_constructible_v>) // strengthened + : _Keep(_STD move(_Right)) {} + + _NODISCARD const iter_value_t<_Iter>* operator->() const noexcept /* strengthened */ { + return _STD addressof(_Keep); + } + }; + +public: + constexpr common_iterator() = default; + + constexpr common_iterator(_Iter _Right) noexcept(is_nothrow_move_constructible_v<_Iter>) // strengthened + : _Val{in_place_type<_Iter>, _STD move(_Right)} {} + + constexpr common_iterator(_Se _Right) noexcept(is_nothrow_move_constructible_v<_Se>) // strengthened + : _Val{in_place_type<_Se>, _STD move(_Right)} {} + + constexpr explicit common_iterator(_Common_iterator_construct_tag _Tag) noexcept : _Val{_Tag} {} + + // clang-format off + template + requires convertible_to && convertible_to + constexpr common_iterator(const common_iterator<_OIter, _OSe>& _Right) noexcept( + is_nothrow_constructible_v<_Iter, const _OIter&>&& is_nothrow_constructible_v<_Se, const _OSe&>) // strengthened + : _Val{_Right._Val} {} + + template + requires convertible_to && convertible_to + && assignable_from<_Iter&, const _OIter&> && assignable_from<_Se&, const _OSe&> + common_iterator& operator=(const common_iterator<_OIter, _OSe>& _Right) noexcept( + is_nothrow_constructible_v<_Iter, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&> + && is_nothrow_assignable_v<_Iter&, const _OIter&> + && is_nothrow_assignable_v<_Se&, const _OSe&>) /* strengthened */ { + // clang-format on + _Val = _Right._Val; + return *this; + } + + _NODISCARD decltype(auto) operator*() { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be dereferenced if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Val._Iterator; + } + + _NODISCARD decltype(auto) operator*() const requires _Dereferenceable { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be dereferenced if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Val._Iterator; + } + + // clang-format off + _NODISCARD decltype(auto) operator->() const + requires indirectly_readable + && (_Has_member_arrow<_Iter> || is_reference_v> + || constructible_from, iter_reference_t<_Iter>>) { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be dereferenced if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (is_pointer_v<_Iter> || _Has_member_arrow<_Iter>) { + return (_Val._Iterator); // NB: () are necessary for decltype(auto) + } else if constexpr (is_reference_v>) { + auto&& _Tmp = *_Val._Iterator; + return _STD addressof(_Tmp); + } else { + return _Proxy{*_Val._Iterator}; + } + } + + common_iterator& operator++() { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be incremented if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + ++_Val._Iterator; + return *this; + } + + decltype(auto) operator++(int) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be incremented if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (forward_iterator<_Iter>) { + common_iterator _Tmp = *this; + ++_Val._Iterator; + return _Tmp; + } else { + return _Val._Iterator++; + } + } + + // clang-format off + template _OSe> + requires sentinel_for<_Se, _OIter> + _NODISCARD friend bool operator==(const common_iterator& _Left, const common_iterator<_OIter, _OSe>& _Right) { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Left._Val._Contains != _Variantish_state::_Nothing && _Right._Val._Contains != _Variantish_state::_Nothing, + "common_iterators can only be compared if both hold a value"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + if (_Left._Val._Contains == _Variantish_state::_Holds_iter) { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + if constexpr (equality_comparable_with<_Iter, _OIter>) { + return _Left._Val._Iterator == _Right._Val._Iterator; + } else { + return true; + } + } else { + return _Left._Val._Iterator == _Right._Val._Sentinel; + } + } else { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + return _Left._Val._Sentinel == _Right._Val._Iterator; + } else { + return true; + } + } + } + + // clang-format off + template _OIter, sized_sentinel_for<_Iter> _OSe> + requires sized_sentinel_for<_Se, _OIter> + _NODISCARD friend iter_difference_t<_OIter> operator-( + const common_iterator& _Left, const common_iterator<_OIter, _OSe>& _Right) { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Left._Val._Contains != _Variantish_state::_Nothing && _Right._Val._Contains != _Variantish_state::_Nothing, + "Cannot take difference of valueless common_iterators"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + if (_Left._Val._Contains == _Variantish_state::_Holds_iter) { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + return _Left._Val._Iterator - _Right._Val._Iterator; + } else { + return _Left._Val._Iterator - _Right._Val._Sentinel; + } + } else { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + return _Left._Val._Sentinel - _Right._Val._Iterator; + } else { + return 0; + } + } + } + + _NODISCARD friend iter_rvalue_reference_t<_Iter> iter_move(const common_iterator& _Right) noexcept( + noexcept(_RANGES iter_move(_Right._Val._Iterator))) requires input_iterator<_Iter> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Right._Val._Contains == _Variantish_state::_Holds_iter, + "can only iter_move from common_iterator if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_move(_Right._Val._Iterator); + } + + template _OIter, class _OSe> + friend void iter_swap(const common_iterator& _Left, const common_iterator<_OIter, _OSe>& _Right) noexcept( + noexcept(_RANGES iter_swap(_Left._Val._Iterator, _Right._Val._Iterator))) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Left._Val._Contains == _Variantish_state::_Holds_iter + && _Right._Val._Contains == _Variantish_state::_Holds_iter, + "can only iter_swap common_iterators if both hold iterators"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_swap(_Left._Val._Iterator, _Right._Val._Iterator); + } + +private: + // clang-format off + template _OSe> + requires (!same_as<_OIter, _OSe> && copyable<_OIter>) + friend class common_iterator; + // clang-format on + + _Variantish<_Iter, _Se> _Val; +}; + +template +struct incrementable_traits> { + using difference_type = iter_difference_t<_Iter>; +}; + +template +struct _Common_iterator_pointer_type { + using pointer = void; +}; + +template <_Has_member_arrow _Iter> +struct _Common_iterator_pointer_type<_Iter> { + using pointer = decltype(_STD declval<_Iter&>().operator->()); +}; + +template +struct iterator_traits> { + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using iterator_category = + conditional_t, forward_iterator_tag>, forward_iterator_tag, input_iterator_tag>; + using value_type = iter_value_t<_Iter>; + using difference_type = iter_difference_t<_Iter>; + using pointer = typename _Common_iterator_pointer_type<_Iter>::pointer; + using reference = iter_reference_t<_Iter>; +}; + // CLASS TEMPLATE counted_iterator template class counted_iterator { @@ -534,21 +1039,20 @@ public: requires convertible_to constexpr counted_iterator(const counted_iterator<_Other>& _Right) noexcept( is_nothrow_constructible_v<_Iter, const _Other&>) // strengthened - : _Current(_Right._Current), _Length(_Right._Length) {} + : _Current(_Right.base()), _Length(_Right.count()) {} template requires assignable_from<_Iter&, const _Other&> constexpr counted_iterator& operator=(const counted_iterator<_Other>& _Right) noexcept( is_nothrow_assignable_v<_Iter&, const _Other&>) /* strengthened */ { // clang-format on - _Current = _Right._Current; - _Length = _Right._Length; + _Current = _Right.base(); + _Length = _Right.count(); return *this; } // [counted.iter.access] - _NODISCARD constexpr _Iter base() const& noexcept(is_nothrow_copy_constructible_v<_Iter>) /* strengthened */ - requires copy_constructible<_Iter> { + _NODISCARD constexpr const _Iter& base() const& noexcept /* strengthened */ { // Per LWG-3391 return _Current; } @@ -720,8 +1224,8 @@ public: template _Other> friend constexpr void iter_swap(const counted_iterator& _Left, const counted_iterator<_Other>& _Right) noexcept( - noexcept(_RANGES iter_swap(_Left._Current, _Right._Current))) { - _RANGES iter_swap(_Left._Current, _Right._Current); + noexcept(_RANGES iter_swap(_Left._Current, _Right.base()))) { + _RANGES iter_swap(_Left._Current, _Right.base()); } template _Other> @@ -750,7 +1254,7 @@ public: template _Other> friend constexpr void _Verify_range(const counted_iterator& _Left, const counted_iterator<_Other>& _Right) { if constexpr (_Range_verifiable_v<_Iter, _Other>) { - _Verify_range(_Left._Current, _Right._Current); + _Verify_range(_Left._Current, _Right.base()); } #if _ITERATOR_DEBUG_LEVEL != 0 _Same_sequence(_Left, _Right); @@ -784,8 +1288,8 @@ public: requires _Wrapped_seekable_v<_Iter, const _Other&> constexpr void _Seek_to(const counted_iterator<_Other>& _It) { // clang-format on - _Current._Seek_to(_It._Current); - _Length = _It._Length; + _Current._Seek_to(_It.base()); + _Length = _It.count(); } // clang-format off @@ -793,14 +1297,11 @@ public: requires _Wrapped_seekable_v<_Iter, _Other> constexpr void _Seek_to(counted_iterator<_Other>&& _It) { // clang-format on - _Current._Seek_to(_STD move(_It)._Current); - _Length = _It._Length; + _Current._Seek_to(_STD move(_It).base()); + _Length = _It.count(); } private: - template - friend class counted_iterator; - _Iter _Current{}; iter_difference_t<_Iter> _Length = 0; }; @@ -814,6 +1315,17 @@ template struct iterator_traits> : iterator_traits<_Iter> { using pointer = void; }; + +template +struct pointer_traits> { // TRANSITION, address LWG-3408 and include this + using pointer = counted_iterator<_Iter>; + using element_type = remove_reference_t>; + using difference_type = iter_difference_t<_Iter>; + + _NODISCARD static constexpr element_type* to_address(const pointer _It) noexcept { + return _STD to_address(_It.base()); + } +}; #endif // __cpp_lib_concepts _STD_END diff --git a/stl/inc/latch b/stl/inc/latch new file mode 100644 index 00000000000..3743af76484 --- /dev/null +++ b/stl/inc/latch @@ -0,0 +1,101 @@ +// latch standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _LATCH_ +#define _LATCH_ +#include +#if _STL_COMPILER_PREPROCESSOR + +#ifdef _M_CEE_PURE +#error is not supported when compiling with /clr:pure. +#endif // _M_CEE_PURE + +#if !_HAS_CXX20 +#pragma message("The contents of are available only with C++20 or later.") +#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv + +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +class latch { +public: + _NODISCARD static constexpr ptrdiff_t(max)() noexcept { + return (1ULL << (sizeof(ptrdiff_t) * CHAR_BIT - 1)) - 1; + } + + constexpr explicit latch(const ptrdiff_t _Expected) noexcept /* strengthened */ : _Counter{_Expected} { + _STL_VERIFY(_Expected >= 0, "Precondition: expected >= 0 (N4861 [thread.latch.class]/4)"); + } + + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + void count_down(const ptrdiff_t _Update = 1) noexcept /* strengthened */ { + _STL_VERIFY(_Update >= 0, "Precondition: update >= 0 (N4861 [thread.latch.class]/7)"); + // TRANSITION, GH-1133: should be memory_order_release + const ptrdiff_t _Current = _Counter.fetch_sub(_Update) - _Update; + if (_Current == 0) { + _Counter.notify_all(); + } else { + _STL_VERIFY(_Current >= 0, "Precondition: update <= counter (N4861 [thread.latch.class]/7)"); + } + } + + _NODISCARD bool try_wait() const noexcept { + // TRANSITION, GH-1133: should be memory_order_acquire + return _Counter.load() == 0; + } + + void wait() const noexcept /* strengthened */ { + for (;;) { + // TRANSITION, GH-1133: should be memory_order_acquire + const ptrdiff_t _Current = _Counter.load(); + if (_Current == 0) { + return; + } else { + _STL_VERIFY(_Current > 0, "Invariant counter >= 0, possibly caused by preconditions violation " + "(N4861 [thread.latch.class]/7)"); + } + _Counter.wait(_Current, memory_order_relaxed); + } + } + + void arrive_and_wait(const ptrdiff_t _Update = 1) noexcept /* strengthened */ { + _STL_VERIFY(_Update >= 0, "Precondition: update >= 0 (N4861 [thread.latch.class]/7)"); + // TRANSITION, GH-1133: should be memory_order_acq_rel + const ptrdiff_t _Current = _Counter.fetch_sub(_Update) - _Update; + if (_Current == 0) { + _Counter.notify_all(); + } else { + _STL_VERIFY(_Current > 0, "Precondition: update <= counter (N4861 [thread.latch.class]/7)"); + _Counter.wait(_Current, memory_order_relaxed); + wait(); + } + } + +private: + atomic _Counter; +}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // ^^^ _HAS_CXX20 ^^^ + +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _LATCH_ diff --git a/stl/inc/memory b/stl/inc/memory index 69eed6da175..42ad7303c0e 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -233,7 +233,7 @@ _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, if (0 < _Count) { auto _UFirst = _Get_unwrapped_n(_First, _Count); if constexpr (_Fill_memset_is_safe<_Unwrapped_n_t, _Tval>) { - _CSTD memset(_UFirst, static_cast(_Val), _Count); + _Fill_memset(_UFirst, _Val, _Count); _UFirst += _Count; } else { _Uninitialized_backout<_Unwrapped_n_t> _Backout{_UFirst}; @@ -266,7 +266,7 @@ template _NoThrowFwdIt _Uninitialized_fill_n_unchecked1( const _NoThrowFwdIt _First, const _Diff _Count, const _Tval& _Val, true_type) { // copy _Count copies of _Val to raw _First, memset optimization - _CSTD memset(_First, static_cast(_Val), _Count); + _Fill_memset(_First, _Val, _Count); return _First + _Count; } @@ -1752,7 +1752,7 @@ void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, } _Guard._Target = nullptr; } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty>) { - _CSTD memset(_Out, static_cast(_Val), _Size); + _Fill_memset(_Out, _Val, _Size); } else { _Uninitialized_rev_destroying_backout _Backout{_Out}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { @@ -2075,7 +2075,7 @@ void _Uninitialized_fill_multidimensional_n_al(_Ty* const _Out, const size_t _Si } _Guard._Target = nullptr; } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { - _CSTD memset(_Out, static_cast(_Val), _Size); + _Fill_memset(_Out, _Val, _Size); } else { _Uninitialized_rev_destroying_backout_al _Backout{_Out, _Al}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { diff --git a/stl/inc/ranges b/stl/inc/ranges index 5964866450e..0abfedeb742 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -12,6 +12,9 @@ #pragma message("The contents of are available only with C++20 concepts support.") #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv #include +#include +#include + #if 1 // TRANSITION, VSO-1174090 #include #endif // TRANSITION, VSO-1174090 @@ -29,14 +32,207 @@ namespace ranges { // clang-format off // CONCEPT ranges::viewable_range - template + template // Per proposed resolution of LWG-3481 concept viewable_range = range<_Rng> - && (borrowed_range<_Rng> || view>); + && ((view> && constructible_from, _Rng>) + || (!view> && borrowed_range<_Rng>)); + + template + concept _Simple_view = view<_Rng> && range + && same_as, iterator_t> + && same_as, sentinel_t>; template concept _Copy_constructible_object = copy_constructible<_Ty> && is_object_v<_Ty>; + + template + concept _Has_arrow = input_iterator<_It> + && (is_pointer_v<_It> || _Has_member_arrow<_It&>); // clang-format on + template + using _Maybe_const = conditional_t<_IsConst, const _Ty, _Ty>; + + template + using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; + + namespace _Pipe { + // clang-format off + template + concept _Can_pipe = requires(_Left&& __l, _Right&& __r) { + static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l)); + }; + + template + concept _Can_compose = constructible_from, _Left> + && constructible_from, _Right>; + // clang-format on + + template + struct _Pipeline; + + template + struct _Base { + // clang-format off + template + requires _Can_compose<_Derived, _Other> + constexpr auto operator|(_Base<_Other>&& __r) && noexcept( + noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)})) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); + return _Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)}; + } + + // clang-format off + template + requires _Can_compose<_Derived, const _Other&> + constexpr auto operator|(const _Base<_Other>& __r) && noexcept(noexcept( + _Pipeline{static_cast<_Derived&&>(*this), static_cast(__r)})) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); + return _Pipeline{static_cast<_Derived&&>(*this), static_cast(__r)}; + } + + // clang-format off + template + requires _Can_compose + constexpr auto operator|(_Base<_Other>&& __r) const& noexcept( + noexcept(_Pipeline{static_cast(*this), static_cast<_Other&&>(__r)})) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); + return _Pipeline{static_cast(*this), static_cast<_Other&&>(__r)}; + } + + // clang-format off + template + requires _Can_compose + constexpr auto operator|(const _Base<_Other>& __r) const& noexcept(noexcept( + _Pipeline{static_cast(*this), static_cast(__r)})) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); + _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); + return _Pipeline{static_cast(*this), static_cast(__r)}; + } + + template <_Can_pipe _Left> + friend constexpr auto operator|(_Left&& __l, const _Base& __r) noexcept( + noexcept(static_cast(__r)(_STD forward<_Left>(__l)))) { + return static_cast(__r)(_STD forward<_Left>(__l)); + } + + template <_Can_pipe<_Derived> _Left> + friend constexpr auto operator|(_Left&& __l, _Base&& __r) noexcept( + noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)))) { + return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)); + } + }; + + template + struct _Pipeline : _Base<_Pipeline<_Left, _Right>> { + /* [[no_unique_address]] */ _Left __l; + /* [[no_unique_address]] */ _Right __r; + + template + constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( + is_nothrow_convertible_v<_Ty1, _Left>&& is_nothrow_convertible_v<_Ty2, _Right>) + : __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {} + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept( + noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { + __r(__l(static_cast<_Ty&&>(_Val))); + } + { return __r(__l(_STD forward<_Ty>(_Val))); } + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) const + noexcept(noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { + __r(__l(static_cast<_Ty&&>(_Val))); + } + { return __r(__l(_STD forward<_Ty>(_Val))); } + }; + + template + _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; + } // namespace _Pipe + + template + class _Cached_position : public view_interface<_Derived> { + static_assert(_Always_false<_Rng>, "A range must be at least forward for position caching to be worthwhile."); + }; + + template + class _Cached_position<_Rng, _Derived> : public view_interface<_Derived> { + private: + using _It = iterator_t<_Rng>; + + /* [[no_unique_address]] */ _It _Pos{}; + bool _Cached = false; + + protected: + _Cached_position() = default; + ~_Cached_position() = default; + + // a copied iterator doesn't point into a copied range, so cache values must not propagate via copy + constexpr _Cached_position(const _Cached_position&) noexcept(is_nothrow_default_constructible_v<_It>) {} + constexpr _Cached_position& operator=(const _Cached_position&) noexcept( + is_nothrow_default_constructible_v<_It>) { + _Pos = _It{}; + _Cached = false; + return *this; + } + + _NODISCARD constexpr bool _Has_cache() const noexcept { // Is there a cached position? + return _Cached; + } + + _NODISCARD constexpr _It _Get_cache(_Rng&) const noexcept(is_nothrow_copy_constructible_v<_It>) { + _STL_INTERNAL_CHECK(_Cached); + return _Pos; + } + + _NODISCARD constexpr void _Set_cache(_Rng&, _It _Iter) noexcept(is_nothrow_move_assignable_v<_It>) { + _Pos = _STD move(_Iter); + _Cached = true; + } + }; + + template + class _Cached_position<_Rng, _Derived> : public view_interface<_Derived> { + private: + using _It = iterator_t<_Rng>; + + range_difference_t<_Rng> _Off = -1; + + protected: + _Cached_position() = default; + ~_Cached_position() = default; + + // Offsets are oblivious to copying, so cache values _do_ propagate via copying. + _Cached_position(const _Cached_position&) = default; + _Cached_position& operator=(const _Cached_position&) = default; + + _NODISCARD constexpr bool _Has_cache() const noexcept { // Is there a cached position? + return _Off >= range_difference_t<_Rng>{0}; + } + + _NODISCARD constexpr _It _Get_cache(_Rng& _Range) const noexcept(noexcept(_RANGES begin(_Range) + _Off)) { + _STL_INTERNAL_CHECK(_Has_cache()); + return _RANGES begin(_Range) + _Off; + } + + _NODISCARD constexpr void _Set_cache(_Rng& _Range, const _It& _Iter) noexcept( + noexcept(_Off = _Iter - _RANGES begin(_Range))) { + _Off = _Iter - _RANGES begin(_Range); + } + }; + + template + using _Cached_position_t = conditional_t<_Enable, _Cached_position<_Rng, _Derived>, view_interface<_Derived>>; + // CLASS TEMPLATE ranges::_Semiregular_box #if 0 // TRANSITION, VSO-1174090 template <_Copy_constructible_object _Ty> @@ -417,10 +613,10 @@ namespace ranges { // clang-format off template _NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept( - noexcept(single_view{static_cast<_Ty&&>(_Val)})) requires requires { + noexcept(single_view{_STD forward<_Ty>(_Val)})) requires requires { single_view{static_cast<_Ty&&>(_Val)}; } { - return single_view{static_cast<_Ty&&>(_Val)}; + return single_view{_STD forward<_Ty>(_Val)}; } // clang-format on }; @@ -486,6 +682,1373 @@ namespace ranges { template inline constexpr bool enable_borrowed_range> = true; + + namespace views { + // VARIABLE views::all + template + concept _Can_ref_view = requires(_Rng&& __r) { + ref_view{static_cast<_Rng&&>(__r)}; + }; + + template + concept _Can_subrange = requires(_Rng&& __r) { + subrange{static_cast<_Rng&&>(__r)}; + }; + + class _All_fn : public _Pipe::_Base<_All_fn> { + private: + enum class _St { _None, _View, _Ref, _Subrange }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + if constexpr (view>) { + if constexpr (constructible_from, _Rng>) { + return {_St::_View, is_nothrow_constructible_v, _Rng>}; + } + } else if constexpr (_Can_ref_view<_Rng>) { + return {_St::_Ref, noexcept(ref_view{_STD declval<_Rng>()})}; + } else if constexpr (_Can_subrange<_Rng>) { + return {_St::_Subrange, noexcept(subrange{_STD declval<_Rng>()})}; + } + + return {_St::_None}; + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + + public: + // clang-format off + template + requires (_Choice<_Rng>._Strategy != _St::_None) + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(_Choice<_Rng>._No_throw) { + // clang-format on + if constexpr (_Choice<_Rng>._Strategy == _St::_View) { + return _STD forward<_Rng>(_Range); + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Ref) { + return ref_view{_STD forward<_Rng>(_Range)}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Subrange) { + return subrange{_STD forward<_Rng>(_Range)}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } + } + }; + + inline constexpr _All_fn all; + + // ALIAS TEMPLATE views::all_t + template + using all_t = decltype(all(_STD declval<_Rng>())); + } // namespace views + + // CLASS TEMPLATE ranges::filter_view + // clang-format off + template > _Pr> + requires view<_Vw> && is_object_v<_Pr> + class filter_view : public _Cached_position_t, _Vw, filter_view<_Vw, _Pr>> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Semiregular_box<_Pr> _Pred{}; + + template // TRANSITION, LWG-3289 + struct _Iterator_base {}; + // clang-format off + template + requires _Has_member_iterator_category<_Traits> + struct _Iterator_base<_Traits> { + // clang-format on + using iterator_category = + conditional_t, + bidirectional_iterator_tag, + conditional_t, + forward_iterator_tag, input_iterator_tag>>; + }; + + class _Iterator : public _Iterator_base>> { + private: + /* [[no_unique_address]] */ iterator_t<_Vw> _Current{}; + filter_view* _Parent{}; + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Check_dereference() const noexcept { + _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized filter_view iterator"); + _STL_VERIFY(_Current != _RANGES end(_Parent->_Range), "cannot dereference end filter_view iterator"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + public: + using iterator_concept = conditional_t, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>; + using value_type = range_value_t<_Vw>; + using difference_type = range_difference_t<_Vw>; + + _Iterator() = default; + constexpr _Iterator(filter_view& _Parent_, iterator_t<_Vw> _Current_) noexcept( + is_nothrow_move_constructible_v>) // strengthened + : _Current(_STD move(_Current_)), _Parent{_STD addressof(_Parent_)} { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); + if constexpr (forward_range<_Vw>) { + _Adl_verify_range(_RANGES begin(_Parent_._Range), _Current); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + } + + _NODISCARD constexpr iterator_t<_Vw> base() const& noexcept(is_nothrow_copy_constructible_v< + iterator_t<_Vw>>) /* strengthened */ requires copyable> { + return _Current; + } + _NODISCARD constexpr iterator_t<_Vw> base() && noexcept( + is_nothrow_move_constructible_v>) /* strengthened */ { + return _STD move(_Current); + } + + _NODISCARD constexpr range_reference_t<_Vw> operator*() const + noexcept(noexcept(*_Current)) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Current; + } + + // clang-format off + _NODISCARD constexpr iterator_t<_Vw> operator->() const + noexcept(is_nothrow_copy_constructible_v>) /* strengthened */ + requires _Has_arrow> && copyable> { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Current; + } + + constexpr _Iterator& operator++() { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Parent != nullptr, "cannot increment value-initialized filter_view iterator"); + _STL_VERIFY(_Current != _RANGES end(_Parent->_Range), "cannot increment filter_view iterator past end"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Current = + _RANGES find_if(_STD move(++_Current), _RANGES end(_Parent->_Range), _STD ref(*_Parent->_Pred)); + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Vw>) { + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + constexpr _Iterator& operator--() requires bidirectional_range<_Vw> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Parent != nullptr, "cannot decrement value-initialized filter_view iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + do { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Current != _RANGES begin(_Parent->_Range), + "cannot decrement filter_view iterator before begin"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + --_Current; + } while (!_STD invoke(*_Parent->_Pred, *_Current)); + return *this; + } + + constexpr _Iterator operator--(int) requires bidirectional_range<_Vw> { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + _NODISCARD friend constexpr bool operator==( + const _Iterator& _Left, const _Iterator& _Right) requires equality_comparable> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Left._Parent == _Right._Parent, "cannot compare incompatible filter_view iterators for equality"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current == _Right._Current; + } + + _NODISCARD friend constexpr range_rvalue_reference_t<_Vw> iter_move(const _Iterator& _It) noexcept( + noexcept(_RANGES iter_move(_It._Current))) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_move(_It._Current); + } + + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept(noexcept( + _RANGES iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Check_dereference(); + _Right._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_swap(_Left._Current, _Right._Current); + } + + _NODISCARD constexpr bool _Equal(const sentinel_t<_Vw>& _Last) const { + return _Current == _Last; + } + }; + + class _Sentinel { + private: + /* [[no_unique_address]] */ sentinel_t<_Vw> _Last{}; + + public: + _Sentinel() = default; + constexpr explicit _Sentinel(filter_view& _Parent) noexcept( + noexcept(_RANGES end(_Parent._Range)) + && is_nothrow_move_constructible_v>) // strengthened + : _Last(_RANGES end(_Parent._Range)) {} + + _NODISCARD constexpr sentinel_t<_Vw> base() const + noexcept(is_nothrow_copy_constructible_v>) /* strengthened */ { + return _Last; + } + + _NODISCARD friend constexpr bool operator==(const _Iterator& _It, const _Sentinel& _Se) { + return _It._Equal(_Se._Last); + } + }; + + public: + filter_view() = default; + constexpr filter_view(_Vw _Range_, _Pr _Pred_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Pr>) // strengthened + : _Range(_STD move(_Range_)), _Pred{in_place, _STD move(_Pred_)} {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr const _Pr& pred() const noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Pred, "value-initialized filter_view has no predicate"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return *_Pred; + } + + _NODISCARD constexpr _Iterator begin() { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY( + _Pred, "N4861 [range.filter.view]/3 forbids calling begin on a filter_view that holds no predicate"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (forward_range<_Vw>) { + if (this->_Has_cache()) { + return _Iterator{*this, this->_Get_cache(_Range)}; + } + } + + auto _First = _RANGES find_if(_Range, _STD ref(*_Pred)); + if constexpr (forward_range<_Vw>) { + this->_Set_cache(_Range, _First); + } + + return _Iterator{*this, _STD move(_First)}; + } + + _NODISCARD constexpr auto end() { + if constexpr (common_range<_Vw>) { + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{*this}; + } + } + }; + + template + filter_view(_Rng&&, _Pr) -> filter_view, _Pr>; + + namespace views { + // VARIABLE views::filter + class _Filter_fn { + private: + template + struct _Partial : _Pipe::_Base<_Partial<_Pr>> { + /* [[no_unique_address]] */ _Semiregular_box<_Pr> _Pred; + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const + noexcept(noexcept(filter_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)})) requires requires { + filter_view{static_cast<_Rng&&>(_Range), _STD move(*_Pred)}; + } + { + // clang-format on + return filter_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)}; + } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pr _Pred) const noexcept(noexcept( + filter_view{_STD forward<_Rng>(_Range), _STD move(_Pred)})) requires requires { + filter_view{static_cast<_Rng&&>(_Range), _STD move(_Pred)}; + } { + // clang-format on + return filter_view{_STD forward<_Rng>(_Range), _STD move(_Pred)}; + } + + template <_Copy_constructible_object _Pr> + _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { + // clang-format on + return _Partial<_Pr>{._Pred = {in_place, _STD move(_Pred)}}; + } + }; + + inline constexpr _Filter_fn filter; + } // namespace views + + // CLASS TEMPLATE ranges::transform_view + // clang-format off + template // TRANSITION, LLVM-47414 + concept _Can_const_transform = range && regular_invocable>; + +#if _ITERATOR_DEBUG_LEVEL == 0 +#define _NOEXCEPT_IDL0(...) noexcept(__VA_ARGS__) +#else +#define _NOEXCEPT_IDL0(...) +#endif // _ITERATOR_DEBUG_LEVEL == 0 + + template + requires view<_Vw> && is_object_v<_Fn> + && regular_invocable<_Fn&, range_reference_t<_Vw>> + && _Can_reference>> + class transform_view : public view_interface> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Semiregular_box<_Fn> _Fun{}; + + template + class _Sentinel; + + template // TRANSITION, LWG-3289 + struct _Iterator_base {}; + // clang-format off + template + requires _Has_member_iterator_category<_Traits> + struct _Iterator_base<_Traits, _Base> { + // clang-format on + using iterator_category = + conditional_t>>, + conditional_t, + random_access_iterator_tag, typename _Traits::iterator_category>, + input_iterator_tag>; + }; + + template + class _Iterator : public _Iterator_base>, _Maybe_const<_Const, _Vw>> { + private: + template + friend class _Iterator; + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, transform_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + iterator_t<_Base> _Current{}; + _Parent_t* _Parent{}; + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Check_dereference() const { + _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized transform_view iterator"); + _STL_VERIFY(_Current != _RANGES end(_Parent->_Range), "cannot dereference end transform_view iterator"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Same_range(const _Iterator& _Right) const noexcept { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible transform_view iterators"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + public: + using iterator_concept = conditional_t, random_access_iterator_tag, + conditional_t, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>; + using value_type = remove_cvref_t>>; + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr _Iterator(_Parent_t& _Parent_, iterator_t<_Base> _Current_) noexcept( + noexcept(is_nothrow_move_constructible_v>)) // strengthened + : _Current{_STD move(_Current_)}, _Parent{_STD addressof(_Parent_)} { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); + if constexpr (forward_range<_Base>) { + _Adl_verify_range(_RANGES begin(_Parent_._Range), _Current); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + } + + // clang-format off + constexpr _Iterator(_Iterator _It) + noexcept(is_nothrow_constructible_v, iterator_t<_Vw>>) // strengthened + requires _Const && convertible_to, iterator_t<_Base>> + : _Current{_STD move(_It._Current)}, _Parent{_It._Parent} {} + // clang-format on + + _NODISCARD constexpr iterator_t<_Base> base() const& noexcept(is_nothrow_copy_constructible_v< + iterator_t<_Base>>) /* strengthened */ requires copyable> { + return _Current; + } + _NODISCARD constexpr iterator_t<_Base> base() && noexcept( + is_nothrow_move_constructible_v>) /* strengthened */ { + return _STD move(_Current); + } + + _NODISCARD constexpr decltype(auto) operator*() const + _NOEXCEPT_IDL0(noexcept(_STD invoke(*_Parent->_Fun, *_Current))) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); + _STL_VERIFY( + _Parent->_Fun, "Cannot dereference iterator into transform_view with no transformation function"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _STD invoke(*_Parent->_Fun, *_Current); + } + + constexpr _Iterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Parent != nullptr, "Cannot increment value-initialized transform_view iterator"); + _STL_VERIFY( + _Current != _RANGES end(_Parent->_Range), "Cannot increment transform_view iterator past end"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + ++_Current; + return *this; + } + + constexpr decltype(auto) operator++(int) noexcept( + noexcept(++_Current) + && (!forward_range<_Base> || is_nothrow_copy_constructible_v>) ) /* strengthened */ { + if constexpr (forward_range<_Base>) { + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + constexpr _Iterator& operator--() noexcept( + noexcept(--_Current)) /* strengthened */ requires bidirectional_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Parent != nullptr, "Cannot decrement value-initialized transform_view iterator"); + if constexpr (forward_range<_Vw>) { + _STL_VERIFY(_Current != _RANGES begin(_Parent->_Range), + "Cannot decrement transform_view iterator before begin"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + --_Current; + return *this; + } + constexpr _Iterator operator--(int) noexcept( + noexcept(--_Current) && is_nothrow_copy_constructible_v>) /* strengthened */ + requires bidirectional_range<_Base> { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr void _Verify_offset(const difference_type _Off) const requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL == 0 + (void) _Off; +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 / _ITERATOR_DEBUG_LEVEL != 0 vvv + _STL_VERIFY(_Off == 0 || _Parent, "cannot seek value-initialized transform_view iterator"); + + if constexpr (_Offset_verifiable_v>) { + _Current._Verify_offset(_Off); + } else { + if (_Off < 0) { + if constexpr (sized_sentinel_for, iterator_t<_Base>>) { + _STL_VERIFY(_Off >= _RANGES begin(_Parent->_Range) - _Current, + "cannot seek transform_view iterator before begin"); + } + } else if (_Off > 0) { + if constexpr (sized_sentinel_for, iterator_t<_Base>>) { + _STL_VERIFY(_Off <= _RANGES end(_Parent->_Range) - _Current, + "cannot seek transform_view iterator after end"); + } else if constexpr (sized_sentinel_for, + iterator_t<_Base>> && sized_range<_Base>) { + const auto _Size = _RANGES distance(_Parent->_Range); + _STL_VERIFY(_Off <= _Size - (_Current - _RANGES begin(_Parent->_Range)), + "cannot seek transform_view iterator after end"); + } + } + } +#endif // _ITERATOR_DEBUG_LEVEL == 0 + } + + constexpr _Iterator& operator+=(const difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_Current += _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Verify_offset(_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Current += _Off; + return *this; + } + constexpr _Iterator& operator-=(const difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_Current -= _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Verify_offset(-_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Current -= _Off; + return *this; + } + + _NODISCARD constexpr decltype(auto) operator[](const difference_type _Idx) const + _NOEXCEPT_IDL0(noexcept(_STD invoke(*_Parent->_Fun, _Current[_Idx]))) /* strengthened */ + requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Verify_offset(_Idx); + _STL_VERIFY( + _Parent->_Fun, "Cannot dereference iterator into transform_view with no transformation function"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _STD invoke(*_Parent->_Fun, _Current[_Idx]); + } + + _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current + == _Right._Current)) /* strengthened */ requires equality_comparable> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current == _Right._Current; + } + + _NODISCARD friend constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current < _Right._Current; + } + _NODISCARD friend constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { + return _Right < _Left; + } + _NODISCARD friend constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { + return !(_Right < _Left); + } + _NODISCARD friend constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { + return !(_Left < _Right); + } + // clang-format off + _NODISCARD friend constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current <=> _Right._Current)) /* strengthened */ + requires random_access_range<_Base> && three_way_comparable> { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current <=> _Right._Current; + } + + _NODISCARD friend constexpr _Iterator operator+(_Iterator _It, difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_It._Current += _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Verify_offset(_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It._Current += _Off; + return _It; + } + _NODISCARD friend constexpr _Iterator operator+(difference_type _Off, _Iterator _It) + _NOEXCEPT_IDL0(noexcept(_It._Current += _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Verify_offset(_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It._Current += _Off; + return _It; + } + + _NODISCARD friend constexpr _Iterator operator-(_Iterator _It, difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_It._Current -= _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Verify_offset(-_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It._Current -= _Off; + return _It; + } + + _NODISCARD friend constexpr difference_type operator-(const _Iterator& _Left, + const _Iterator& _Right) noexcept(noexcept(_Left._Current - _Right._Current)) /* strengthened */ + requires sized_sentinel_for, iterator_t<_Base>> { // constraints per LWG issue + // unnumbered as of 2020-09-03 +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current - _Right._Current; + } + + _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept(noexcept(*_It)) { + if constexpr (is_lvalue_reference_v) { + return _STD move(*_It); + } else { + return *_It; + } + } + + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) _NOEXCEPT_IDL0(noexcept( + ranges::iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Check_dereference(); + _Right._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _RANGES iter_swap(_Left._Current, _Right._Current); + } + }; + + template + class _Sentinel { + private: + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, transform_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + sentinel_t<_Base> _Last{}; + + _NODISCARD constexpr bool _Equal(const _Iterator<_Const>& _It) const + noexcept(noexcept(_It._Current == _Last)) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _It._Parent != nullptr, "cannot compare transform_view sentinel with value-initialized iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _It._Current == _Last; + } + + _NODISCARD constexpr range_difference_t<_Base> _Distance_to(const _Iterator<_Const>& _It) const + noexcept(noexcept(_It._Current - _Last)) { + _STL_INTERNAL_STATIC_ASSERT(sized_sentinel_for, iterator_t<_Base>>); +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _It._Parent != nullptr, "cannot compare transform_view sentinel with value-initialized iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _It._Current - _Last; + } + + public: + _Sentinel() = default; + constexpr explicit _Sentinel(sentinel_t<_Base> _Last_) noexcept( + is_nothrow_move_constructible_v>) // strengthened + : _Last(_STD move(_Last_)) {} + + // clang-format off + constexpr _Sentinel(_Sentinel _Se) + noexcept(is_nothrow_constructible_v, sentinel_t<_Vw>>) // strengthened + requires _Const && convertible_to, sentinel_t<_Base>> + : _Last(_STD move(_Se._Last)) {} + // clang-format on + + _NODISCARD constexpr sentinel_t<_Base> base() const + noexcept(is_nothrow_copy_constructible_v>) /* strengthened */ { + return _Last; + } + + _NODISCARD friend constexpr bool operator==(const _Iterator<_Const>& _Left, + const _Sentinel& _Right) noexcept(noexcept(_Right._Equal(_Left))) /* strengthened */ { + return _Right._Equal(_Left); + } + + _NODISCARD friend constexpr range_difference_t<_Base> + operator-(const _Iterator<_Const>& _Left, const _Sentinel& _Right) noexcept( + noexcept(_Right._Distance_to( + _Left))) /* strengthened */ requires sized_sentinel_for, iterator_t<_Base>> { + return _Right._Distance_to(_Left); + } + _NODISCARD friend constexpr range_difference_t<_Base> + operator-(const _Sentinel& _Left, const _Iterator<_Const>& _Right) noexcept(noexcept(_Left._Distance_to( + _Right))) /* strengthened */ requires sized_sentinel_for, iterator_t<_Base>> { + return -_Left._Distance_to(_Right); + } + }; + + public: + transform_view() = default; + constexpr transform_view(_Vw _Range_, _Fn _Fun_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Fn>) // strengthened + : _Range(_STD move(_Range_)), _Fun{in_place, _STD move(_Fun_)} {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr _Iterator begin() noexcept( + noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + return _Iterator{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( + _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ +#ifdef __clang__ // TRANSITION, LLVM-47414 + requires _Can_const_transform<_Vw, _Fn> +#else // ^^^ workaround / no workaround vvv + requires range && regular_invocable> +#endif // TRANSITION, LLVM-47414 + { + // clang-format on + return _Iterator{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr auto end() noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v) /* strengthened */ { + // clang-format on + if constexpr (common_range<_Vw>) { + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + // clang-format off + _NODISCARD constexpr auto end() const noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v) /* strengthened */ +#ifdef __clang__ // TRANSITION, LLVM-47414 + requires _Can_const_transform<_Vw, _Fn> +#else // ^^^ workaround / no workaround vvv + requires range && regular_invocable> +#endif // TRANSITION, LLVM-47414 + { + // clang-format on + if constexpr (common_range<_Vw>) { + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto size() noexcept( + noexcept(_RANGES size(_Range))) /* strengthened */ requires sized_range<_Vw> { + return _RANGES size(_Range); + } + _NODISCARD constexpr auto size() const + noexcept(noexcept(_RANGES size(_Range))) /* strengthened */ requires sized_range { + return _RANGES size(_Range); + } + }; + +#undef _NOEXCEPT_IDL0 + + template + transform_view(_Rng&&, _Fn) -> transform_view, _Fn>; + + namespace views { + // VARIABLE views::transform + class _Transform_fn { + private: + template + struct _Partial : _Pipe::_Base<_Partial<_Fn>> { + /* [[no_unique_address]] */ _Semiregular_box<_Fn> _Fun; + + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( + transform_view{_STD forward<_Rng>(_Range), _STD move(*_Fun)})) requires requires { + transform_view{static_cast<_Rng&&>(_Range), _STD move(*_Fun)}; + } { + // clang-format on + return transform_view{_STD forward<_Rng>(_Range), _STD move(*_Fun)}; + } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Fun) const noexcept(noexcept( + transform_view{_STD forward<_Rng>(_Range), _STD move(_Fun)})) requires requires { + transform_view{static_cast<_Rng&&>(_Range), _STD move(_Fun)}; + } { + // clang-format on + return transform_view{_STD forward<_Rng>(_Range), _STD move(_Fun)}; + } + + template <_Copy_constructible_object _Fn> + _NODISCARD constexpr auto operator()(_Fn _Fun) const noexcept(is_nothrow_move_constructible_v<_Fn>) { + return _Partial<_Fn>{._Fun = {in_place, _STD move(_Fun)}}; + } + }; + + inline constexpr _Transform_fn transform; + } // namespace views + + // CLASS TEMPLATE ranges::take_view + template + class take_view : public view_interface> { + private: + /* [[no_unique_address]] */ _Vw _Range{}; + range_difference_t<_Vw> _Count = 0; + + template + class _Sentinel { + private: + template + friend class _Sentinel; + + using _Base_Ty = _Maybe_const<_Const, _Vw>; + using _Base_sentinel = _Maybe_wrapped<_Wrapped, sentinel_t<_Base_Ty>>; + using _Counted_Iter = counted_iterator<_Maybe_wrapped<_Wrapped, iterator_t<_Base_Ty>>>; + + _Base_sentinel _Last{}; + + public: + _Sentinel() = default; + + constexpr explicit _Sentinel(_Base_sentinel _Last_) noexcept( + is_nothrow_move_constructible_v<_Base_sentinel>) // strengthened + : _Last(_STD move(_Last_)) {} + + // clang-format off + constexpr _Sentinel(_Sentinel _That) noexcept( + is_nothrow_constructible_v<_Base_sentinel, _Maybe_wrapped<_Wrapped, sentinel_t<_Vw>>>) // strengthened + requires _Const && convertible_to<_Maybe_wrapped<_Wrapped, sentinel_t<_Vw>>, _Base_sentinel> + : _Last(_STD move(_That._Last)) {} + // clang-format on + + _NODISCARD constexpr _Base_sentinel base() const + noexcept(is_nothrow_copy_constructible_v<_Base_sentinel>) /* strengthened */ { + return _Last; + } + + _NODISCARD friend constexpr bool operator==(const _Counted_Iter& _Left, const _Sentinel& _Right) { + return _Left.count() == 0 || _Left.base() == _Right._Last; + } + + using _Prevent_inheriting_unwrap = _Sentinel; + + // clang-format off + _NODISCARD constexpr auto _Unwrapped() const& + requires _Wrapped && _Unwrappable_v&> { + // clang-format on + return _Sentinel<_Const, false>{_Get_unwrapped(_Last)}; + } + // clang-format off + _NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v> { + // clang-format on + return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))}; + } + + static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v>; + + constexpr void _Seek_to(const _Sentinel<_Const, false>& _That) requires _Wrapped { + _Seek_wrapped(_Last, _That._Last); + } + constexpr void _Seek_to(_Sentinel<_Const, false>&& _That) requires _Wrapped { + _Seek_wrapped(_Last, _STD move(_That._Last)); + } + }; + + public: + take_view() = default; + + constexpr take_view(_Vw _Range_, const range_difference_t<_Vw> _Count_) noexcept( + is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)), _Count{_Count_} {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + // clang-format off + _NODISCARD constexpr auto begin() requires (!_Simple_view<_Vw>) { + // clang-format on + if constexpr (sized_range<_Vw>) { + if constexpr (random_access_range<_Vw>) { + return _RANGES begin(_Range); + } else { + const auto _Size = size(); + return counted_iterator{_RANGES begin(_Range), _Size}; + } + } else { + return counted_iterator{_RANGES begin(_Range), _Count}; + } + } + + _NODISCARD constexpr auto begin() const requires range { + if constexpr (sized_range) { + if constexpr (random_access_range) { + return _RANGES begin(_Range); + } else { + const auto _Size = size(); + return counted_iterator{_RANGES begin(_Range), _Size}; + } + } else { + return counted_iterator{_RANGES begin(_Range), _Count}; + } + } + + // clang-format off + _NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) { + // clang-format on + if constexpr (sized_range<_Vw>) { + if constexpr (random_access_range<_Vw>) { + return _RANGES begin(_Range) + static_cast>(size()); + } else { + return default_sentinel; + } + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto end() const requires range { + if constexpr (sized_range) { + if constexpr (random_access_range) { + return _RANGES begin(_Range) + static_cast>(size()); + } else { + return default_sentinel; + } + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto size() requires sized_range<_Vw> { + const auto _Length = _RANGES size(_Range); + return (_STD min)(_Length, static_cast(_Count)); + } + + _NODISCARD constexpr auto size() const requires sized_range { + const auto _Length = _RANGES size(_Range); + return (_STD min)(_Length, static_cast(_Count)); + } + }; + + template // Per P/R of LWG-3447 + take_view(_Rng&&, range_difference_t<_Rng>)->take_view>; + + namespace views { + // VARIABLE views::take + template + static constexpr bool _Is_dynamic_span = false; + template + static constexpr bool _Is_dynamic_span> = true; + + template + static constexpr bool _Is_subrange = false; + template + static constexpr bool _Is_subrange> = true; + + template + concept _Reconstructible_range = random_access_range<_Rng> && sized_range<_Rng> + && (_Is_dynamic_span> + || _Is_specialization_v, basic_string_view> + // || _Is_specialization_v, iota_view> // TRANSITION, iota_view + || _Is_subrange>); + + class _Take_fn { + private: + enum class _St { _Empty, _Preserve, _Take_view }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + using _Ty = remove_cvref_t<_Rng>; + + if constexpr (_Is_specialization_v<_Ty, empty_view>) { + return {_St::_Empty, true}; + } else if constexpr (_Reconstructible_range<_Rng>) { + return {_St::_Preserve, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()), + _RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>())})}; + } else { + return {_St::_Take_view, noexcept(take_view{_STD declval<_Rng>(), range_difference_t<_Rng>{0}})}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + + template <_Integer_like _Ty> + struct _Partial : _Pipe::_Base<_Partial<_Ty>> { + _Ty _Length; + + // clang-format off + template + requires convertible_to<_Ty&, range_difference_t<_Rng>> + _NODISCARD constexpr auto operator()(_Rng&& _Range) const + noexcept(noexcept(_Take_fn{}(_STD forward<_Rng>(_Range), _Length))) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); + return _Take_fn{}(_STD forward<_Rng>(_Range), _Length); + } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const noexcept( + _Choice<_Rng>._No_throw) { + // clang-format on + if constexpr (_Choice<_Rng>._Strategy == _St::_Empty) { + // it's an empty_view: return another empty view + return remove_cvref_t<_Rng>{}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Preserve) { + // it's a "reconstructible range"; return the same kind of range with a restricted extent + _Count = (_STD min)(_RANGES distance(_Range), _Count); + const auto _First = _RANGES begin(_Range); + return remove_cvref_t<_Rng>{_First, _First + _Count}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Take_view) { + return take_view{_STD forward<_Rng>(_Range), _Count}; + } + } + + template <_Integer_like _Ty> + _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { + return _Partial<_Ty>{._Length = _Length}; + } + }; + + inline constexpr _Take_fn take; + } // namespace views + + // CLASS TEMPLATE ranges::drop_view + template + class drop_view : public _Cached_position_t && !(random_access_range<_Vw> && sized_range<_Vw>), + _Vw, drop_view<_Vw>> { + private: + /* [[no_unique_address]] */ _Vw _Range{}; + range_difference_t<_Vw> _Count = 0; + + public: + drop_view() = default; + + constexpr drop_view(_Vw _Range_, const range_difference_t<_Vw> _Count_) noexcept( + is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)), _Count{_Count_} { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Count_ >= 0, "Numer of elements to drop must be non-negative (N4861 [range.drop.view]/1"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + // clang-format off + _NODISCARD constexpr auto begin() // constraints per proposed resolution of LWG-3482 + requires (!(_Simple_view<_Vw> && random_access_range && sized_range)) { + // clang-format on + if constexpr (sized_range<_Vw> && random_access_range<_Vw>) { + const auto _Offset = (_STD min)(_RANGES distance(_Range), _Count); + return _RANGES begin(_Range) + _Offset; + } else { + if constexpr (forward_range<_Vw>) { + if (this->_Has_cache()) { + return this->_Get_cache(_Range); + } + } + + iterator_t<_Vw> _Result; + if constexpr (sized_range<_Vw>) { + auto _Offset = _RANGES distance(_Range); + if constexpr (bidirectional_range<_Vw> && common_range<_Vw>) { + if (_Count >= _Offset / 2) { + _Result = _RANGES end(_Range); + while (_Offset > _Count) { + --_Offset; + --_Result; + } + + this->_Set_cache(_Range, _Result); + return _Result; + } + } + + if (_Offset > _Count) { + _Offset = _Count; + } + + _Result = _RANGES next(_RANGES begin(_Range), _Offset); + } else { + _Result = _RANGES next(_RANGES begin(_Range), _Count, _RANGES end(_Range)); + } + + if constexpr (forward_range<_Vw>) { + this->_Set_cache(_Range, _Result); + } + return _Result; + } + } + + // clang-format off + _NODISCARD constexpr auto begin() const // constraints per proposed resolution of LWG-3482 + requires random_access_range && sized_range { + // clang-format on + const auto _Offset = (_STD min)(_RANGES distance(_Range), _Count); + return _RANGES begin(_Range) + _Offset; + } + + // clang-format off + _NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) { + // clang-format on + return _RANGES end(_Range); + } + + _NODISCARD constexpr auto end() const requires range { + return _RANGES end(_Range); + } + + _NODISCARD constexpr auto size() requires sized_range<_Vw> { + const auto _Size = _RANGES size(_Range); + const auto _Count_as_size = static_cast>(_Count); + if (_Size < _Count_as_size) { + return range_size_t<_Vw>{0}; + } else { + return static_cast>(_Size - _Count_as_size); + } + } + + _NODISCARD constexpr auto size() const requires sized_range { + const auto _Size = _RANGES size(_Range); + const auto _Count_as_size = static_cast>(_Count); + if (_Size < _Count_as_size) { + return range_size_t<_Vw>{0}; + } else { + return static_cast>(_Size - _Count_as_size); + } + } + }; + + template + drop_view(_Rng&&, range_difference_t<_Rng>) -> drop_view>; + + namespace views { + // VARIABLE views::drop + class _Drop_fn { + private: + enum class _St { _Empty, _Preserve, _Drop_view }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + using _Ty = remove_cvref_t<_Rng>; + + if constexpr (_Is_specialization_v<_Ty, empty_view>) { + return {_St::_Empty, true}; + } else if constexpr (_Reconstructible_range<_Rng>) { + return {_St::_Preserve, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>())})}; + } else { + return {_St::_Drop_view, noexcept(drop_view{_STD declval<_Rng>(), range_difference_t<_Rng>{0}})}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + + template <_Integer_like _Ty> + struct _Partial : _Pipe::_Base<_Partial<_Ty>> { + _Ty _Length; + + // clang-format off + template + requires convertible_to<_Ty&, range_difference_t<_Rng>> + _NODISCARD constexpr auto operator()(_Rng&& _Range) const + noexcept(noexcept(_Drop_fn{}(_STD forward<_Rng>(_Range), _Length))) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); + return _Drop_fn{}(_STD forward<_Rng>(_Range), _Length); + } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const noexcept( + _Choice<_Rng>._No_throw) { + // clang-format on + if constexpr (_Choice<_Rng>._Strategy == _St::_Empty) { + // it's an empty_view: return another empty view + return remove_cvref_t<_Rng>{}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Preserve) { + // it's a "reconstructible range"; return the same kind of range with a restricted extent + _Count = (_STD min)(_RANGES distance(_Range), _Count); + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Drop_view) { + return drop_view{_STD forward<_Rng>(_Range), _Count}; + } + } + + template <_Integer_like _Ty> + _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { + return _Partial<_Ty>{._Length = _Length}; + } + }; + + inline constexpr _Drop_fn drop; + } // namespace views + + // CLASS TEMPLATE ranges::reverse_view + // clang-format off + template + requires bidirectional_range<_Vw> + class reverse_view : public _Cached_position_t, _Vw, reverse_view<_Vw>> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + + template + using _Rev_iter = reverse_iterator>; + + public: + reverse_view() = default; + constexpr explicit reverse_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)) {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr _Rev_iter<_Vw> begin() { + if constexpr (common_range<_Vw>) { + return _Rev_iter<_Vw>{_RANGES end(_Range)}; + } else { + if (this->_Has_cache()) { + return _Rev_iter<_Vw>{this->_Get_cache(_Range)}; + } + + iterator_t<_Vw> _First; + if constexpr (sized_range<_Vw>) { + _First = _RANGES next(_RANGES begin(_Range), _RANGES distance(_Range)); + } else { + _First = _RANGES next(_RANGES begin(_Range), _RANGES end(_Range)); + } + this->_Set_cache(_Range, _First); + return _Rev_iter<_Vw>{_STD move(_First)}; + } + } + + _NODISCARD constexpr auto begin() const noexcept( + noexcept(_Rev_iter{_RANGES end(_Range)})) /* strengthened */ requires common_range { + return _Rev_iter{_RANGES end(_Range)}; + } + + _NODISCARD constexpr _Rev_iter<_Vw> end() noexcept( + noexcept(_Rev_iter<_Vw>{_RANGES begin(_Range)})) /* strengthened */ { + return _Rev_iter<_Vw>{_RANGES begin(_Range)}; + } + _NODISCARD constexpr auto end() const noexcept( + noexcept(_Rev_iter{_RANGES begin(_Range)})) /* strengthened */ requires common_range { + return _Rev_iter{_RANGES begin(_Range)}; + } + + _NODISCARD constexpr auto size() noexcept( + noexcept(_RANGES size(_Range))) /* strengthened */ requires sized_range<_Vw> { + return _RANGES size(_Range); + } + _NODISCARD constexpr auto size() const + noexcept(noexcept(_RANGES size(_Range))) /* strengthened */ requires sized_range { + return _RANGES size(_Range); + } + }; + + template + reverse_view(_Rng &&) -> reverse_view>; + + namespace views { + // VARIABLE views::reverse + template + concept _Can_extract_base = requires(_Rng&& __r) { + static_cast<_Rng&&>(__r).base(); + }; + + template + concept _Can_reverse = requires(_Rng&& __r) { + reverse_view{static_cast<_Rng&&>(__r)}; + }; + + class _Reverse_fn : public _Pipe::_Base<_Reverse_fn> { + private: + enum class _St { _None, _Base, _Subrange_unsized, _Subrange_sized, _Reverse }; + + template + static constexpr auto _Reversed_subrange = -1; + + template + static constexpr auto + _Reversed_subrange, reverse_iterator<_It>, _Ki>> = static_cast(_Ki); + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + if constexpr (bidirectional_range<_Rng>) { + if constexpr (_Is_specialization_v<_Rng, reverse_view>) { + if constexpr (_Can_extract_base<_Rng>) { + return {_St::_Base, noexcept(_STD declval<_Rng>().base())}; + } + } else if constexpr (_Reversed_subrange> == 0) { + using _It = decltype(_STD declval<_Rng&>().begin().base()); + return {_St::_Subrange_unsized, + noexcept(subrange<_It, _It, subrange_kind::unsized>{ + _STD declval<_Rng&>().end().base(), _STD declval<_Rng&>().begin().base()})}; + } else if constexpr (_Reversed_subrange> == 1) { + using _It = decltype(_STD declval<_Rng&>().begin().base()); + return {_St::_Subrange_sized, + noexcept(subrange<_It, _It, subrange_kind::sized>{_STD declval<_Rng&>().end().base(), + _STD declval<_Rng&>().begin().base(), _STD declval<_Rng&>().size()})}; + } else if constexpr (_Can_reverse<_Rng>) { + return {_St::_Reverse, noexcept(reverse_view{_STD declval<_Rng>()})}; + } + } + + return {_St::_None}; + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + + public: + // clang-format off + template + requires (_Choice<_Rng>._Strategy != _St::_None) + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(_Choice<_Rng>._No_throw) { + // clang-format on + if constexpr (_Choice<_Rng>._Strategy == _St::_Base) { + return _STD forward<_Rng>(_Range).base(); + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Subrange_unsized) { + return subrange{_Range.end().base(), _Range.begin().base()}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Subrange_sized) { + return subrange{_Range.end().base(), _Range.begin().base(), _Range.size()}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Reverse) { + return reverse_view{_STD forward<_Rng>(_Range)}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } + } + }; + + inline constexpr _Reverse_fn reverse; + } // namespace views } // namespace ranges namespace views = ranges::views; diff --git a/stl/inc/semaphore b/stl/inc/semaphore new file mode 100644 index 00000000000..0845bdf00f3 --- /dev/null +++ b/stl/inc/semaphore @@ -0,0 +1,311 @@ +// semaphore standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _SEMAPHORE_ +#define _SEMAPHORE_ +#include +#if _STL_COMPILER_PREPROCESSOR + +#ifdef _M_CEE_PURE +#error is not supported when compiling with /clr:pure. +#endif // _M_CEE_PURE + +#if !_HAS_CXX20 +#pragma message("The contents of are available only with C++20 or later.") +#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv + +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +template +_NODISCARD unsigned long long _Semaphore_deadline(const chrono::duration<_Rep, _Period>& _Rel_time) { + return __std_atomic_wait_get_deadline( + chrono::duration_cast>(_Rel_time).count()); +} + +template +_NODISCARD unsigned long _Semaphore_remaining_timeout(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + const auto _Now = _Clock::now(); + if (_Now >= _Abs_time) { + return 0; + } + + const auto _Rel_time = chrono::ceil(_Abs_time - _Now); + static constexpr chrono::milliseconds _Ten_days{chrono::hours{24 * 10}}; + static_assert(_Ten_days.count() < ULONG_MAX, "Bad sizing assumption"); + if (_Rel_time >= _Ten_days) { + return static_cast(_Ten_days.count()); + } + + return static_cast(_Rel_time.count()); +} + +inline constexpr ptrdiff_t _Semaphore_max = (1ULL << (sizeof(ptrdiff_t) * CHAR_BIT - 1)) - 1; + +template +class counting_semaphore { +public: + _NODISCARD static constexpr ptrdiff_t(max)() noexcept { + return _Least_max_value; + } + + constexpr explicit counting_semaphore(const ptrdiff_t _Desired) noexcept /* strengthened */ + : _Counter(_Desired) { + _STL_VERIFY(_Desired >= 0 && _Desired <= _Least_max_value, + "Precondition: desired >= 0, and desired <= max() (N4861 [thread.sema.cnt]/5)"); + } + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + void release(ptrdiff_t _Update = 1) noexcept /* strengthened */ { + if (_Update == 0) { + return; + } + _STL_VERIFY(_Update > 0 && _Update <= _Least_max_value, + "Precondition: update >= 0, and update <= max() - counter (N4861 [thread.sema.cnt]/8)"); + + // We need to notify (wake) at least _Update waiting threads. + // Errors towards waking more cannot be always avoided, but they are performance issues. + // Errors towards waking fewer must be avoided, as they are correctness issues. + + // release thread: Increment semaphore counter, then load waiting counter; + // acquire thread: Increment waiting counter, then load semaphore counter; + + // memory_order_seq_cst for all four operations guarantees that the release thread loads + // the incremented value, or the acquire thread loads the incremented value, or both, but not neither. + // memory_order_seq_cst might be superfluous for some hardware mappings of the C++ memory model, + // but from the point of view of the C++ memory model itself it is needed; weaker orders don't work. + + const ptrdiff_t _Prev = _Counter.fetch_add(static_cast(_Update)); + _STL_VERIFY(_Prev + _Update > 0 && _Prev + _Update <= _Least_max_value, + "Precondition: update <= max() - counter (N4861 [thread.sema.cnt]/8)"); + + const ptrdiff_t _Waiting_upper_bound = _Waiting.load(); + + if (_Waiting_upper_bound == 0) { + // Definitely no one is waiting + } else if (_Waiting_upper_bound <= _Update) { + // No more waiting threads than update, can wake everyone. + _Counter.notify_all(); + } else { + // Wake at most _Update. Though repeated notify_one() is somewhat less efficient than single notify_all(), + // the amount of OS calls is still the same; the benefit from trying not to wake unnecessary threads + // is expected to be greater than the loss on extra calls and atomic operations. + for (; _Update != 0; --_Update) { + _Counter.notify_one(); + } + } + } + + void _Wait(const unsigned long _Remaining_timeout) noexcept { + // See the comment in release() + _Waiting.fetch_add(1); + ptrdiff_t _Current = _Counter.load(); + if (_Current == 0) { + __std_atomic_wait_direct(&_Counter, &_Current, sizeof(_Current), _Remaining_timeout); + } + _Waiting.fetch_sub(1, memory_order_relaxed); + } + + void acquire() noexcept /* strengthened */ { + ptrdiff_t _Current = _Counter.load(memory_order_relaxed); + for (;;) { + while (_Current == 0) { + _Wait(_Atomic_wait_no_timeout); + _Current = _Counter.load(memory_order_relaxed); + } + _STL_VERIFY(_Current > 0 && _Current <= _Least_max_value, + "Invariant: counter >= 0, and counter <= max() " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + + // "happens after release" ordering is provided by this CAS, so loads and waits can be relaxed + if (_Counter.compare_exchange_weak(_Current, _Current - 1)) { + return; + } + } + } + + _NODISCARD bool try_acquire() noexcept { + ptrdiff_t _Current = _Counter.load(); + if (_Current == 0) { + return false; + } + _STL_VERIFY(_Current > 0 && _Current <= _Least_max_value, + "Invariant: counter >= 0, and counter <= max() " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + + return _Counter.compare_exchange_weak(_Current, _Current - 1); + } + + template + _NODISCARD bool try_acquire_for(const chrono::duration<_Rep, _Period>& _Rel_time) { + auto _Deadline = _Semaphore_deadline(_Rel_time); + ptrdiff_t _Current = _Counter.load(memory_order_relaxed); + for (;;) { + while (_Current == 0) { + const auto _Remaining_timeout = __std_atomic_wait_get_remaining_timeout(_Deadline); + if (_Remaining_timeout == 0) { + return false; + } + _Wait(_Remaining_timeout); + _Current = _Counter.load(memory_order_relaxed); + } + _STL_VERIFY(_Current > 0 && _Current <= _Least_max_value, + "Invariant: counter >= 0, and counter <= max() " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + + // "happens after release" ordering is provided by this CAS, so loads and waits can be relaxed + if (_Counter.compare_exchange_weak(_Current, _Current - 1)) { + return true; + } + } + } + + template + _NODISCARD bool try_acquire_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + ptrdiff_t _Current = _Counter.load(memory_order_relaxed); + for (;;) { + while (_Current == 0) { + const unsigned long _Remaining_timeout = _Semaphore_remaining_timeout(_Abs_time); + if (_Remaining_timeout == 0) { + return false; + } + _Wait(_Remaining_timeout); + _Current = _Counter.load(memory_order_relaxed); + } + _STL_VERIFY(_Current > 0 && _Current <= _Least_max_value, + "Invariant: counter >= 0, and counter <= max() " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + + // "happens after release" ordering is provided by this CAS, so loads and waits can be relaxed + if (_Counter.compare_exchange_weak(_Current, _Current - 1)) { + return true; + } + } + } + +private: + atomic _Counter; + atomic _Waiting; +}; + +template <> +class counting_semaphore<1> { +public: + _NODISCARD static constexpr ptrdiff_t(max)() noexcept { + return 1; + } + + constexpr explicit counting_semaphore(const ptrdiff_t _Desired) noexcept /* strengthened */ + : _Counter(static_cast(_Desired)) { + _STL_VERIFY((_Desired & ~1) == 0, "Precondition: desired >= 0, and desired <= max() " + "(N4861 [thread.sema.cnt]/5)"); + } + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + void release(const ptrdiff_t _Update = 1) noexcept /* strengthened */ { + if (_Update == 0) { + return; + } + _STL_VERIFY(_Update == 1, "Precondition: update >= 0, " + "and update <= max() - counter (N4861 [thread.sema.cnt]/8)"); + // TRANSITION, GH-1133: should be memory_order_release + _Counter.store(1); + _Counter.notify_one(); + } + + void acquire() noexcept /* strengthened */ { + for (;;) { + // "happens after release" ordering is provided by this exchange, so loads and waits can be relaxed + // TRANSITION, GH-1133: should be memory_order_acquire + unsigned char _Prev = _Counter.exchange(0); + if (_Prev == 1) { + break; + } + _STL_VERIFY(_Prev == 0, "Invariant: semaphore counter is non-negative and doesn't exceed max(), " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + _Counter.wait(0, memory_order_relaxed); + } + } + + _NODISCARD bool try_acquire() noexcept { + // TRANSITION, GH-1133: should be memory_order_acquire + unsigned char _Prev = _Counter.exchange(0); + _STL_VERIFY((_Prev & ~1) == 0, "Invariant: semaphore counter is non-negative and doesn't exceed max(), " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + return reinterpret_cast(_Prev); + } + + template + _NODISCARD bool try_acquire_for(const chrono::duration<_Rep, _Period>& _Rel_time) { + auto _Deadline = _Semaphore_deadline(_Rel_time); + for (;;) { + // "happens after release" ordering is provided by this exchange, so loads and waits can be relaxed + // TRANSITION, GH-1133: should be memory_order_acquire + unsigned char _Prev = _Counter.exchange(0); + if (_Prev == 1) { + return true; + } + _STL_VERIFY(_Prev == 0, "Invariant: semaphore counter is non-negative and doesn't exceed max(), " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + const auto _Remaining_timeout = __std_atomic_wait_get_remaining_timeout(_Deadline); + if (_Remaining_timeout == 0) { + return false; + } + __std_atomic_wait_direct(&_Counter, &_Prev, sizeof(_Prev), _Remaining_timeout); + } + } + + template + _NODISCARD bool try_acquire_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + for (;;) { + // "happens after release" ordering is provided by this exchange, so loads and waits can be relaxed + // TRANSITION, GH-1133: should be memory_order_acquire + unsigned char _Prev = _Counter.exchange(0); + if (_Prev == 1) { + return true; + } + _STL_VERIFY(_Prev == 0, "Invariant: semaphore counter is non-negative and doesn't exceed max(), " + "possibly caused by preconditions violation (N4861 [thread.sema.cnt]/8)"); + + const unsigned long _Remaining_timeout = _Semaphore_remaining_timeout(_Abs_time); + if (_Remaining_timeout == 0) { + return false; + } + + __std_atomic_wait_direct(&_Counter, &_Prev, sizeof(_Prev), _Remaining_timeout); + } + } + +private: + atomic _Counter; +}; + +using binary_semaphore = counting_semaphore<1>; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // ^^^ _HAS_CXX20 ^^^ + +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _SEMAPHORE_ diff --git a/stl/inc/utility b/stl/inc/utility index f40b42623e6..1727b5ca4b6 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -132,7 +132,7 @@ struct pair { // store a pair of values template , is_default_constructible<_Uty2>>, int> = 0> constexpr explicit( - !_Is_implicitly_default_constructible<_Uty1>::value || !_Is_implicitly_default_constructible<_Uty2>::value) + !conjunction_v<_Is_implicitly_default_constructible<_Uty1>, _Is_implicitly_default_constructible<_Uty2>>) pair() noexcept( is_nothrow_default_constructible_v<_Uty1>&& is_nothrow_default_constructible_v<_Uty2>) // strengthened : first(), second() {} @@ -158,7 +158,7 @@ struct pair { // store a pair of values #if _HAS_CONDITIONAL_EXPLICIT template , is_copy_constructible<_Uty2>>, int> = 0> - constexpr explicit(!is_convertible_v || !is_convertible_v) + constexpr explicit(!conjunction_v, is_convertible>) pair(const _Ty1& _Val1, const _Ty2& _Val2) noexcept( is_nothrow_copy_constructible_v<_Uty1>&& is_nothrow_copy_constructible_v<_Uty2>) // strengthened : first(_Val1), second(_Val2) {} @@ -184,7 +184,7 @@ struct pair { // store a pair of values #if _HAS_CONDITIONAL_EXPLICIT template , is_constructible<_Ty2, _Other2>>, int> = 0> - constexpr explicit(!is_convertible_v<_Other1, _Ty1> || !is_convertible_v<_Other2, _Ty2>) + constexpr explicit(!conjunction_v, is_convertible<_Other2, _Ty2>>) pair(_Other1&& _Val1, _Other2&& _Val2) noexcept( is_nothrow_constructible_v<_Ty1, _Other1>&& is_nothrow_constructible_v<_Ty2, _Other2>) // strengthened : first(_STD forward<_Other1>(_Val1)), second(_STD forward<_Other2>(_Val2)) {} @@ -213,7 +213,7 @@ struct pair { // store a pair of values template , is_constructible<_Ty2, const _Other2&>>, int> = 0> - constexpr explicit(!is_convertible_v || !is_convertible_v) + constexpr explicit(!conjunction_v, is_convertible>) pair(const pair<_Other1, _Other2>& _Right) noexcept(is_nothrow_constructible_v<_Ty1, const _Other1&>&& is_nothrow_constructible_v<_Ty2, const _Other2&>) // strengthened : first(_Right.first), second(_Right.second) {} @@ -240,7 +240,7 @@ struct pair { // store a pair of values #if _HAS_CONDITIONAL_EXPLICIT template , is_constructible<_Ty2, _Other2>>, int> = 0> - constexpr explicit(!is_convertible_v<_Other1, _Ty1> || !is_convertible_v<_Other2, _Ty2>) + constexpr explicit(!conjunction_v, is_convertible<_Other2, _Ty2>>) pair(pair<_Other1, _Other2>&& _Right) noexcept( is_nothrow_constructible_v<_Ty1, _Other1>&& is_nothrow_constructible_v<_Ty2, _Other2>) // strengthened : first(_STD forward<_Other1>(_Right.first)), second(_STD forward<_Other2>(_Right.second)) {} diff --git a/stl/inc/valarray b/stl/inc/valarray index 087a5f37bb1..40b68df05ad 100644 --- a/stl/inc/valarray +++ b/stl/inc/valarray @@ -385,10 +385,18 @@ public: } _NODISCARD const _Ty& operator[](size_t _Off) const noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Off < _Mysize, "valarray subscript out of range"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Myptr[_Off]; } _NODISCARD _Ty& operator[](size_t _Off) noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Off < _Mysize, "valarray subscript out of range"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Myptr[_Off]; } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index c5744776e0c..c723ab4d262 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -223,13 +223,6 @@ void _Deallocate(void* _Ptr, size_t _Bytes) noexcept { #undef _HAS_ALIGNED_NEW -// FUNCTION TEMPLATE _Construct_in_place -template -void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { - ::new (const_cast(static_cast(_STD addressof(_Obj)))) - _Ty(_STD forward<_Types>(_Args)...); -} - // FUNCTION TEMPLATE _Global_new template _Ty* _Global_new(_Types&&... _Args) { // acts as "new" while disallowing user overload selection @@ -1751,7 +1744,7 @@ _Alloc_ptr_t<_Alloc> _Uninitialized_fill_n( // copy _Count copies of _Val to raw _First, using _Al using _Ty = typename _Alloc::value_type; if constexpr (_Fill_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { - _CSTD memset(_Unfancy(_First), static_cast(_Val), static_cast(_Count)); + _Fill_memset(_Unfancy(_First), _Val, static_cast(_Count)); return _First + _Count; } else { _Uninitialized_backout_al<_Alloc> _Backout{_First, _Al}; @@ -1779,7 +1772,7 @@ template _Alloc_ptr_t<_Alloc> _Uninit_alloc_fill_n1(_Alloc_ptr_t<_Alloc> _First, _Alloc_size_t<_Alloc> _Count, const typename _Alloc::value_type& _Val, _Alloc&, true_type) { // copy _Count copies of _Val to raw _First, using default _Alloc construct, memset optimization - _CSTD memset(_Unfancy(_First), static_cast(_Val), _Count); + _Fill_memset(_Unfancy(_First), _Val, _Count); return _First + _Count; } @@ -1802,7 +1795,7 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); if constexpr (_Fill_memset_is_safe<_Unwrapped_t, _Tval>) { - _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); + _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); } else { _Uninitialized_backout<_Unwrapped_t> _Backout{_UFirst}; while (_Backout._Last != _ULast) { @@ -1829,7 +1822,7 @@ template void _Uninitialized_fill_unchecked( const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, const _Tval& _Val, true_type) { // copy _Val throughout raw [_First, _Last), memset optimization - _CSTD memset(_First, static_cast(_Val), static_cast(_Last - _First)); + _Fill_memset(_First, _Val, static_cast(_Last - _First)); } template diff --git a/stl/inc/xstring b/stl/inc/xstring index 5cd221c2f72..a1ac9f75908 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -1048,7 +1048,7 @@ public: constexpr _String_view_iterator& operator+=(const difference_type _Off) noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _Verify_offset(_Off); - _Myoff += _Off; + _Myoff += static_cast(_Off); #else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv _Myptr += _Off; #endif // _ITERATOR_DEBUG_LEVEL @@ -1077,7 +1077,7 @@ public: "cannot seek string_view iterator after end"); } - _Myoff -= _Off; + _Myoff -= static_cast(_Off); #else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv _Myptr -= _Off; #endif // _ITERATOR_DEBUG_LEVEL @@ -1209,6 +1209,10 @@ public: "Bad char_traits for basic_string_view; " "N4659 24.4.2 [string.view.template]/1 \"the type traits::char_type shall name the same type as charT.\""); + static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>, + "The character type of basic_string_view must be a non-array trivial standard-layout type. See N4861 " + "[strings.general]/1."); + using traits_type = _Traits; using value_type = _Elem; using pointer = _Elem*; @@ -1240,6 +1244,15 @@ public: #endif // _CONTAINER_DEBUG_LEVEL > 0 } +#ifdef __cpp_lib_concepts + // clang-format off + template _Se> + requires (is_same_v, _Elem> && !is_convertible_v<_Se, size_type>) + constexpr basic_string_view(_It _First, _Se _Last) noexcept // strengthened + : _Mydata(_STD to_address(_First)), _Mysize(static_cast(_Last - _First)) {} + // clang-format on +#endif // __cpp_lib_concepts + _NODISCARD constexpr const_iterator begin() const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 return const_iterator(_Mydata, _Mysize, 0); @@ -1628,6 +1641,9 @@ private: }; #ifdef __cpp_lib_concepts +template _Se> +basic_string_view(_It, _Se) -> basic_string_view>; + namespace ranges { template inline constexpr bool enable_view> = true; @@ -2278,6 +2294,10 @@ private: "N4659 24.3.2.1 [string.require]/3 requires that the supplied " "char_traits character type match the string's character type."); + static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>, + "The character type of basic_string must be a non-array trivial standard-layout type. See N4861 " + "[strings.general]/1."); + public: using traits_type = _Traits; using allocator_type = _Alloc; diff --git a/stl/inc/xutility b/stl/inc/xutility index 7a08e0a3cc5..9658711f8ac 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -119,6 +119,13 @@ struct _Get_rebind_alias<_Ty, _Other, void_t; }; +// FUNCTION TEMPLATE _Construct_in_place +template +void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { + ::new (const_cast(static_cast(_STD addressof(_Obj)))) + _Ty(_STD forward<_Types>(_Args)...); +} + // STRUCT TEMPLATE pointer_traits template struct pointer_traits { @@ -516,7 +523,7 @@ struct _Iter_traits_pointer<_Itraits_pointer_strategy::_Use_decltype> { }; template -concept _Has_op_arrow = requires(_Ty&& __t) { +concept _Has_member_arrow = requires(_Ty&& __t) { static_cast<_Ty&&>(__t).operator->(); }; @@ -620,7 +627,7 @@ struct _Iterator_traits_base<_It> { using value_type = typename indirectly_readable_traits<_It>::value_type; using pointer = typename _Iter_traits_pointer<( _Has_member_pointer<_It> ? _Itraits_pointer_strategy::_Use_member - : _Has_op_arrow<_It&> ? _Itraits_pointer_strategy::_Use_decltype + : _Has_member_arrow<_It&> ? _Itraits_pointer_strategy::_Use_decltype : _Itraits_pointer_strategy::_Use_void)>::template _Apply<_It>; using reference = typename _Iter_traits_reference<_Has_member_reference<_It>>::template _Apply<_It>; }; @@ -645,7 +652,7 @@ struct iterator_traits<_Ty*> { }; // clang-format on -// CUSTOMIZATION POINT OBJECT iter_move +// CUSTOMIZATION POINT OBJECT ranges::iter_move namespace ranges { // STRUCT TEMPLATE _Choice_t template @@ -707,9 +714,10 @@ namespace ranges { // clang-format on } // namespace _Iter_move - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Iter_move::_Cpo iter_move; } + using namespace _Cpos; } // namespace ranges // iter_swap defined below since it depends on indirectly_movable_storable @@ -1017,7 +1025,7 @@ concept indirectly_copyable_storable = indirectly_copyable<_In, _Out> && assignable_from&, iter_reference_t<_In>>; // clang-format on -// CUSTOMIZATION POINT OBJECT iter_swap +// CUSTOMIZATION POINT OBJECT ranges::iter_swap namespace ranges { namespace _Iter_swap { template @@ -1091,9 +1099,10 @@ namespace ranges { }; } // namespace _Iter_swap - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Iter_swap::_Cpo iter_swap; } + using namespace _Cpos; } // namespace ranges // clang-format off @@ -2327,6 +2336,10 @@ _NODISCARD _Ty _Fake_decay_copy(_Ty) noexcept; // (2) is well-formed if and only if E is implicitly convertible to T and T is destructible, and // (3) is non-throwing if and only if both conversion from decltype((E)) to T and destruction of T are non-throwing. +// CONCEPT _Not_same_as +template +concept _Not_same_as = !same_as, remove_cvref_t<_Ty2>>; + namespace ranges { // VARIABLE TEMPLATE _Has_complete_elements template @@ -2408,9 +2421,10 @@ namespace ranges { }; } // namespace _Begin - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Begin::_Cpo begin; } + using namespace _Cpos; // ALIAS TEMPLATE ranges::iterator_t template @@ -2468,9 +2482,10 @@ namespace ranges { }; } // namespace _Unchecked_begin - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Unchecked_begin::_Cpo _Ubegin; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::end namespace _End { @@ -2542,9 +2557,10 @@ namespace ranges { }; } // namespace _End - inline namespace _Cpos { + namespace _Cpos { inline constexpr _End::_Cpo end; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::_Uend namespace _Unchecked_end { @@ -2599,9 +2615,10 @@ namespace ranges { }; } // namespace _Unchecked_end - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Unchecked_end::_Cpo _Uend; } + using namespace _Cpos; // CONCEPT ranges::range template @@ -2648,9 +2665,10 @@ namespace ranges { // clang-format on }; - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Cbegin_fn cbegin; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::cend struct _Cend_fn { @@ -2664,9 +2682,10 @@ namespace ranges { // clang-format on }; - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Cend_fn cend; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::rbegin namespace _Rbegin { @@ -2733,9 +2752,10 @@ namespace ranges { }; } // namespace _Rbegin - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Rbegin::_Cpo rbegin; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::rend namespace _Rend { @@ -2803,9 +2823,10 @@ namespace ranges { }; } // namespace _Rend - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Rend::_Cpo rend; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::crbegin struct _Crbegin_fn { @@ -2819,9 +2840,10 @@ namespace ranges { // clang-format on }; - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Crbegin_fn crbegin; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::crend struct _Crend_fn { @@ -2835,9 +2857,10 @@ namespace ranges { // clang-format on }; - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Crend_fn crend; } + using namespace _Cpos; // VARIABLE TEMPLATE ranges::disable_sized_range template @@ -2921,9 +2944,10 @@ namespace ranges { }; } // namespace _Size - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Size::_Cpo size; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::empty namespace _Empty { @@ -2989,9 +3013,10 @@ namespace ranges { }; } // namespace _Empty - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Empty::_Cpo empty; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::data namespace _Data { @@ -3046,9 +3071,10 @@ namespace ranges { }; } // namespace _Data - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Data::_Cpo data; } + using namespace _Cpos; // CUSTOMIZATION POINT OBJECT ranges::cdata struct _Cdata_fn { @@ -3062,9 +3088,10 @@ namespace ranges { // clang-format on }; - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Cdata_fn cdata; } + using namespace _Cpos; // clang-format off // CONCEPT ranges::sized_range @@ -3241,7 +3268,8 @@ namespace ranges { using _Not_quite_object::_Not_quite_object; template _Se> - _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const { + _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const + noexcept(_Nothrow_distance<_It, _Se>) /* strengthened */ { if constexpr (sized_sentinel_for<_Se, _It>) { return _Last - _First; } else { @@ -3251,7 +3279,8 @@ namespace ranges { } template - _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const { + _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const + noexcept(_Nothrow_size<_Rng>) /* strengthened */ { if constexpr (sized_range<_Rng>) { return static_cast>(_RANGES size(_Range)); } else { @@ -3261,7 +3290,8 @@ namespace ranges { private: template - _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) { + _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept( + _Nothrow_distance<_It, _Se>) { _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); @@ -3272,6 +3302,18 @@ namespace ranges { return _Count; } + + template + static constexpr bool _Nothrow_distance = noexcept( + noexcept(++_STD declval<_Unwrapped_t<_It>&>() != _STD declval&>())); + template _Se> + static constexpr bool _Nothrow_distance<_It, _Se> = noexcept( + noexcept(_STD declval<_Se&>() - _STD declval<_It&>())); + + template + static constexpr bool _Nothrow_size = _Nothrow_distance, sentinel_t<_Rng>>; + template + static constexpr bool _Nothrow_size<_Rng> = noexcept(_RANGES size(_STD declval<_Rng&>())); }; inline constexpr _Distance_fn distance{_Not_quite_object::_Construct_tag{}}; @@ -3289,9 +3331,10 @@ namespace ranges { // clang-format on }; - inline namespace _Cpos { + namespace _Cpos { inline constexpr _Ssize_fn ssize; } + using namespace _Cpos; // VARIABLE ranges::next class _Next_fn : private _Not_quite_object { @@ -3394,10 +3437,6 @@ namespace ranges { using is_transparent = int; }; - // CONCEPT _Not_same_as - template - concept _Not_same_as = !same_as, remove_cvref_t<_Ty2>>; - // CONCEPT ranges::common_range // clang-format off template @@ -3584,7 +3623,9 @@ namespace ranges { auto& _Self = _Cast(); #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (sized_range<_Derived>) { - _STL_VERIFY(_Idx < _RANGES size(_Self), "index out of range for view_interface"); + using _U_diff = _Make_unsigned_like_t>; + _STL_VERIFY(static_cast<_U_diff>(_Idx) < static_cast<_U_diff>(_RANGES size(_Self)), + "index out of range for view_interface"); } #endif // _CONTAINER_DEBUG_LEVEL > 0 return _RANGES begin(_Self)[_Idx]; @@ -3595,7 +3636,9 @@ namespace ranges { auto& _Self = _Cast(); #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (sized_range<_Derived>) { - _STL_VERIFY(_Idx < _RANGES size(_Self), "index out of range for view_interface"); + using _U_diff = _Make_unsigned_like_t>; + _STL_VERIFY(static_cast<_U_diff>(_Idx) < static_cast<_U_diff>(_RANGES size(_Self)), + "index out of range for view_interface"); } #endif // _CONTAINER_DEBUG_LEVEL > 0 return _RANGES begin(_Self)[_Idx]; @@ -3970,10 +4013,10 @@ public: // clang-format on #ifdef __cpp_lib_concepts - _NODISCARD constexpr const iterator_type& base() const& { // Per LWG-3391 + _NODISCARD constexpr const iterator_type& base() const& noexcept /* strengthened */ { // Per LWG-3391 return _Current; } - _NODISCARD constexpr iterator_type base() && { + _NODISCARD constexpr iterator_type base() && noexcept(is_nothrow_move_constructible_v<_Iter>) /* strengthened */ { return _STD move(_Current); } #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv @@ -4732,24 +4775,31 @@ struct _Is_character : true_type {}; // UTF-8 code units are sort-of ch #endif // __cpp_char8_t template -struct _Is_character_or_byte : _Is_character<_Ty>::type {}; +struct _Is_character_or_byte_or_bool : _Is_character<_Ty>::type {}; #ifdef __cpp_lib_byte template <> -struct _Is_character_or_byte : true_type {}; +struct _Is_character_or_byte_or_bool : true_type {}; #endif // __cpp_lib_byte +template <> +struct _Is_character_or_byte_or_bool : true_type {}; + // _Fill_memset_is_safe determines if _FwdIt and _Ty are eligible for memset optimization in fill template > -_INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v< - disjunction>, - _Is_character_or_byte<_Unwrap_enum_t<_Iter_value_t<_FwdIt>>>>, - conjunction>, is_same>>>>, +_INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, + _Is_character_or_byte_or_bool<_Unwrap_enum_t>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; template _INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; +template +void _Fill_memset(_DestTy* const _Dest, const _Ty _Val, const size_t _Count) { + _DestTy _Dest_val = _Val; // implicitly convert (a cast would suppress warnings); also handles _DestTy being bool + _CSTD memset(_Dest, static_cast(_Dest_val), _Count); +} + #if _HAS_IF_CONSTEXPR template _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) { @@ -4765,7 +4815,7 @@ _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); + _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); return; } } @@ -4787,7 +4837,7 @@ void _Fill_unchecked1(_FwdIt _First, _FwdIt _Last, const _Ty& _Val, false_type) template void _Fill_unchecked1(_FwdIt _First, _FwdIt _Last, const _Ty& _Val, true_type) { // copy _Val through [_First, _Last), memset optimization - _CSTD memset(_First, static_cast(_Val), static_cast(_Last - _First)); + _Fill_memset(_First, _Val, static_cast(_Last - _First)); } template @@ -4826,7 +4876,7 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - _CSTD memset(_UDest, static_cast(_Val), static_cast(_Count)); + _Fill_memset(_UDest, _Val, static_cast(_Count)); _UDest += _Count; _Seek_wrapped(_Dest, _UDest); return _Dest; @@ -4857,7 +4907,7 @@ _OutIt _Fill_n_unchecked2(_OutIt _Dest, _Diff _Count, const _Ty& _Val, false_typ template _OutIt _Fill_n_unchecked2(_OutIt _Dest, _Diff _Count, const _Ty& _Val, true_type) { // copy _Val _Count times through [_Dest, ...), memset optimization - _CSTD memset(_Dest, static_cast(_Val), static_cast(_Count)); + _Fill_memset(_Dest, _Val, static_cast(_Count)); return _Dest + _Count; } @@ -4892,15 +4942,18 @@ _FwdIt fill_n(_ExPo&&, _FwdIt _Dest, _Diff _Count_raw, const _Ty& _Val) noexcept // Integral types are eligible for memcmp in very specific cases. // * They must be the same size. (`int == long` is eligible; `int == long long` isn't.) -// * They can't be bool. (Not even `bool == bool`; we're concerned about representations other than 0 and 1.) // * They can't be volatile. // * Finally, the usual arithmetic conversions must preserve bit patterns. (This is true for `int == unsigned int`, // but false for `short == unsigned short`.) +#pragma warning(push) +#pragma warning(disable : 4806) // no value of type 'bool' promoted to type 'char' can equal the given constant template && !is_volatile_v<_Elem1> // - && _Is_nonbool_integral<_Elem2> && !is_volatile_v<_Elem2>> -_INLINE_VAR constexpr bool _Can_memcmp_elements = static_cast<_Elem1>(-1) == static_cast<_Elem2>(-1); + && is_integral_v<_Elem1> && !is_volatile_v<_Elem1> // + && is_integral_v<_Elem2> && !is_volatile_v<_Elem2>> +_INLINE_VAR constexpr bool _Can_memcmp_elements = + is_same_v<_Elem1, bool> || is_same_v<_Elem2, bool> || static_cast<_Elem1>(-1) == static_cast<_Elem2>(-1); +#pragma warning(pop) #ifdef __cpp_lib_byte // Allow memcmping std::byte. @@ -5752,6 +5805,52 @@ _NODISCARD _CONSTEXPR20 _InIt find_if(_InIt _First, const _InIt _Last, _Pr _Pred return _First; } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::find_if + // concept-constrained for strict enforcement as it is used by several algorithms + template _Se, class _Pj, indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It _Find_if_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + for (; _First != _Last; ++_First) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + break; + } + } + + return _First; + } + + class _Find_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _RANGES _Find_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + template , _Pj>> _Pr> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = _RANGES _Find_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + }; + + inline constexpr _Find_if_fn find_if{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE lower_bound template _NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, const _FwdIt _Last, const _Ty& _Val, _Pr _Pred) { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 3043b0eec62..0d62a90da3e 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -185,7 +185,6 @@ // P1115R3 erase()/erase_if() Return size_type // P1123R0 Atomic Compare-And-Exchange With Padding Bits For atomic_ref // P1135R6 The C++20 Synchronization Library -// (partially implemented) // P1207R4 Movability Of Single-Pass Iterators // (partially implemented) // P1209R0 erase_if(), erase() @@ -194,6 +193,7 @@ // (partially implemented) // P1248R1 Fixing Relations // P1357R1 is_bounded_array, is_unbounded_array +// P1391R4 Range Constructor For string_view // P1394R4 Range Constructor For span // P1423R3 char8_t Backward Compatibility Remediation // P1456R1 Move-Only Views @@ -203,7 +203,10 @@ // P1651R0 bind_front() Should Not Unwrap reference_wrapper // P1690R1 Refining Heterogeneous Lookup For Unordered Containers // P1716R3 Range Comparison Algorithms Are Over-Constrained +// P1739R4 Avoiding Template Bloat For Ranges +// (partially implemented) // P1754R1 Rename Concepts To standard_case +// P1865R1 Adding max() To latch And barrier // P1870R1 Rename forwarding-range To borrowed_range (Was safe_range before LWG-3379) // P1871R1 disable_sized_sentinel_for // P1872R0 span Should Have size_type, Not index_type @@ -495,7 +498,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 142 -#define _MSVC_STL_UPDATE 202008L +#define _MSVC_STL_UPDATE 202009L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #ifdef __EDG__ @@ -1145,6 +1148,7 @@ #define __cpp_lib_atomic_ref 201806L #define __cpp_lib_atomic_shared_ptr 201711L #define __cpp_lib_atomic_wait 201907L +#define __cpp_lib_barrier 201907L #define __cpp_lib_bind_front 201907L #define __cpp_lib_bit_cast 201806L #define __cpp_lib_bitops 201907L @@ -1154,9 +1158,9 @@ #define __cpp_lib_char8_t 201907L #endif // __cpp_char8_t -#ifndef __EDG__ // TRANSITION, EDG concepts support +#if !defined(__EDG__) || defined(__INTELLISENSE__) // TRANSITION, EDG concepts support #define __cpp_lib_concepts 201907L -#endif // __EDG__ +#endif // !defined(__EDG__) || defined(__INTELLISENSE__) #define __cpp_lib_constexpr_algorithms 201806L #define __cpp_lib_constexpr_complex 201711L @@ -1185,9 +1189,11 @@ #define __cpp_lib_interpolate 201902L #define __cpp_lib_is_constant_evaluated 201811L #define __cpp_lib_is_nothrow_convertible 201806L +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_remove_cvref 201711L +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/stl/src/cthread.cpp b/stl/src/cthread.cpp index 99cee9b12e6..7b144c82151 100644 --- a/stl/src/cthread.cpp +++ b/stl/src/cthread.cpp @@ -48,8 +48,8 @@ _CRTIMP2_PURE void _Thrd_exit(int res) { // terminate execution of calling threa // TRANSITION, ABI: _Thrd_start() is preserved for binary compatibility _CRTIMP2_PURE int _Thrd_start(_Thrd_t* thr, _Thrd_callback_t func, void* b) { // start a thread - thr->_Hnd = reinterpret_cast(_beginthreadex(0, 0, func, b, 0, &thr->_Id)); - return thr->_Hnd == 0 ? _Thrd_error : _Thrd_success; + thr->_Hnd = reinterpret_cast(_beginthreadex(nullptr, 0, func, b, 0, &thr->_Id)); + return thr->_Hnd == nullptr ? _Thrd_error : _Thrd_success; } int _Thrd_join(_Thrd_t thr, int* code) { // return exit code when thread terminates diff --git a/stl/src/filesys.cpp b/stl/src/filesys.cpp index 3218e6e5e6d..2ec30bdad0d 100644 --- a/stl/src/filesys.cpp +++ b/stl/src/filesys.cpp @@ -52,8 +52,8 @@ static HANDLE _FilesysOpenFile(const wchar_t* _Fname, DWORD _Desired_access, DWO return CreateFile2(_Fname, _Desired_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, OPEN_EXISTING, &_Create_file_parameters); #else // _CRT_APP - return CreateFileW( - _Fname, _Desired_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, _Flags, 0); + return CreateFileW(_Fname, _Desired_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_EXISTING, _Flags, nullptr); #endif // _CRT_APP } @@ -174,7 +174,7 @@ _FS_DLL wchar_t* __CLRCALL_PURE_OR_CDECL _Temp_get(wchar_t (&_Dest)[_MAX_FILESYS _FS_DLL int __CLRCALL_PURE_OR_CDECL _Make_dir(const wchar_t* _Fname, const wchar_t*) { // make a new directory (ignore attributes) - int _Ans = CreateDirectoryW(_Fname, 0); + int _Ans = CreateDirectoryW(_Fname, nullptr); if (_Ans != 0) { return 1; @@ -195,7 +195,7 @@ _FS_DLL file_type __CLRCALL_PURE_OR_CDECL _Stat(const wchar_t* _Fname, perms* _P if (GetFileAttributesExW(_Fname, GetFileExInfoStandard, &_Data)) { // get file type and return permissions - if (_Pmode != 0) { + if (_Pmode != nullptr) { constexpr perms _Write_perms = perms::owner_write | perms::group_write | perms::others_write; constexpr perms _Readonly_perms = perms::all & ~_Write_perms; @@ -398,7 +398,7 @@ _FS_DLL int __CLRCALL_PURE_OR_CDECL _Link(const wchar_t* _Fname1, const wchar_t* (void) _Fname2; return errno = EDOM; // hardlinks not supported #else // _CRT_APP - return CreateHardLinkW(_Fname2, _Fname1, 0) != 0 ? 0 : GetLastError(); + return CreateHardLinkW(_Fname2, _Fname1, nullptr) != 0 ? 0 : GetLastError(); #endif // _CRT_APP } @@ -426,7 +426,7 @@ _FS_DLL int __CLRCALL_PURE_OR_CDECL _Resize(const wchar_t* _Fname, uintmax_t _Ne if (_Handle != INVALID_HANDLE_VALUE) { // set file pointer to new size and trim LARGE_INTEGER _Large; _Large.QuadPart = _Newsize; - _Ok = SetFilePointerEx(_Handle, _Large, 0, FILE_BEGIN) != 0 && SetEndOfFile(_Handle) != 0; + _Ok = SetFilePointerEx(_Handle, _Large, nullptr, FILE_BEGIN) != 0 && SetEndOfFile(_Handle) != 0; CloseHandle(_Handle); } return _Ok ? 0 : GetLastError(); diff --git a/stl/src/filesystem.cpp b/stl/src/filesystem.cpp index c5f3232e377..cb81206ad2a 100644 --- a/stl/src/filesystem.cpp +++ b/stl/src/filesystem.cpp @@ -271,7 +271,7 @@ _EXTERN_C _In_ const __std_fs_file_flags _Flags) noexcept { // calls CreateFile2 or CreateFileW const HANDLE _Result = __vcp_CreateFile(_File_name, static_cast(_Desired_access), FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, - static_cast(_Flags), 0); + static_cast(_Flags), nullptr); *_Handle = static_cast<__std_fs_file_handle>(reinterpret_cast(_Result)); return _Translate_CreateFile_last_error(_Result); } @@ -462,15 +462,15 @@ void __stdcall __std_fs_directory_iterator_close(_In_ const __std_fs_dir_handle // We test equivalent() not by directly doing what equivalent() does, but by opening the handles // in exclusive mode, so a subsequent open will fail with ERROR_SHARING_VIOLATION. { - const _STD _Fs_file _Source_handle( - __vcp_CreateFile(_Source, FILE_READ_ATTRIBUTES | FILE_READ_DATA, 0, nullptr, OPEN_EXISTING, 0, 0)); + const _STD _Fs_file _Source_handle(__vcp_CreateFile( + _Source, FILE_READ_ATTRIBUTES | FILE_READ_DATA, 0, nullptr, OPEN_EXISTING, 0, nullptr)); __std_win_error _Last_error = _Translate_CreateFile_last_error(_Source_handle._Get()); if (_Last_error != __std_win_error::_Success) { return {false, _Last_error}; } - const _STD _Fs_file _Target_handle( - __vcp_CreateFile(_Target, FILE_READ_ATTRIBUTES | FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, 0, 0)); + const _STD _Fs_file _Target_handle(__vcp_CreateFile( + _Target, FILE_READ_ATTRIBUTES | FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, 0, nullptr)); _Last_error = _Translate_CreateFile_last_error(_Target_handle._Get()); if (_Last_error != __std_win_error::_Success) { // Also handles the equivalent(from, to) error case @@ -767,7 +767,7 @@ _Success_(return == __std_win_error::_Success) __std_win_error LARGE_INTEGER _Large; _Large.QuadPart = _New_size; - if (SetFilePointerEx(_Handle._Get(), _Large, 0, FILE_BEGIN) == 0 || SetEndOfFile(_Handle._Get()) == 0) { + if (SetFilePointerEx(_Handle._Get(), _Large, nullptr, FILE_BEGIN) == 0 || SetEndOfFile(_Handle._Get()) == 0) { return __std_win_error{GetLastError()}; } diff --git a/stl/src/fiopen.cpp b/stl/src/fiopen.cpp index f5e2892dfd1..da9b5363207 100644 --- a/stl/src/fiopen.cpp +++ b/stl/src/fiopen.cpp @@ -11,14 +11,14 @@ _STD_BEGIN FILE* _Xfsopen(_In_z_ const char* filename, _In_ int mode, _In_ int prot) { static const char* const mods[] = {// fopen mode strings corresponding to valid[i] - "r", "w", "w", "a", "rb", "wb", "wb", "ab", "r+", "w+", "a+", "r+b", "w+b", "a+b", 0}; + "r", "w", "w", "a", "rb", "wb", "wb", "ab", "r+", "w+", "a+", "r+b", "w+b", "a+b", nullptr}; return _fsopen(filename, mods[mode], prot); } FILE* _Xfsopen(_In_z_ const wchar_t* filename, _In_ int mode, _In_ int prot) { static const wchar_t* const mods[] = {// fopen mode strings corresponding to valid[i] - L"r", L"w", L"w", L"a", L"rb", L"wb", L"wb", L"ab", L"r+", L"w+", L"a+", L"r+b", L"w+b", L"a+b", 0}; + L"r", L"w", L"w", L"a", L"rb", L"wb", L"wb", L"ab", L"r+", L"w+", L"a+", L"r+b", L"w+b", L"a+b", nullptr}; return _wfsopen(filename, mods[mode], prot); } diff --git a/stl/src/ios.cpp b/stl/src/ios.cpp index 73c12428252..9f15c44dc4a 100644 --- a/stl/src/ios.cpp +++ b/stl/src/ios.cpp @@ -22,7 +22,8 @@ __PURE_APPDOMAIN_GLOBAL int ios_base::_Index = 0; // initialize source of unique __PURE_APPDOMAIN_GLOBAL bool ios_base::_Sync = true; // initialize synchronization flag -__PURE_APPDOMAIN_GLOBAL static ios_base* stdstr[_Nstdstr + 2] = {0}; // [1, _Nstdstr] hold pointers to standard streams +__PURE_APPDOMAIN_GLOBAL static ios_base* stdstr[_Nstdstr + 2] = { + nullptr}; // [1, _Nstdstr] hold pointers to standard streams __PURE_APPDOMAIN_GLOBAL static char stdopens[_Nstdstr + 2] = {0}; // [1, _Nstdstr] hold open counts for standard streams // void __CLR_OR_THIS_CALL ios_base::clear(iostate state, bool reraise) { // set state, possibly reraise exception diff --git a/stl/src/taskscheduler.cpp b/stl/src/taskscheduler.cpp index 724bb60bfaa..105a7f05ec8 100644 --- a/stl/src/taskscheduler.cpp +++ b/stl/src/taskscheduler.cpp @@ -48,11 +48,11 @@ namespace Concurrency { // that as a failure to call. (void) _Flags; (void) _Addr; - return 0; + return nullptr; #else // ^^^ defined(_CRT_APP) ^^^ // vvv !defined(_CRT_APP) vvv HMODULE _Result; if (::GetModuleHandleExW(_Flags, _Addr, &_Result) == 0) { - return 0; + return nullptr; } return _Result; @@ -66,7 +66,7 @@ namespace Concurrency { return _STL_host_status::_Dll; #else // ^^^ CRTDLL2 ^^^ // vvv !CRTDLL2 vvv HANDLE _HExe = _Call_get_module_handle_ex(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nullptr); - if (_HExe == 0) { + if (_HExe == nullptr) { return _STL_host_status::_Unknown; } else if (_HExe == reinterpret_cast(&__ImageBase)) { return _STL_host_status::_Exe; @@ -133,7 +133,7 @@ namespace Concurrency { GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(_Chore->_M_callback)); - if (_Callback_dll != 0) { + if (_Callback_dll != nullptr) { __crtFreeLibraryWhenCallbackReturns(_Pci, _Callback_dll); } } diff --git a/stl/src/winapisupp.cpp b/stl/src/winapisupp.cpp index 9e51805873c..9275e307f8c 100644 --- a/stl/src/winapisupp.cpp +++ b/stl/src/winapisupp.cpp @@ -530,7 +530,7 @@ extern "C" void __cdecl __crtGetSystemTimePreciseAsFileTime(_Out_ LPFILETIME lpS #else // defined _ONECORE -extern "C" PVOID __KERNEL32Functions[eMaxKernel32Function] = {0}; +extern "C" PVOID __KERNEL32Functions[eMaxKernel32Function] = {nullptr}; static int __cdecl initialize_pointers() { HINSTANCE hKernel32 = GetModuleHandleW(L"kernel32.dll"); diff --git a/stl/src/xstoul.cpp b/stl/src/xstoul.cpp index 7e36fa0bf4b..c9a3214ed5b 100644 --- a/stl/src/xstoul.cpp +++ b/stl/src/xstoul.cpp @@ -74,7 +74,7 @@ _CRTIMP2_PURE unsigned long __CLRCALL_PURE_OR_CDECL _Stoulx( } x = 0; - for (s2 = sc, y = 0; (sd = static_cast(memchr(&digits[0], tolower(*sc), base))) != 0; + for (s2 = sc, y = 0; (sd = static_cast(memchr(&digits[0], tolower(*sc), base))) != nullptr; ++sc) { // accumulate digits y = x; dig = static_cast(sd - digits); // for overflow checking diff --git a/stl/src/xstoull.cpp b/stl/src/xstoull.cpp index 5875fb47809..2d04d149ee8 100644 --- a/stl/src/xstoull.cpp +++ b/stl/src/xstoull.cpp @@ -70,7 +70,7 @@ _CRTIMP2_PURE unsigned long long __CLRCALL_PURE_OR_CDECL _Stoullx( } x = 0; - for (s2 = sc, y = 0, dig = 0; (sd = static_cast(memchr(&digits[0], tolower(*sc), base))) != 0; + for (s2 = sc, y = 0, dig = 0; (sd = static_cast(memchr(&digits[0], tolower(*sc), base))) != nullptr; ++sc) { // accumulate digits y = x; dig = static_cast(sd - digits); // for overflow checking diff --git a/stl/src/xstoxflt.cpp b/stl/src/xstoxflt.cpp index 3c18095a71d..a4e33a6a6e7 100644 --- a/stl/src/xstoxflt.cpp +++ b/stl/src/xstoxflt.cpp @@ -42,7 +42,7 @@ _In_range_(0, maxsig) int _Stoxflt( seen = 1; } - while ((pd = static_cast(memchr(&digits[0], *s, 22))) != 0) { + while ((pd = static_cast(memchr(&digits[0], *s, 22))) != nullptr) { if (nsig <= maxsig) { buf[nsig++] = vals[pd - digits]; // accumulate a digit } else { @@ -63,7 +63,7 @@ _In_range_(0, maxsig) int _Stoxflt( } } - for (; (pd = static_cast(memchr(&digits[0], *s, 22))) != 0; ++s, seen = 1) { + for (; (pd = static_cast(memchr(&digits[0], *s, 22))) != nullptr; ++s, seen = 1) { if (nsig <= maxsig) { // accumulate a fraction digit buf[nsig++] = vals[pd - digits]; --lo[0]; diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 40698cb88b2..3e4254debc4 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -469,26 +469,6 @@ std/language.support/support.limits/support.limits.general/functional.version.pa std/language.support/support.limits/support.limits.general/iterator.version.pass.cpp FAIL std/language.support/support.limits/support.limits.general/memory.version.pass.cpp FAIL -# C++20 P1135R6 "The C++20 Synchronization Library" -std/thread/thread.barrier/arrive.pass.cpp FAIL -std/thread/thread.barrier/arrive_and_drop.pass.cpp FAIL -std/thread/thread.barrier/arrive_and_wait.pass.cpp FAIL -std/thread/thread.barrier/completion.pass.cpp FAIL -std/thread/thread.barrier/max.pass.cpp FAIL -std/thread/thread.barrier/version.pass.cpp FAIL -std/thread/thread.latch/arrive_and_wait.pass.cpp FAIL -std/thread/thread.latch/count_down.pass.cpp FAIL -std/thread/thread.latch/max.pass.cpp FAIL -std/thread/thread.latch/try_wait.pass.cpp FAIL -std/thread/thread.latch/version.pass.cpp FAIL -std/thread/thread.semaphore/acquire.pass.cpp FAIL -std/thread/thread.semaphore/binary.pass.cpp FAIL -std/thread/thread.semaphore/max.pass.cpp FAIL -std/thread/thread.semaphore/release.pass.cpp FAIL -std/thread/thread.semaphore/timed.pass.cpp FAIL -std/thread/thread.semaphore/try_acquire.pass.cpp FAIL -std/thread/thread.semaphore/version.pass.cpp FAIL - # *** MISSING COMPILER FEATURES *** # Nothing here! :-) @@ -509,9 +489,6 @@ std/thread/futures/futures.task/futures.task.members/make_ready_at_thread_exit.p # Compiler bug: DevCom-409222 "Constructing rvalue reference from non-reference-related lvalue reference" std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:0 FAIL -# Compiler bug: VSO-1035737 "constexpr pointers-by-reference" -std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.index/difference_type.pass.cpp:0 FAIL - # Compiler bug: DevCom-876860 "conditional operator errors" blocks readable. std/containers/views/span.cons/ptr_len.pass.cpp:0 FAIL std/containers/views/span.cons/ptr_ptr.pass.cpp:0 FAIL @@ -571,9 +548,6 @@ std/containers/sequences/array/array.data/data_const.pass.cpp FAIL std/containers/sequences/array/array.data/data.pass.cpp FAIL std/containers/sequences/array/iterators.pass.cpp FAIL -# STL bug: string_view doesn't enforce "non-array trivial standard-layout", related to LWG-3034. -std/strings/string.view/char.bad.fail.cpp:0 FAIL - # Predicate count assertions - IDL2 is slightly bending the Standard's rules here. std/algorithms/alg.sorting/alg.heap.operations/make.heap/make_heap_comp.pass.cpp FAIL std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp FAIL @@ -647,6 +621,19 @@ std/thread/thread.threads/thread.thread.class/thread.thread.member/join.pass.cpp # *** LIKELY BOGUS TESTS *** +# "error: _LIBCPP_VERSION not defined" +std/thread/thread.barrier/version.pass.cpp FAIL +std/thread/thread.latch/version.pass.cpp FAIL +std/thread/thread.semaphore/version.pass.cpp FAIL + +# "error C3861: 'assert': identifier not found" +std/thread/thread.semaphore/timed.pass.cpp FAIL +std/thread/thread.semaphore/try_acquire.pass.cpp FAIL + +# pass lambda without noexcept to barrier +std/thread/thread.barrier/completion.pass.cpp FAIL +std/thread/thread.barrier/max.pass.cpp FAIL + # Test bug/LEWG issue or STL bug. See GH-519 ": signbit() misses overloads for integer types". std/depr/depr.c.headers/math_h.pass.cpp FAIL std/numerics/c.math/cmath.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 2f3866059fb..bc5a21bba1f 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -469,26 +469,6 @@ language.support\support.limits\support.limits.general\functional.version.pass.c language.support\support.limits\support.limits.general\iterator.version.pass.cpp language.support\support.limits\support.limits.general\memory.version.pass.cpp -# C++20 P1135R6 "The C++20 Synchronization Library" -thread\thread.barrier\arrive.pass.cpp -thread\thread.barrier\arrive_and_drop.pass.cpp -thread\thread.barrier\arrive_and_wait.pass.cpp -thread\thread.barrier\completion.pass.cpp -thread\thread.barrier\max.pass.cpp -thread\thread.barrier\version.pass.cpp -thread\thread.latch\arrive_and_wait.pass.cpp -thread\thread.latch\count_down.pass.cpp -thread\thread.latch\max.pass.cpp -thread\thread.latch\try_wait.pass.cpp -thread\thread.latch\version.pass.cpp -thread\thread.semaphore\acquire.pass.cpp -thread\thread.semaphore\binary.pass.cpp -thread\thread.semaphore\max.pass.cpp -thread\thread.semaphore\release.pass.cpp -thread\thread.semaphore\timed.pass.cpp -thread\thread.semaphore\try_acquire.pass.cpp -thread\thread.semaphore\version.pass.cpp - # *** MISSING COMPILER FEATURES *** # Nothing here! :-) @@ -509,9 +489,6 @@ thread\futures\futures.task\futures.task.members\make_ready_at_thread_exit.pass. # Compiler bug: DevCom-409222 "Constructing rvalue reference from non-reference-related lvalue reference" utilities\meta\meta.unary\meta.unary.prop\is_constructible.pass.cpp -# Compiler bug: VSO-1035737 "constexpr pointers-by-reference" -iterators\predef.iterators\move.iterators\move.iter.ops\move.iter.op.index\difference_type.pass.cpp - # Compiler bug: DevCom-876860 "conditional operator errors" blocks readable. containers\views\span.cons\ptr_len.pass.cpp containers\views\span.cons\ptr_ptr.pass.cpp @@ -571,9 +548,6 @@ containers\sequences\array\array.data\data_const.pass.cpp containers\sequences\array\array.data\data.pass.cpp containers\sequences\array\iterators.pass.cpp -# STL bug: string_view doesn't enforce "non-array trivial standard-layout", related to LWG-3034. -strings\string.view\char.bad.fail.cpp - # Predicate count assertions - IDL2 is slightly bending the Standard's rules here. algorithms\alg.sorting\alg.heap.operations\make.heap\make_heap_comp.pass.cpp algorithms\alg.sorting\alg.merge\inplace_merge_comp.pass.cpp @@ -647,6 +621,19 @@ thread\thread.threads\thread.thread.class\thread.thread.member\join.pass.cpp # *** LIKELY BOGUS TESTS *** +# "error: _LIBCPP_VERSION not defined" +thread\thread.barrier\version.pass.cpp +thread\thread.latch\version.pass.cpp +thread\thread.semaphore\version.pass.cpp + +# "error C3861: 'assert': identifier not found" +thread\thread.semaphore\timed.pass.cpp +thread\thread.semaphore\try_acquire.pass.cpp + +# pass lambda without noexcept to barrier +thread\thread.barrier\completion.pass.cpp +thread\thread.barrier\max.pass.cpp + # Test bug/LEWG issue or STL bug. See GH-519 ": signbit() misses overloads for integer types". depr\depr.c.headers\math_h.pass.cpp numerics\c.math\cmath.pass.cpp diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 60fa690bc52..901f64822b6 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -110,7 +110,16 @@ namespace test { using _Prevent_inheriting_unwrap = sentinel; - using unwrap = sentinel; + using unwrap = sentinel; + using Constinel = sentinel; + + constexpr operator Constinel() && noexcept { + return Constinel{exchange(ptr_, nullptr)}; + } + + constexpr operator Constinel() const& noexcept { + return Constinel{ptr_}; + } // clang-format off [[nodiscard]] constexpr auto _Unwrapped() const noexcept requires (to_bool(Wrapped)) { @@ -340,6 +349,22 @@ namespace test { static constexpr bool at_least = derived_from; using ReferenceType = conditional_t, Element&>; + + struct post_increment_proxy { + Element* ptr_; + + const post_increment_proxy& operator*() const noexcept { + return *this; + } + + template + requires std::indirectly_writable + const post_increment_proxy& operator=(T&& t) const noexcept { + *ptr_ = std::forward(t); + return *this; + } + }; + public: using Consterator = iterator; @@ -385,10 +410,11 @@ namespace test { ++ptr_; return *this; } - constexpr iterator operator++(int) & noexcept { - auto tmp = *this; + + constexpr post_increment_proxy operator++(int) & noexcept requires std::is_same_v { + post_increment_proxy result{ptr_}; ++ptr_; - return tmp; + return result; } auto operator--() & { @@ -398,7 +424,7 @@ namespace test { STATIC_ASSERT(always_false); } - friend void iter_swap(iterator const&, iterator const&) { + friend void iter_swap(iterator const&, iterator const&) requires std::is_same_v { STATIC_ASSERT(always_false); } @@ -424,8 +450,16 @@ namespace test { return std::move(*i.ptr_); } - constexpr friend void iter_swap(iterator const& x, iterator const& y) requires at_least { - ranges::iter_swap(x.ptr_, y.ptr_); + constexpr friend void iter_swap(iterator const& x, iterator const& y) + noexcept(std::is_nothrow_swappable_v) requires at_least && std::swappable { + ranges::swap(*x.ptr_, *y.ptr_); + } + + // forward iterator operations: + constexpr iterator operator++(int) & noexcept requires at_least { + auto tmp = *this; + ++ptr_; + return tmp; } // sentinel operations (implied by forward iterator): @@ -468,9 +502,14 @@ namespace test { [[nodiscard]] constexpr boolish operator>=(iterator const& that) const noexcept requires at_least { return !(*this < that); } + [[nodiscard]] constexpr auto operator<=>(iterator const& that) const noexcept requires at_least { + return ptr_ <=> that.ptr_; + } + [[nodiscard]] constexpr ReferenceType operator[](ptrdiff_t const n) const& noexcept requires at_least { return ReferenceType{ptr_[n]}; } + constexpr iterator& operator+=(ptrdiff_t const n) & noexcept requires at_least { ptr_ += n; return *this; @@ -479,6 +518,7 @@ namespace test { ptr_ -= n; return *this; } + [[nodiscard]] constexpr iterator operator+(ptrdiff_t const n) const noexcept requires at_least { return iterator{ptr_ + n}; } @@ -486,6 +526,7 @@ namespace test { requires at_least { return i + n; } + [[nodiscard]] constexpr iterator operator-(ptrdiff_t const n) const noexcept requires at_least { return iterator{ptr_ - n}; } @@ -573,6 +614,93 @@ struct std::pointer_traits<::test::iterator + class range_base { + public: + static_assert(Copy == Copyability::immobile); + + range_base() = default; + constexpr explicit range_base(span elements) noexcept : elements_{elements} {} + + range_base(const range_base&) = delete; + range_base& operator=(const range_base&) = delete; + + protected: + [[nodiscard]] constexpr bool moved_from() const noexcept { + return false; + } + span elements_; + }; + + template + class range_base { + public: + range_base() = default; + constexpr explicit range_base(span elements) noexcept : elements_{elements} {} + + constexpr range_base(range_base&& that) noexcept + : elements_{that.elements_}, moved_from_{that.moved_from_} { + that.elements_ = {}; + that.moved_from_ = true; + } + + constexpr range_base& operator=(range_base&& that) noexcept { + elements_ = that.elements_; + moved_from_ = that.moved_from_; + that.elements_ = {}; + that.moved_from_ = true; + return *this; + } + + protected: + [[nodiscard]] constexpr bool moved_from() const noexcept { + return moved_from_; + } + + span elements_; + + private: + bool moved_from_ = false; + }; + + template + class range_base { + public: + range_base() = default; + constexpr explicit range_base(span elements) noexcept : elements_{elements} {} + + range_base(const range_base&) = default; + range_base& operator=(const range_base&) = default; + + constexpr range_base(range_base&& that) noexcept + : elements_{that.elements_}, moved_from_{that.moved_from_} { + that.elements_ = {}; + that.moved_from_ = true; + } + + constexpr range_base& operator=(range_base&& that) noexcept { + elements_ = that.elements_; + moved_from_ = that.moved_from_; + that.elements_ = {}; + that.moved_from_ = true; + return *this; + } + + protected: + [[nodiscard]] constexpr bool moved_from() const noexcept { + return moved_from_; + } + + span elements_; + + private: + bool moved_from_ = false; + }; + } // namespace detail // clang-format off template }, // Use a ProxyRef reference type? - ProxyRef Proxy = ProxyRef{!derived_from}> + ProxyRef Proxy = ProxyRef{!derived_from}, + // Should this range satisfy the view concept? + CanView IsView = CanView::no, + // Should this range type be copyable/movable/neither? + Copyability Copy = IsView == CanView::yes ? Copyability::move_only : Copyability::immobile> requires (!to_bool(IsCommon) || to_bool(Eq)) && (to_bool(Eq) || !derived_from) && (!to_bool(Proxy) || !derived_from) - class range { - span elements_; + && (!to_bool(IsView) || Copy != Copyability::immobile) + class range : public detail::range_base { + private: mutable bool begin_called_ = false; + using detail::range_base::elements_; + + using detail::range_base::moved_from; public: using I = iterator; using S = conditional_t>; - range() = default; - constexpr explicit range(span elements) noexcept : elements_{elements} {} - - range(range const&) = delete; - range& operator=(range const&) = delete; + using detail::range_base::range_base; [[nodiscard]] constexpr I begin() const noexcept { + assert(!moved_from()); if constexpr (!derived_from) { assert(!exchange(begin_called_, true)); } @@ -611,10 +744,12 @@ namespace test { } [[nodiscard]] constexpr S end() const noexcept { + assert(!moved_from()); return S{elements_.data() + elements_.size()}; } [[nodiscard]] constexpr ptrdiff_t size() const noexcept requires (to_bool(IsSized)) { + assert(!moved_from()); if constexpr (!derived_from) { assert(!begin_called_); } @@ -622,6 +757,7 @@ namespace test { } [[nodiscard]] constexpr Element* data() const noexcept requires derived_from { + assert(!moved_from()); return elements_.data(); } @@ -629,12 +765,14 @@ namespace test { using US = conditional_t>; [[nodiscard]] constexpr UI _Unchecked_begin() const noexcept { + assert(!moved_from()); if constexpr (!derived_from) { assert(!exchange(begin_called_, true)); } return UI{elements_.data()}; } [[nodiscard]] constexpr US _Unchecked_end() const noexcept { + assert(!moved_from()); return US{elements_.data() + elements_.size()}; } @@ -649,6 +787,11 @@ namespace test { // clang-format on } // namespace test +template +inline constexpr bool std::ranges::enable_view< + test::range> = true; + template class basic_borrowed_range : public test::range { @@ -1103,6 +1246,11 @@ constexpr void test_in_fwd() { with_input_ranges, Element1>::call(); } +template +constexpr void test_in_random() { + with_input_ranges, Element1>::call(); +} + template constexpr void test_fwd_fwd() { with_forward_ranges, Element1>::call(); @@ -1170,3 +1318,110 @@ struct get_nth_fn { }; inline constexpr get_nth_fn<0> get_first; inline constexpr get_nth_fn<1> get_second; + +template +concept CanBegin = requires(R&& r) { + ranges::begin(std::forward(r)); +}; +template +concept CanMemberBegin = requires(R&& r) { + std::forward(r).begin(); +}; + +template +concept CanEnd = requires(R&& r) { + ranges::end(std::forward(r)); +}; +template +concept CanMemberEnd = requires(R&& r) { + std::forward(r).end(); +}; + +template +concept CanCBegin = requires(R&& r) { + ranges::cbegin(std::forward(r)); +}; +template +concept CanCEnd = requires(R&& r) { + ranges::cend(std::forward(r)); +}; + +template +concept CanRBegin = requires(R&& r) { + ranges::rbegin(std::forward(r)); +}; +template +concept CanREnd = requires(R&& r) { + ranges::rend(std::forward(r)); +}; + +template +concept CanCRBegin = requires(R&& r) { + ranges::crbegin(std::forward(r)); +}; +template +concept CanCREnd = requires(R&& r) { + ranges::crend(std::forward(r)); +}; + +template +concept CanEmpty = requires(R&& r) { + ranges::empty(std::forward(r)); +}; + +template +concept CanSize = requires(R&& r) { + ranges::size(std::forward(r)); +}; +template +concept CanMemberSize = requires(R&& r) { + std::forward(r).size(); +}; + +template +concept CanSSize = requires(R&& r) { + ranges::ssize(std::forward(r)); +}; + +template +concept CanData = requires(R&& r) { + ranges::data(std::forward(r)); +}; +template +concept CanMemberData = requires(R&& r) { + std::forward(r).data(); +}; + +template +concept CanCData = requires(R&& r) { + ranges::cdata(std::forward(r)); +}; + +template +concept CanMemberBase = requires(T&& t) { + std::forward(t).base(); +}; + +template +concept CanMemberEmpty = requires(R&& r) { + std::forward(r).empty(); +}; + +template +concept CanMemberFront = requires(R&& r) { + std::forward(r).front(); +}; +template +concept CanMemberBack = requires(R&& r) { + std::forward(r).back(); +}; + +template +concept CanIndex = requires(R&& r, const ranges::range_difference_t i) { + std::forward(r)[i]; +}; + +template +concept CanBool = requires(R&& r) { + std::forward(r) ? true : false; +}; diff --git a/tests/std/test.lst b/tests/std/test.lst index 499a40f9004..2585db8b27f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -242,6 +242,8 @@ tests\P0768R1_spaceship_operator tests\P0769R2_shift_left_shift_right tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp +tests\P0896R4_common_iterator +tests\P0896R4_common_iterator_death tests\P0896R4_counted_iterator tests\P0896R4_counted_iterator_death tests\P0896R4_P1614R2_comparisons @@ -279,6 +281,8 @@ tests\P0896R4_ranges_alg_move tests\P0896R4_ranges_alg_move_backward tests\P0896R4_ranges_alg_none_of tests\P0896R4_ranges_alg_nth_element +tests\P0896R4_ranges_alg_partial_sort +tests\P0896R4_ranges_alg_partial_sort_copy tests\P0896R4_ranges_alg_partition tests\P0896R4_ranges_alg_partition_copy tests\P0896R4_ranges_alg_partition_point @@ -317,8 +321,16 @@ tests\P0896R4_ranges_ref_view tests\P0896R4_ranges_subrange tests\P0896R4_ranges_test_machinery tests\P0896R4_ranges_to_address +tests\P0896R4_views_all +tests\P0896R4_views_drop tests\P0896R4_views_empty +tests\P0896R4_views_filter +tests\P0896R4_views_filter_death +tests\P0896R4_views_reverse tests\P0896R4_views_single +tests\P0896R4_views_take +tests\P0896R4_views_transform +tests\P0896R4_views_transform_death tests\P0898R3_concepts tests\P0898R3_identity tests\P0912R5_coroutine @@ -329,6 +341,9 @@ tests\P1032R1_miscellaneous_constexpr tests\P1135R6_atomic_flag_test tests\P1135R6_atomic_wait tests\P1135R6_atomic_wait_vista +tests\P1135R6_barrier +tests\P1135R6_latch +tests\P1135R6_semaphore tests\P1165R1_consistently_propagating_stateful_allocators tests\P1423R3_char8_t_remediation tests\P1645R1_constexpr_numeric diff --git a/tests/std/tests/Dev11_0836436_get_time/test.cpp b/tests/std/tests/Dev11_0836436_get_time/test.cpp index 12b64dfc8ae..79d2dde40a9 100644 --- a/tests/std/tests/Dev11_0836436_get_time/test.cpp +++ b/tests/std/tests/Dev11_0836436_get_time/test.cpp @@ -314,216 +314,206 @@ void test_990695() { void test_locale_russian() { // Russian January in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x042f\x043d\x0432\x0430\x0440\x044c-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); - assert(read_date_locale(L"2020-\x044f\x043d\x0432-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); - assert(read_date_locale(L"2020-\x044f\x043d\x0412\x0410\x0440\x042c-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); - assert(read_date_locale(L"2020-\x042f\x041d\x0412-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x042f\x043d\x0432\x0430\x0440\x044c-05", "ru-RU") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x044f\x043d\x0432-05", "ru-RU") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x044f\x043d\x0412\x0410\x0440\x042c-05", "ru-RU") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x042f\x041d\x0412-05", "ru-RU") == make_tuple(5, 0, 120)); // Russian February in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0424\x0435\x0432\x0440\x0430\x043b\x044c-15", "ru_RU.UTF-8") - == make_tuple(15, 1, 120)); - assert(read_date_locale(L"2020-\x0444\x0435\x0432-15", "ru_RU.UTF-8") == make_tuple(15, 1, 120)); - assert(read_date_locale(L"2020-\x0444\x0435\x0412\x0440\x0410\x043b\x044c-15", "ru_RU.UTF-8") - == make_tuple(15, 1, 120)); - assert(read_date_locale(L"2020-\x0424\x0435\x0412-15", "ru_RU.UTF-8") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0424\x0435\x0432\x0440\x0430\x043b\x044c-15", "ru-RU") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0444\x0435\x0432-15", "ru-RU") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0444\x0435\x0412\x0440\x0410\x043b\x044c-15", "ru-RU") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0424\x0435\x0412-15", "ru-RU") == make_tuple(15, 1, 120)); // Russian March in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x041c\x0430\x0440\x0442-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); - assert(read_date_locale(L"2020-\x043c\x0430\x0440-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); - assert(read_date_locale(L"2020-\x041c\x0430\x0420\x0442-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); - assert(read_date_locale(L"2020-\x041c\x0430\x0420-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x041c\x0430\x0440\x0442-25", "ru-RU") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x043c\x0430\x0440-25", "ru-RU") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x041c\x0430\x0420\x0442-25", "ru-RU") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x041c\x0430\x0420-25", "ru-RU") == make_tuple(25, 2, 120)); // Russian April in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0410\x043f\x0440\x0435\x043b\x044c-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); - assert(read_date_locale(L"2020-\x0430\x043f\x0440-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); - assert(read_date_locale(L"2020-\x0410\x043f\x0420\x0415\x043b\x044c-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); - assert(read_date_locale(L"2020-\x0430\x041f\x0420-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0410\x043f\x0440\x0435\x043b\x044c-05", "ru-RU") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0430\x043f\x0440-05", "ru-RU") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0410\x043f\x0420\x0415\x043b\x044c-05", "ru-RU") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0430\x041f\x0420-05", "ru-RU") == make_tuple(5, 3, 120)); // Russian May in different cases (expanded, mixed cases) // Expanded and abbreviated versions are identical - assert(read_date_locale(L"2020-\x041c\x0430\x0439-15", "ru_RU.UTF-8") == make_tuple(15, 4, 120)); - assert(read_date_locale(L"2020-\x043c\x0410\x0419-15", "ru_RU.UTF-8") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x041c\x0430\x0439-15", "ru-RU") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x043c\x0410\x0419-15", "ru-RU") == make_tuple(15, 4, 120)); // Russian June in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0418\x044e\x043d\x044c-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); - assert(read_date_locale(L"2020-\x0438\x044e\x043d-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); - assert(read_date_locale(L"2020-\x0418\x044e\x041d\x042c-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); - assert(read_date_locale(L"2020-\x0438\x042e\x041d-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x043d\x044c-25", "ru-RU") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0438\x044e\x043d-25", "ru-RU") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x041d\x042c-25", "ru-RU") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0438\x042e\x041d-25", "ru-RU") == make_tuple(25, 5, 120)); // Russian July in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0418\x044e\x043b\x044c-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); - assert(read_date_locale(L"2020-\x0438\x044e\x043b-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); - assert(read_date_locale(L"2020-\x0418\x044e\x041b\x044c-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); - assert(read_date_locale(L"2020-\x0418\x044e\x041b-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x043b\x044c-12", "ru-RU") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0438\x044e\x043b-12", "ru-RU") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x041b\x044c-12", "ru-RU") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x041b-12", "ru-RU") == make_tuple(12, 6, 120)); // Russian August in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0410\x0432\x0433\x0443\x0441\x0442-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); - assert(read_date_locale(L"2020-\x0430\x0432\x0433-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); - assert(read_date_locale(L"2020-\x0410\x0432\x0433\x0423\x0421\x0442-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); - assert(read_date_locale(L"2020-\x0430\x0412\x0413-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0410\x0432\x0433\x0443\x0441\x0442-02", "ru-RU") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0430\x0432\x0433-02", "ru-RU") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0410\x0432\x0433\x0423\x0421\x0442-02", "ru-RU") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0430\x0412\x0413-02", "ru-RU") == make_tuple(2, 7, 120)); // Russian September in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0421\x0435\x043d\x0442\x044f\x0431\x0440\x044c-21", "ru_RU.UTF-8") + assert(read_date_locale(L"2020-\x0421\x0435\x043d\x0442\x044f\x0431\x0440\x044c-21", "ru-RU") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0441\x0435\x043d-21", "ru_RU.UTF-8") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0421\x0435\x043d\x0442\x044f\x0411\x0440\x044c-21", "ru_RU.UTF-8") + assert(read_date_locale(L"2020-\x0441\x0435\x043d-21", "ru-RU") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0421\x0435\x043d\x0442\x044f\x0411\x0440\x044c-21", "ru-RU") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0441\x0415\x041d-21", "ru_RU.UTF-8") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0441\x0415\x041d-21", "ru-RU") == make_tuple(21, 8, 120)); // Russian October in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x041e\x043a\x0442\x044f\x0431\x0440\x044c-01", "ru_RU.UTF-8") - == make_tuple(1, 9, 120)); - assert(read_date_locale(L"2020-\x043e\x043a\x0442-01", "ru_RU.UTF-8") == make_tuple(1, 9, 120)); - assert(read_date_locale(L"2020-\x041e\x043a\x0442\x044f\x0411\x0440\x044c-01", "ru_RU.UTF-8") - == make_tuple(1, 9, 120)); - assert(read_date_locale(L"2020-\x043e\x041a\x0442-01", "ru_RU.UTF-8") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x041e\x043a\x0442\x044f\x0431\x0440\x044c-01", "ru-RU") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x043e\x043a\x0442-01", "ru-RU") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x041e\x043a\x0442\x044f\x0411\x0440\x044c-01", "ru-RU") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x043e\x041a\x0442-01", "ru-RU") == make_tuple(1, 9, 120)); // Russian November in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x041d\x043e\x044f\x0431\x0440\x044c-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); - assert(read_date_locale(L"2020-\x043d\x043e\x044f-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); - assert(read_date_locale(L"2020-\x041d\x043e\x044f\x0411\x0440\x044c-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); - assert(read_date_locale(L"2020-\x043d\x041e\x042f-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x041d\x043e\x044f\x0431\x0440\x044c-09", "ru-RU") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x043d\x043e\x044f-09", "ru-RU") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x041d\x043e\x044f\x0411\x0440\x044c-09", "ru-RU") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x043d\x041e\x042f-09", "ru-RU") == make_tuple(9, 10, 120)); // Russian December in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0414\x0435\x043a\x0430\x0431\x0440\x044c-31", "ru_RU.UTF-8") - == make_tuple(31, 11, 120)); - assert(read_date_locale(L"2020-\x0434\x0435\x043a-31", "ru_RU.UTF-8") == make_tuple(31, 11, 120)); - assert(read_date_locale(L"2020-\x0414\x0435\x043a\x0430\x0411\x0440\x044c-31", "ru_RU.UTF-8") - == make_tuple(31, 11, 120)); - assert(read_date_locale(L"2020-\x0434\x0415\x043a-31", "ru_RU.UTF-8") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0414\x0435\x043a\x0430\x0431\x0440\x044c-31", "ru-RU") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0434\x0435\x043a-31", "ru-RU") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0414\x0435\x043a\x0430\x0411\x0440\x044c-31", "ru-RU") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0434\x0415\x043a-31", "ru-RU") == make_tuple(31, 11, 120)); } void test_locale_german() { // German January in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x004a\x0061\x006e\x0075\x0061\x0072-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); - assert(read_date_locale(L"2020-\x004a\x0061\x006e-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); - assert(read_date_locale(L"2020-\x004a\x0061\x006e\x0055\x0041\x0072-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); - assert(read_date_locale(L"2020-\x006a\x0041\x004e-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x004a\x0061\x006e\x0075\x0061\x0072-05", "de-DE") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x004a\x0061\x006e-05", "de-DE") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x004a\x0061\x006e\x0055\x0041\x0072-05", "de-DE") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x006a\x0041\x004e-05", "de-DE") == make_tuple(5, 0, 120)); // German February in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0046\x0065\x0062\x0072\x0075\x0061\x0072-15", "de_DE.utf-8") - == make_tuple(15, 1, 120)); - assert(read_date_locale(L"2020-\x0046\x0065\x0062-15", "de_DE.utf-8") == make_tuple(15, 1, 120)); - assert(read_date_locale(L"2020-\x0046\x0065\x0062\x0072\x0055\x0061\x0072-15", "de_DE.utf-8") - == make_tuple(15, 1, 120)); - assert(read_date_locale(L"2020-\x0066\x0045\x0062-15", "de_DE.utf-8") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0046\x0065\x0062\x0072\x0075\x0061\x0072-15", "de-DE") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0046\x0065\x0062-15", "de-DE") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0046\x0065\x0062\x0072\x0055\x0061\x0072-15", "de-DE") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0066\x0045\x0062-15", "de-DE") == make_tuple(15, 1, 120)); // German March in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x004d\x00e4\x0072\x007a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); - assert(read_date_locale(L"2020-\x004d\x0072\x007a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); - assert(read_date_locale(L"2020-\x006d\x00e4\x0052\x005a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); - assert(read_date_locale(L"2020-\x006d\x0052\x005a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x004d\x00e4\x0072\x007a-25", "de-DE") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x004d\x0072\x007a-25", "de-DE") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x006d\x00e4\x0052\x005a-25", "de-DE") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x006d\x0052\x005a-25", "de-DE") == make_tuple(25, 2, 120)); // German April in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0041\x0070\x0072\x0069\x006c-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); - assert(read_date_locale(L"2020-\x0041\x0070\x0072-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); - assert(read_date_locale(L"2020-\x0061\x0070\x0052\x0069\x004c-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); - assert(read_date_locale(L"2020-\x0061\x0050\x0052-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0041\x0070\x0072\x0069\x006c-05", "de-DE") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0041\x0070\x0072-05", "de-DE") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0061\x0070\x0052\x0069\x004c-05", "de-DE") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0061\x0050\x0052-05", "de-DE") == make_tuple(5, 3, 120)); // German May in different cases (expanded, mixed cases) // Expanded and abbreviated versions are identical - assert(read_date_locale(L"2020-\x004d\x0061\x0069-15", "de_DE.utf-8") == make_tuple(15, 4, 120)); - assert(read_date_locale(L"2020-\x006d\x0041\x0069-15", "de_DE.utf-8") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x004d\x0061\x0069-15", "de-DE") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x006d\x0041\x0069-15", "de-DE") == make_tuple(15, 4, 120)); // German June in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x004a\x0075\x006e\x0069-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); - assert(read_date_locale(L"2020-\x004a\x0075\x006e-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); - assert(read_date_locale(L"2020-\x006a\x0055\x004e\x0069-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); - assert(read_date_locale(L"2020-\x006a\x0055\x004e-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x004a\x0075\x006e\x0069-25", "de-DE") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x004a\x0075\x006e-25", "de-DE") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x006a\x0055\x004e\x0069-25", "de-DE") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x006a\x0055\x004e-25", "de-DE") == make_tuple(25, 5, 120)); // German July in different cases (expanded, mixed cases) // Expanded and abbreviated are identical - assert(read_date_locale(L"2020-\x004a\x0075\x006c\x0069-12", "de_DE.utf-8") == make_tuple(12, 6, 120)); - assert(read_date_locale(L"2020-\x004a\x0075\x004c\x0069-12", "de_DE.utf-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x004a\x0075\x006c\x0069-12", "de-DE") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x004a\x0075\x004c\x0069-12", "de-DE") == make_tuple(12, 6, 120)); // German August in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0041\x0075\x0067\x0075\x0073\x0074-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); - assert(read_date_locale(L"2020-\x0041\x0075\x0067-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); - assert(read_date_locale(L"2020-\x0061\x0075\x0047\x0075\x0053\x0074-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); - assert(read_date_locale(L"2020-\x0061\x0055\x0047-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0041\x0075\x0067\x0075\x0073\x0074-02", "de-DE") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0041\x0075\x0067-02", "de-DE") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0061\x0075\x0047\x0075\x0053\x0074-02", "de-DE") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0061\x0055\x0047-02", "de-DE") == make_tuple(2, 7, 120)); // German September in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0053\x0065\x0070\x0074\x0065\x006d\x0062\x0065\x0072-21", "de_DE.utf-8") + assert(read_date_locale(L"2020-\x0053\x0065\x0070\x0074\x0065\x006d\x0062\x0065\x0072-21", "de-DE") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0053\x0065\x0070-21", "de_DE.utf-8") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0073\x0045\x0070\x0054\x0065\x004d\x0062\x0065\x0072-21", "de_DE.utf-8") + assert(read_date_locale(L"2020-\x0053\x0065\x0070-21", "de-DE") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0073\x0045\x0070\x0054\x0065\x004d\x0062\x0065\x0072-21", "de-DE") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0053\x0065\x0070-21", "de_DE.utf-8") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0073\x0045\x0050-21", "de_DE.utf-8") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0053\x0065\x0070-21", "de-DE") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0073\x0045\x0050-21", "de-DE") == make_tuple(21, 8, 120)); // German October in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x004f\x006b\x0074\x006f\x0062\x0065\x0072-01", "de_DE.utf-8") - == make_tuple(1, 9, 120)); - assert(read_date_locale(L"2020-\x004f\x006b\x0074-01", "de_DE.utf-8") == make_tuple(1, 9, 120)); - assert(read_date_locale(L"2020-\x006f\x004b\x0074\x006f\x0042\x0065\x0052-01", "de_DE.utf-8") - == make_tuple(1, 9, 120)); - assert(read_date_locale(L"2020-\x006f\x004b\x0074-01", "de_DE.utf-8") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x004f\x006b\x0074\x006f\x0062\x0065\x0072-01", "de-DE") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x004f\x006b\x0074-01", "de-DE") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x006f\x004b\x0074\x006f\x0042\x0065\x0052-01", "de-DE") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x006f\x004b\x0074-01", "de-DE") == make_tuple(1, 9, 120)); // German November in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x004e\x006f\x0076\x0065\x006d\x0062\x0065\x0072-09", "de_DE.utf-8") + assert(read_date_locale(L"2020-\x004e\x006f\x0076\x0065\x006d\x0062\x0065\x0072-09", "de-DE") == make_tuple(9, 10, 120)); - assert(read_date_locale(L"2020-\x004e\x006f\x0076-09", "de_DE.utf-8") == make_tuple(9, 10, 120)); - assert(read_date_locale(L"2020-\x006e\x006f\x0056\x0065\x006d\x0042\x0065\x0052-09", "de_DE.utf-8") + assert(read_date_locale(L"2020-\x004e\x006f\x0076-09", "de-DE") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x006e\x006f\x0056\x0065\x006d\x0042\x0065\x0052-09", "de-DE") == make_tuple(9, 10, 120)); - assert(read_date_locale(L"2020-\x006e\x004f\x0056-09", "de_DE.utf-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x006e\x004f\x0056-09", "de-DE") == make_tuple(9, 10, 120)); // German December in different cases (expanded, abbreviated, mixed cases) - assert(read_date_locale(L"2020-\x0044\x0065\x007a\x0065\x006d\x0062\x0065\x0072-31", "de_DE.utf-8") + assert(read_date_locale(L"2020-\x0044\x0065\x007a\x0065\x006d\x0062\x0065\x0072-31", "de-DE") == make_tuple(31, 11, 120)); - assert(read_date_locale(L"2020-\x0044\x0065\x007a-31", "de_DE.utf-8") == make_tuple(31, 11, 120)); - assert(read_date_locale(L"2020-\x0064\x0065\x005a\x0065\x004d\x0062\x0045\x0072-31", "de_DE.utf-8") + assert(read_date_locale(L"2020-\x0044\x0065\x007a-31", "de-DE") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0064\x0065\x005a\x0065\x004d\x0062\x0045\x0072-31", "de-DE") == make_tuple(31, 11, 120)); - assert(read_date_locale(L"2020-\x0064\x0045\x005a-31", "de_DE.utf-8") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0064\x0045\x005a-31", "de-DE") == make_tuple(31, 11, 120)); } void test_locale_chinese() { // Chinese letters don't have distinct upper and lower cases // January in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x4e00\x6708-05", "zh-CN.utf-8") == make_tuple(5, 0, 120)); - assert(read_date_locale(L"2020-\x0031\x6708-05", "zh-CN.utf-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x4e00\x6708-05", "zh-CN") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x0031\x6708-05", "zh-CN") == make_tuple(5, 0, 120)); // February in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x4e8c\x6708-15", "zh-CN.utf-8") == make_tuple(15, 1, 120)); - assert(read_date_locale(L"2020-\x0032\x6708-15", "zh-CN.utf-8") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x4e8c\x6708-15", "zh-CN") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0032\x6708-15", "zh-CN") == make_tuple(15, 1, 120)); // March in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x4e09\x6708-25", "zh-CN.utf-8") == make_tuple(25, 2, 120)); - assert(read_date_locale(L"2020-\x0033\x6708-25", "zh-CN.utf-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x4e09\x6708-25", "zh-CN") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x0033\x6708-25", "zh-CN") == make_tuple(25, 2, 120)); // April in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x56db\x6708-05", "zh-CN.utf-8") == make_tuple(5, 3, 120)); - assert(read_date_locale(L"2020-\x0034\x6708-05", "zh-CN.utf-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x56db\x6708-05", "zh-CN") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0034\x6708-05", "zh-CN") == make_tuple(5, 3, 120)); // May in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x4e94\x6708-15", "zh-CN.utf-8") == make_tuple(15, 4, 120)); - assert(read_date_locale(L"2020-\x0035\x6708-15", "zh-CN.utf-8") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x4e94\x6708-15", "zh-CN") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x0035\x6708-15", "zh-CN") == make_tuple(15, 4, 120)); // June in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x516d\x6708-25", "zh-CN.utf-8") == make_tuple(25, 5, 120)); - assert(read_date_locale(L"2020-\x0036\x6708-25", "zh-CN.utf-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x516d\x6708-25", "zh-CN") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0036\x6708-25", "zh-CN") == make_tuple(25, 5, 120)); // July in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x4e03\x6708-12", "zh-CN.utf-8") == make_tuple(12, 6, 120)); - assert(read_date_locale(L"2020-\x0037\x6708-12", "zh-CN.utf-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x4e03\x6708-12", "zh-CN") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0037\x6708-12", "zh-CN") == make_tuple(12, 6, 120)); // August in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x516b\x6708-02", "zh-CN.utf-8") == make_tuple(2, 7, 120)); - assert(read_date_locale(L"2020-\x0038\x6708-02", "zh-CN.utf-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x516b\x6708-02", "zh-CN") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0038\x6708-02", "zh-CN") == make_tuple(2, 7, 120)); // September in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x4e5d\x6708-21", "zh-CN.utf-8") == make_tuple(21, 8, 120)); - assert(read_date_locale(L"2020-\x0039\x6708-21", "zh-CN.utf-8") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x4e5d\x6708-21", "zh-CN") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0039\x6708-21", "zh-CN") == make_tuple(21, 8, 120)); // October in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x5341\x6708-01", "zh-CN.utf-8") == make_tuple(1, 9, 120)); - assert(read_date_locale(L"2020-\x0031\x0030\x6708-01", "zh-CN.utf-8") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x5341\x6708-01", "zh-CN") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x0031\x0030\x6708-01", "zh-CN") == make_tuple(1, 9, 120)); // November in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x5341\x4e00\x6708-09", "zh-CN.utf-8") == make_tuple(9, 10, 120)); - assert(read_date_locale(L"2020-\x0031\x0031\x6708-09", "zh-CN.utf-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x5341\x4e00\x6708-09", "zh-CN") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x0031\x0031\x6708-09", "zh-CN") == make_tuple(9, 10, 120)); // December in Chinese (expanded and abbreviated) - assert(read_date_locale(L"2020-\x5341\x4e8c\x6708-31", "zh-CN.utf-8") == make_tuple(31, 11, 120)); - assert(read_date_locale(L"2020-\x0031\x0032\x6708-31", "zh-CN.utf-8") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x5341\x4e8c\x6708-31", "zh-CN") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0031\x0032\x6708-31", "zh-CN") == make_tuple(31, 11, 120)); } diff --git a/tests/std/tests/P0220R1_string_view/test.cpp b/tests/std/tests/P0220R1_string_view/test.cpp index 1d0fd5abba2..3e611eef4d1 100644 --- a/tests/std/tests/P0220R1_string_view/test.cpp +++ b/tests/std/tests/P0220R1_string_view/test.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include #include #include @@ -303,6 +304,24 @@ constexpr bool test_case_buffer_constructor() { return true; } +constexpr bool test_case_contiguous_constructor() { +#ifdef __cpp_lib_concepts + const array expectedData{'n', 'o', ' ', 'n', 'u', 'l', 'l'}; + // Also tests the corresponding deduction guide: + same_as auto sv = basic_string_view(expectedData.begin(), expectedData.end()); + assert(sv.data() == expectedData.data()); + assert(sv.size() == 7); + assert(sv.length() == 7); + assert(!sv.empty()); + assert(sv[1] == 'o'); + assert(sv.at(1) == 'o'); + assert(sv.front() == 'n'); + assert(sv.back() == 'l'); +#endif // __cpp_lib_concepts + + return true; +} + template constexpr bool test_case_iterators() { using iterator = typename basic_string_view::iterator; @@ -1028,6 +1047,7 @@ static_assert(c_string_view{"abcd"} == "abcd"); static_assert(test_case_default_constructor()); static_assert(test_case_ntcts_constructor()); static_assert(test_case_buffer_constructor()); +static_assert(test_case_contiguous_constructor()); #if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-284079 "C1XX's C++14 constexpr emits bogus warnings C4146, // C4308, C4307 for basic_string_view::iterator" static_assert(test_case_iterators()); @@ -1072,6 +1092,7 @@ int main() { test_case_default_constructor(); test_case_ntcts_constructor(); test_case_buffer_constructor(); + test_case_contiguous_constructor(); test_case_iterators>(); test_case_iterators>(); test_case_prefix>(); diff --git a/tests/std/tests/P0896R4_common_iterator/env.lst b/tests/std/tests/P0896R4_common_iterator/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_common_iterator/test.cpp b/tests/std/tests/P0896R4_common_iterator/test.cpp new file mode 100644 index 00000000000..317118c1144 --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator/test.cpp @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// clang-format off +template +concept CanDifference = requires(Iter it) { + { it - it }; +}; + +template +concept HasProxy = !is_reference_v>; +// clang-format on + +struct instantiator { + template + static constexpr void call() { + if constexpr (copyable) { + using ConstIter = typename Iter::Consterator; + using Sen = test::sentinel>; + using OSen = test::sentinel>; + using Cit = common_iterator; + using OCit = common_iterator; + P input[3] = {{0, 1}, {0, 2}, {0, 3}}; + + // [common.iter.types] + { + using iconcept = typename iterator_traits::iterator_concept; + if constexpr (forward_iterator) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as::iterator_concept, input_iterator_tag>); + } + + using icat = typename iterator_traits::iterator_category; + if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as); + } + + using ipointer = typename iterator_traits::pointer; + if constexpr (_Has_member_arrow) { + STATIC_ASSERT(same_as().operator->())>); + } else { + STATIC_ASSERT(same_as); + } + } + + { // [common.iter.const] + Cit defaultConstructed{}; + Cit iterConstructed{Iter{input}}; + Cit sentinelConstructed(Sen{}); + Cit copyConstructed{defaultConstructed}; + copyConstructed = iterConstructed; + + OCit conversionConstructed{defaultConstructed}; + conversionConstructed = iterConstructed; + + OCit conversionConstructedSentinel{sentinelConstructed}; + conversionConstructed = iterConstructed; + } + + { // [common.iter.access] + Cit iter{Iter{input}}; + assert(*iter == P(0, 1)); + assert(iter->first == 0); + assert(iter->second == 1); + if constexpr (HasProxy) { + // We return a proxy class here + static_assert(is_class_v())>); + } else { + // Either a pointer or the wrapped iterator + static_assert(!is_class_v())>); + } + + const Cit constIter{Iter{input}}; + assert(*constIter == P(0, 1)); + assert(constIter->first == 0); + assert(constIter->second == 1); + if constexpr (HasProxy) { + // We return a proxy class here + static_assert(is_class_v())>); + } else { + // Either a pointer or the wrapped iterator + static_assert(!is_class_v())>); + } + } + + { // [common.iter.nav] + Cit iter{Iter{input}}; + ++iter; + assert(*iter == P(0, 2)); + + assert(*iter++ == P(0, 2)); + assert(*iter == P(0, 3)); + } + + { // [common.iter.cmp] + // Compare iterator / iterator + assert(Cit{Iter{input}} == Cit{Iter{input}}); + assert(Cit{Iter{input}} != Cit{Iter{input + 1}}); + + // Compare iterator / sentinel + assert(Cit{Iter{input}} == Cit{Sen{input}}); + assert(Cit{Sen{input}} != Cit{Iter{input + 1}}); + + // Compare sentinel / sentinel + assert(Cit{Sen{input}} == Cit{Sen{input}}); + assert(Cit{Sen{input}} == Cit{Sen{input + 1}}); + + if constexpr (CanDifference) { + // Difference iterator / iterator + const same_as> auto diff_it_it = Cit{Iter{input}} - Cit{Iter{input + 1}}; + assert(diff_it_it == -1); + + // Difference iterator / sentinel + const same_as> auto diff_it_sen = Cit{Iter{input}} - Cit{Sen{input + 1}}; + const same_as> auto diff_sen_it = Cit{Sen{input + 1}} - Cit{Iter{input}}; + assert(diff_it_sen == -1); + assert(diff_sen_it == 1); + + // Difference sentinel / sentinel + const same_as> auto diff_sen_sen = Cit{Sen{input}} - Cit{Sen{input + 1}}; + assert(diff_sen_sen == 0); + + // Difference iterator / other iterator + const same_as> auto diff_it_oit = Cit{Iter{input}} - OCit{Iter{input + 1}}; + assert(diff_it_oit == -1); + + // Difference iterator / other sentinel + const same_as> auto diff_it_osen = Cit{Iter{input}} - OCit{OSen{input + 1}}; + assert(diff_it_osen == -1); + + // Difference other iterator / sentinel + const same_as> auto diff_sen_oit = Cit{Sen{input + 1}} - OCit{Iter{input}}; + assert(diff_sen_oit == 1); + + // Difference sentinel / other sentinel + const same_as> auto diff_sen_osen = Cit{Sen{input}} - OCit{OSen{input + 1}}; + assert(diff_sen_osen == 0); + } + } + + { // [common.iter.cust] + if constexpr (input_iterator) { // iter_move + Cit iter1{Iter{input}}; + + const same_as> auto element1 = ranges::iter_move(iter1); + assert(element1 == P(0, 1)); + } + + if constexpr (indirectly_swappable) { // iter_swap + Cit iter1{Iter{input}}; + Cit iter2{Iter{input + 1}}; + + ranges::iter_swap(iter1, iter2); + assert(*iter1 == P(0, 2)); + assert(*iter2 == P(0, 1)); + } + } + } + } +}; + +bool test_operator_arrow() { + P input[3] = {{0, 1}, {0, 2}, {0, 3}}; + + using pointerTest = common_iterator; + pointerTest pointerIter{input}; + + assert(*pointerIter == P(0, 1)); + assert(pointerIter->first == 0); + assert(pointerIter->second == 1); + static_assert(is_same_v()), P* const&>); + + using countedTest = common_iterator, default_sentinel_t>; + countedTest countedIter{counted_iterator{input, 3}}; + + assert(*countedIter == P(0, 1)); + assert(countedIter->first == 0); + assert(countedIter->second == 1); + static_assert(is_same_v()), P*>); + + return true; +} + +int main() { + with_writable_iterators::call(); + + test_operator_arrow(); +} diff --git a/tests/std/tests/P0896R4_common_iterator_death/env.lst b/tests/std/tests/P0896R4_common_iterator_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_common_iterator_death/test.cpp b/tests/std/tests/P0896R4_common_iterator_death/test.cpp new file mode 100644 index 00000000000..131b1fdf6cf --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator_death/test.cpp @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include + +#include +using namespace std; + +struct simple_input_iter { + using value_type = int; + using difference_type = int; + + value_type operator*() const { + return 0; + } + value_type operator->() const { + return 0; + } + simple_input_iter& operator++() { + return *this; + } + simple_input_iter operator++(int) { + return *this; + } + + bool operator==(const simple_input_iter&) const = default; + bool operator==(const default_sentinel_t&) const { + return true; + } + + difference_type operator-(const simple_input_iter&) const { + return 42; + } + friend difference_type operator-(const simple_input_iter&, const default_sentinel_t&) { + return 42; + } + friend difference_type operator-(const default_sentinel_t&, const simple_input_iter&) { + return 42; + } + + friend void iter_swap(const simple_input_iter&, const simple_input_iter&) {} +}; + +using CIT = common_iterator; + +void test_case_operator_dereference_sentinel() { + CIT cit{default_sentinel}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_dereference_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_dereference_const_sentinel() { + const CIT cit{default_sentinel}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_dereference_const_valueless() { + const CIT cit{_Common_iterator_construct_tag{}}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_arrow_sentinel() { + CIT cit{default_sentinel}; + (void) (cit.operator->()); // common_iterator can only be dereferenced if it holds an iterator +} +void test_case_operator_arrow_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + (void) (cit.operator->()); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_preincrement_sentinel() { + CIT cit{default_sentinel}; + ++cit; // common_iterator can only be preincremented if it holds an iterator +} + +void test_case_operator_preincrement_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + ++cit; // common_iterator can only be preincremented if it holds an iterator +} + +void test_case_operator_postincrement_sentinel() { + CIT cit{default_sentinel}; + cit++; // common_iterator can only be postincremented if it holds an iterator +} + +void test_case_operator_postincrement_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + cit++; // common_iterator can only be postincremented if it holds an iterator +} + +void test_case_equality_left_valueless() { + CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit2{}; + (void) (cit1 == cit2); // common_iterator can only be compared if it holds a value +} + +void test_case_equality_right_valueless() { + CIT cit1{}; + CIT cit2{_Common_iterator_construct_tag{}}; + (void) (cit1 == cit2); // common_iterator can only be compared if it holds a value +} + +void test_case_difference_left_valueless() { + CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit2{}; + (void) (cit1 - cit2); // common_iterator can only be subtracted if it holds a value +} + +void test_case_difference_right_valueless() { + CIT cit1{}; + CIT cit2{_Common_iterator_construct_tag{}}; + (void) (cit1 - cit2); // common_iterator can only be subtracted if it holds a value +} + +void test_case_iter_move_sentinel() { + CIT cit{default_sentinel}; + (void) ranges::iter_move(cit); // can only iter_move from common_iterator if it holds an iterator +} + +void test_case_iter_move_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + (void) ranges::iter_move(cit); // can only iter_move from common_iterator if it holds an iterator +} + +void test_case_iter_swap_sentinel_left_sentinel() { + CIT cit1{default_sentinel}; + CIT cit2{}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +void test_case_iter_swap_sentinel_left_valueless() { + CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit2{}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +void test_case_iter_swap_sentinel_right_sentinel() { + CIT cit1{}; + CIT cit2{default_sentinel}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +void test_case_iter_swap_sentinel_right_valueless() { + CIT cit1{}; + CIT cit2{_Common_iterator_construct_tag{}}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec([] {}); + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_case_operator_dereference_sentinel, + test_case_operator_dereference_valueless, + test_case_operator_dereference_const_sentinel, + test_case_operator_dereference_const_valueless, + test_case_operator_arrow_sentinel, + test_case_operator_arrow_valueless, + test_case_operator_preincrement_sentinel, + test_case_operator_preincrement_valueless, + test_case_operator_postincrement_sentinel, + test_case_operator_postincrement_valueless, + test_case_equality_left_valueless, + test_case_equality_right_valueless, + test_case_difference_left_valueless, + test_case_difference_right_valueless, + test_case_iter_move_sentinel, + test_case_iter_move_valueless, + test_case_iter_swap_sentinel_left_sentinel, + test_case_iter_swap_sentinel_left_valueless, + test_case_iter_swap_sentinel_right_sentinel, + test_case_iter_swap_sentinel_right_valueless, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp index 6927a887bf9..c61c58e6227 100644 --- a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp @@ -97,4 +97,6 @@ struct instantiator { } }; +#ifndef _PREFAST_ // TRANSITION, GH-1030 template void test_in_in(); +#endif // TRANSITION, GH-1030 diff --git a/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp b/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp index 916ac5f73f7..0bea140c685 100644 --- a/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp @@ -31,6 +31,13 @@ struct instantiator { } assert(result == ranges::end(output)); } + { // Validate int is properly converted to bool + bool output[] = {false, true, false}; + fill(ranges::begin(output), ranges::end(output), 5); + for (const bool& elem : output) { + assert(elem == true); + } + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp index ff51fa9f6b2..622794b11c3 100644 --- a/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp @@ -36,6 +36,13 @@ struct instantiator { assert(ranges::equal(output, expected_output)); assert(result == ranges::begin(output)); } + { // Validate int is properly converted to bool + bool output[] = {false, true, false}; + fill_n(ranges::begin(output), ranges::distance(output), 5); + for (const bool& elem : output) { + assert(elem == true); + } + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_partial_sort/env.lst b/tests/std/tests/P0896R4_ranges_alg_partial_sort/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_partial_sort/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_partial_sort/test.cpp b/tests/std/tests/P0896R4_ranges_alg_partial_sort/test.cpp new file mode 100644 index 00000000000..3023ee38e28 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_partial_sort/test.cpp @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{}, nullptr_to)), ranges::dangling>); +STATIC_ASSERT(same_as{}, nullptr_to)), int*>); + +struct instantiator { + static constexpr P sorted[] = {{0, 16}, {1, 12}, {2, 17}, {3, 13}, {4, 15}, {5, 11}, {6, 14}, {7, 10}}; + + template + static constexpr void call() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if (!ranges::contiguous_range || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::partial_sort, ranges::equal, ranges::iterator_t, ranges::less, ranges::next, ranges::size; + + { // Validate range overload + for (size_t i = 0; i <= size(sorted); ++i) { + P elements[] = {{7, 10}, {5, 11}, {1, 12}, {3, 13}, {6, 14}, {4, 15}, {0, 16}, {2, 17}}; + const R range{elements}; + const auto middle = next(range.begin(), static_cast(i)); + const same_as> auto result = partial_sort(range, middle, less{}, get_first); + assert(result == range.end()); + assert(equal(range.begin(), middle, sorted + 0, sorted + i)); + } + } + + { // Validate iterator overload + for (size_t i = 0; i <= size(sorted); ++i) { + P elements[] = {{7, 10}, {5, 11}, {1, 12}, {3, 13}, {6, 14}, {4, 15}, {0, 16}, {2, 17}}; + const R range{elements}; + const auto middle = next(range.begin(), static_cast(i)); + const same_as> auto result = + partial_sort(range.begin(), middle, range.end(), less{}, get_first); + assert(result == range.end()); + assert(equal(range.begin(), middle, sorted + 0, sorted + i)); + } + } + } + } +}; + +int main() { + STATIC_ASSERT((test_random(), true)); + test_random(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_partial_sort_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_partial_sort_copy/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_partial_sort_copy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_partial_sort_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_partial_sort_copy/test.cpp new file mode 100644 index 00000000000..c757fa93519 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_partial_sort_copy/test.cpp @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate that partial_sort_copy_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::partial_sort_copy_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::partial_sort_copy_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::partial_sort_copy_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::partial_sort_copy_result>); + +constexpr P source[] = {{5, 11}, {1, 12}, {3, 13}, {4, 15}, {0, 16}, {2, 17}}; +constexpr P expected[] = {{0, 16}, {1, 12}, {2, 17}, {3, 13}, {4, 15}, {5, 11}}; + +struct instantiator1 { + template + static constexpr void call() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if ((!ranges::contiguous_range && !ranges::contiguous_range) || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::partial_sort_copy, ranges::partial_sort_copy_result, ranges::equal, ranges::iterator_t, + ranges::less, ranges::min, ranges::next, ranges::size; + + P output[2 * size(source)]; + constexpr int sizes[] = {0, int{size(source) / 2}, int{size(source)}, int{2 * size(source)}}; + + { // Validate range overload + for (const int& i : sizes) { + In range1{source}; + Out range2{span{output}.first(static_cast(i))}; + const same_as, iterator_t>> auto result = + partial_sort_copy(range1, range2, less{}, get_first, get_first); + assert(result.in == range1.end()); + const auto n = min(i, int{size(source)}); + assert(result.out == range2.begin() + n); + assert(equal(range2.begin(), range2.begin() + n, expected, expected + n)); + } + + // also with empty input + In range1{}; + Out range2{output}; + const same_as, iterator_t>> auto result = + partial_sort_copy(range1, range2, less{}, get_first, get_first); + assert(result.in == range1.end()); + assert(result.out == range2.begin()); + } + } + } +}; + +struct instantiator2 { + template + static constexpr void call() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if ((!ranges::contiguous_range && !ranges::contiguous_range) || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::partial_sort_copy, ranges::partial_sort_copy_result, ranges::equal, ranges::iterator_t, + ranges::less, ranges::min, ranges::next, ranges::size; + + P output[2 * size(source)]; + constexpr int sizes[] = {0, int{size(source) / 2}, int{size(source)}, int{2 * size(source)}}; + + { // Validate iterator overload + for (const int& i : sizes) { + In range1{source}; + Out range2{span{output}.first(static_cast(i))}; + const same_as, iterator_t>> auto result = + partial_sort_copy( + range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_first); + assert(result.in == range1.end()); + const auto n = min(i, int{size(source)}); + assert(result.out == range2.begin() + n); + assert(equal(range2.begin(), range2.begin() + n, expected, expected + n)); + } + + // also with empty input + In range1{}; + Out range2{output}; + const same_as, iterator_t>> auto result = + partial_sort_copy( + range1.begin(), range1.end(), range2.begin(), range2.end(), less{}, get_first, get_first); + assert(result.in == range1.end()); + assert(result.out == range2.begin()); + } + } + } +}; + +#ifdef TEST_EVERYTHING +int main() { + // No constexpr tests - these overrun the compilers' constexpr step limits quickly + test_in_random(); + test_in_random(); +} +#else // ^^^ test all range combinations // test only interesting range combos vvv +constexpr void run_tests() { + using namespace test; + using test::iterator, test::range; + // The algorithm uses advance(i, s) in the input range, so it's slightly sensitive to that range's commonality + // and/or difference capability. We therefore test three kinds of source ranges: + using source_input = range; + using source_forward = + range; + using source_random = + range; + + // The result range must be random access - it's passed to various heap algorithm internals. Let's go ahead and use + // all 15 permutations of random access (or contiguous) ranges: + with_random_ranges::call(); + with_random_ranges::call(); + with_random_ranges::call(); + + with_random_ranges::call(); + with_random_ranges::call(); + with_random_ranges::call(); +} + +struct weird_pair : pair { + using pair::pair; + + weird_pair& operator=(const P& p) { + first = to_string(p.second); + second = to_string(p.first); + return *this; + } +}; + +int main() { + STATIC_ASSERT((run_tests(), true)); + run_tests(); + + { + constexpr auto proj = [](const weird_pair& s) { return stoi(s.second); }; + const weird_pair expected_result[] = {{"16", "0"}, {"12", "1"}, {"17", "2"}}; + weird_pair actual[ranges::size(expected_result)]; + + auto result = ranges::partial_sort_copy(source, actual, ranges::less{}, get_first, proj); + assert(result.in == ranges::end(source)); + assert(result.out == ranges::end(actual)); + + assert(ranges::equal(actual, expected_result)); + } +} +#endif // TEST_EVERYTHING diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 5289201f181..1605cb5ebbe 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -34,45 +34,10 @@ STATIC_ASSERT(std::same_as, std::size_t>); template concept Decayed = std::same_as, T>; -// clang-format off template -concept CanBegin = requires(R&& r) { ranges::begin(std::forward(r)); }; -template -concept CanEnd = requires(R&& r) { ranges::end(std::forward(r)); }; - -template -concept CanCBegin = requires(R&& r) { ranges::cbegin(std::forward(r)); }; -template -concept CanCEnd = requires(R&& r) {ranges::cend(std::forward(r)); }; - -template -concept CanRBegin = requires(R&& r) { ranges::rbegin(std::forward(r)); }; -template -concept CanREnd = requires(R&& r) { ranges::rend(std::forward(r)); }; - -template -concept CanCRBegin = requires(R&& r) { ranges::crbegin(std::forward(r)); }; -template -concept CanCREnd = requires(R&& r) { ranges::crend(std::forward(r)); }; - -template -concept CanEmpty = requires(R&& r) { ranges::empty(std::forward(r)); }; - -template -concept CanSize = requires(R&& r) { ranges::size(std::forward(r)); }; - -template -concept CanSSize = requires(R&& r) { ranges::ssize(std::forward(r)); }; - -template -concept CanSizeType = requires { typename ranges::range_size_t; }; - -template -concept CanData = requires(R&& r) { ranges::data(std::forward(r)); }; - -template -concept CanCData = requires(R&& r) { ranges::cdata(std::forward(r)); }; -// clang-format on +concept CanSizeType = requires { + typename ranges::range_size_t; +}; struct invalid_type {}; @@ -124,7 +89,13 @@ STATIC_ASSERT(test_cpo(ranges::empty)); STATIC_ASSERT(test_cpo(ranges::data)); STATIC_ASSERT(test_cpo(ranges::cdata)); +STATIC_ASSERT(test_cpo(ranges::views::all)); +STATIC_ASSERT(test_cpo(ranges::views::drop)); +STATIC_ASSERT(test_cpo(ranges::views::filter)); +STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); +STATIC_ASSERT(test_cpo(ranges::views::take)); +STATIC_ASSERT(test_cpo(ranges::views::transform)); void test_cpo_ambiguity() { using namespace std::ranges; diff --git a/tests/std/tests/P0896R4_ranges_subrange/test.cpp b/tests/std/tests/P0896R4_ranges_subrange/test.cpp index 961505a64ce..d3e67fbcc01 100644 --- a/tests/std/tests/P0896R4_ranges_subrange/test.cpp +++ b/tests/std/tests/P0896R4_ranges_subrange/test.cpp @@ -62,35 +62,6 @@ namespace test_view_interface { STATIC_ASSERT(test_template_id()); STATIC_ASSERT(test_template_id()); - template - concept CanEmpty = requires(T& t) { - ranges::empty(t); - }; - template - concept CanBool = requires(T& t) { - static_cast(t); - }; - template - concept CanData = requires(T& t) { - ranges::data(t); - }; - template - concept CanSize = requires(T& t) { - ranges::size(t); - }; - template - concept CanFront = requires(T& t) { - t.front(); - }; - template - concept CanBack = requires(T& t) { - t.back(); - }; - template - concept CanIndex = requires(T& t) { - t[42]; - }; - using test::CanCompare, test::CanDifference, test::Common, test::ProxyRef, test::to_bool; enum class ConstRange : bool { no, yes }; @@ -113,20 +84,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace output_unsized_onlymutable namespace output_unsized_allowconst { @@ -134,20 +105,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace output_unsized_allowconst namespace output_sized_onlymutable { @@ -155,20 +126,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace output_sized_onlymutable namespace output_sized_allowconst { @@ -176,20 +147,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace output_sized_allowconst namespace input_unsized_onlymutable { @@ -197,20 +168,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace input_unsized_onlymutable namespace input_unsized_allowconst { @@ -218,20 +189,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace input_unsized_allowconst namespace input_sized_onlymutable { @@ -239,20 +210,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace input_sized_onlymutable namespace input_sized_allowconst { @@ -260,20 +231,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace input_sized_allowconst namespace forward_uncommon_unsized_onlymutable { @@ -281,20 +252,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_uncommon_unsized_onlymutable namespace forward_uncommon_unsized_allowconst { @@ -302,20 +273,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_uncommon_unsized_allowconst namespace forward_uncommon_sized_onlymutable { @@ -323,20 +294,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_uncommon_sized_onlymutable namespace forward_uncommon_sized_allowconst { @@ -344,20 +315,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_uncommon_sized_allowconst namespace forward_common_unsized_onlymutable { @@ -365,20 +336,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_common_unsized_onlymutable namespace forward_common_unsized_allowconst { @@ -386,20 +357,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_common_unsized_allowconst namespace forward_common_sized_onlymutable { @@ -407,20 +378,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_common_sized_onlymutable namespace forward_common_sized_allowconst { @@ -428,20 +399,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace forward_common_sized_allowconst namespace bidi_uncommon_unsized_onlymutable { @@ -449,20 +420,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_uncommon_unsized_onlymutable namespace bidi_uncommon_unsized_allowconst { @@ -470,20 +441,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_uncommon_unsized_allowconst namespace bidi_uncommon_sized_onlymutable { @@ -491,20 +462,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_uncommon_sized_onlymutable namespace bidi_uncommon_sized_allowconst { @@ -512,20 +483,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_uncommon_sized_allowconst namespace bidi_common_unsized_onlymutable { @@ -533,20 +504,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_common_unsized_onlymutable namespace bidi_common_unsized_allowconst { @@ -554,20 +525,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_common_unsized_allowconst namespace bidi_common_sized_onlymutable { @@ -575,20 +546,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_common_sized_onlymutable namespace bidi_common_sized_allowconst { @@ -596,20 +567,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(!CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace bidi_common_sized_allowconst namespace random_uncommon_sized_onlymutable { @@ -617,20 +588,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace random_uncommon_sized_onlymutable namespace random_uncommon_sized_allowconst { @@ -638,20 +609,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanIndex); } // namespace random_uncommon_sized_allowconst namespace random_common_sized_onlymutable { @@ -659,20 +630,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace random_common_sized_onlymutable namespace random_common_sized_allowconst { @@ -680,20 +651,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanIndex); } // namespace random_common_sized_allowconst namespace contiguous_uncommon_sized_onlymutable { @@ -701,20 +672,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace contiguous_uncommon_sized_onlymutable namespace contiguous_uncommon_sized_allowconst { @@ -722,20 +693,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanData); - STATIC_ASSERT(CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanData); + STATIC_ASSERT(CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanIndex); } // namespace contiguous_uncommon_sized_allowconst namespace contiguous_common_sized_onlymutable { @@ -743,20 +714,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(!ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(!CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(!CanBool); - STATIC_ASSERT(CanData); - STATIC_ASSERT(!CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(!CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(!CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(!CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(!CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(!CanBool); + STATIC_ASSERT(CanData); + STATIC_ASSERT(!CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(!CanIndex); } // namespace contiguous_common_sized_onlymutable namespace contiguous_common_sized_allowconst { @@ -764,20 +735,20 @@ namespace test_view_interface { STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::range); STATIC_ASSERT(ranges::view); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanEmpty); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanBool); - STATIC_ASSERT(CanData); - STATIC_ASSERT(CanData); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanSize); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanFront); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(CanBack); - STATIC_ASSERT(CanIndex); - STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanEmpty); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanBool); + STATIC_ASSERT(CanData); + STATIC_ASSERT(CanData); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanSize); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberFront); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(CanMemberBack); + STATIC_ASSERT(CanIndex); + STATIC_ASSERT(CanIndex); } // namespace contiguous_common_sized_allowconst } // namespace test_view_interface diff --git a/tests/std/tests/P0896R4_views_all/env.lst b/tests/std/tests/P0896R4_views_all/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_views_all/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_all/test.cpp b/tests/std/tests/P0896R4_views_all/test.cpp new file mode 100644 index 00000000000..b44020a9344 --- /dev/null +++ b/tests/std/tests/P0896R4_views_all/test.cpp @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +template +concept CanViewAll = requires(Rng&& r) { + views::all(static_cast(r)); +}; + +// Test a silly precomposed range adaptor pipeline +constexpr auto pipeline = views::all | views::all | views::all | views::all | views::all | views::all; + +template +constexpr bool test_one(Rng&& rng) { + constexpr bool is_view = ranges::view>; + static_assert(is_view || is_lvalue_reference_v); + + using V = conditional_t, ranges::ref_view>>; + + static_assert(CanViewAll == (!is_view || copyable) ); + if constexpr (!is_view || copyable) { // Validate lvalue + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + static_assert(same_as, V>); + static_assert(same_as); + static_assert(noexcept(views::all(rng)) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | views::all | views::all | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | pipeline) == is_noexcept); + } + + static_assert(CanViewAll&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + static_assert(same_as&>, V>); + static_assert(same_as); + static_assert(noexcept(views::all(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::all | views::all | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RV = ranges::ref_view>; + + static_assert(same_as&>, RV>); + static_assert(same_as); + static_assert(noexcept(views::all(as_const(rng)))); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::all)); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::all | views::all | views::all)); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | pipeline)); + } + + // Validate rvalue + static_assert(CanViewAll> == is_view || ranges::enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + static_assert(same_as>, V>); + static_assert(same_as); + static_assert(noexcept(views::all(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::all | views::all | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (ranges::enable_borrowed_range) { + using S = decltype(ranges::subrange{declval()}); + constexpr bool is_noexcept = noexcept(S{declval()}); + + static_assert(same_as>, S>); + static_assert(same_as); + static_assert(noexcept(views::all(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::all | views::all | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // Validate const rvalue + static_assert(CanViewAll> == (is_view && copyable) + || (!is_view && ranges::enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + static_assert(same_as>, V>); + static_assert(same_as); + static_assert(noexcept(views::all(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::all | views::all | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && ranges::enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + constexpr bool is_noexcept = noexcept(S{declval>()}); + + static_assert(same_as>, S>); + static_assert(same_as); + static_assert(noexcept(views::all(move(as_const(rng)))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::all | views::all | views::all) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + return true; +} + +struct non_view_borrowed_range { + int* begin() const; + int* end() const; +}; + +template <> +inline constexpr bool ranges::enable_borrowed_range = true; + +template > +using move_only_view = test::range}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +int main() { + static constexpr int some_ints[] = {0, 1, 2}; + + // Validate views + { // ... copyable + constexpr string_view str{"Hello, World!"}; + static_assert(test_one(str)); + test_one(str); + assert(ranges::equal(views::all(str), str)); + } + { // ... move-only + test_one(move_only_view{some_ints}); + assert(ranges::equal(views::all(move_only_view{some_ints}), some_ints)); + test_one(move_only_view{some_ints}); + assert(ranges::equal(views::all(move_only_view{some_ints}), some_ints)); + test_one(move_only_view{some_ints}); + assert( + ranges::equal(views::all(move_only_view{some_ints}), some_ints)); + test_one(move_only_view{some_ints}); + assert(ranges::equal( + views::all(move_only_view{some_ints}), some_ints)); + test_one(move_only_view{some_ints}); + assert(ranges::equal( + views::all(move_only_view{some_ints}), some_ints)); + test_one(move_only_view{some_ints}); + assert(ranges::equal( + views::all(move_only_view{some_ints}), some_ints)); + test_one(move_only_view{some_ints}); + assert(ranges::equal( + views::all(move_only_view{some_ints}), some_ints)); + } + + // Validate non-views + { + static_assert(test_one(some_ints)); + test_one(some_ints); + assert(ranges::equal(views::all(some_ints), some_ints)); + } + { + string str{"Hello, World!"}; + test_one(str); + assert(ranges::equal(views::all(str), str)); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + static_assert(test_one(s)); + test_one(s); + } +} diff --git a/tests/std/tests/P0896R4_views_drop/env.lst b/tests/std/tests/P0896R4_views_drop/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_drop/test.cpp b/tests/std/tests/P0896R4_views_drop/test.cpp new file mode 100644 index 00000000000..aae2ce6f41b --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop/test.cpp @@ -0,0 +1,510 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#pragma warning(disable : 6011) // Dereferencing NULL pointer '%s' + +// Test a silly precomposed range adaptor pipeline +constexpr auto pipeline = views::drop(1) | views::drop(1) | views::drop(1) | views::drop(1); + +template +inline constexpr bool is_empty_view = false; +template +inline constexpr bool is_empty_view> = true; + +template +inline constexpr bool is_dynamic_span = false; +template +inline constexpr bool is_dynamic_span> = true; + +template +inline constexpr bool is_string_view = false; +template +inline constexpr bool is_string_view> = true; + +template +inline constexpr bool is_subrange = false; +template +inline constexpr bool is_subrange> = true; + +// clang-format off +template +concept reconstructible = ranges::random_access_range + && ranges::sized_range + && (is_empty_view + || is_dynamic_span + || is_string_view + // || is_iota_view // TRANSITION, iota_view + || is_subrange); +// clang-format on + +template +using mapped_t = conditional_t, V, ranges::drop_view>; + +template +using pipeline_t = mapped_t>>>>; + +template +concept CanViewDrop = requires(Rng&& r) { + views::drop(static_cast(r), 42); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::drop_view, ranges::common_range, ranges::enable_borrowed_range, ranges::iterator_t, ranges::prev, + ranges::range, ranges::sentinel_t, ranges::sized_range; + using ranges::input_range, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range, + ranges::contiguous_range; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using M = mapped_t; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(common_range == common_range); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto drop_four = views::drop(4); + + // ... with lvalue argument + STATIC_ASSERT(CanViewDrop == (!is_view || copyable) ); + if constexpr (CanViewDrop) { // Validate lvalue + constexpr bool is_noexcept = !is_view || (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(rng, 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewDrop&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = mapped_t&>>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, int>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewDrop> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v && !is_subrange; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = drop_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewDrop> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = drop_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(move(as_const(rng)), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + const bool is_empty = ranges::empty(expected); + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as> auto r = drop_view{forward(rng), 4}; + using R = decltype(r); + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate drop_view::size + STATIC_ASSERT(CanMemberSize == CanSize); + if constexpr (CanMemberSize) { + assert(r.size() == static_cast(ranges::size(expected))); + } else { + STATIC_ASSERT(!CanSize); + } + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(r) == is_empty); + assert(static_cast(r) == !is_empty); + } + } + + STATIC_ASSERT(CanMemberEmpty == (random_access_range && sized_range) ); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(as_const(r)) == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate content + assert(ranges::equal(r, expected)); + + // Validate drop_view::begin + STATIC_ASSERT(CanMemberBegin); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(CanBegin == (random_access_range && sized_range) ); + if (forward_range) { // intentionally not if constexpr + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (random_access_range && sized_range) { + STATIC_ASSERT(same_as, iterator_t>); + const same_as> auto i3 = as_const(r).begin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Validate drop_view::end + STATIC_ASSERT(CanMemberEnd); + STATIC_ASSERT(same_as, sentinel_t>); + STATIC_ASSERT(CanEnd == (random_access_range && sized_range) ); + if (!is_empty) { + same_as> auto i = r.end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i) == *prev(end(expected))); + } + + if constexpr (random_access_range && sized_range) { + same_as> auto i2 = as_const(r).end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i2) == *prev(end(expected))); + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(CanMemberData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + STATIC_ASSERT(CanData == (contiguous_range && sized_range) ); + if constexpr (contiguous_range) { + const same_as>*> auto ptr1 = r.data(); + assert(to_address(ptr1) == to_address(r.begin())); + + if constexpr (CanData) { + const same_as>*> auto ptr2 = as_const(r).data(); + assert(to_address(ptr2) == to_address(as_const(r).begin())); + } + } + + // Validate view_interface::front and back + if (!is_empty) { + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (forward_range) { + assert(r.front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberFront == (random_access_range && sized_range) ); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + STATIC_ASSERT( + CanMemberBack == (common_range && random_access_range && sized_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + STATIC_ASSERT(CanIndex == (random_access_range && sized_range) ); + if (!is_empty) { + if constexpr (CanIndex) { + assert(r[0] == *r.begin()); + } + if constexpr (CanIndex) { + assert(as_const(r)[0] == *as_const(r).begin()); + } + } + + // Validate drop_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (CanMemberBase && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end()) == 7); // NB: depends on the test data + } + } + } + + // Validate drop_view::base() && (NB: do this last since it leaves r moved-from) + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end()) == 7); // NB: depends on the test data + } + } + } + + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int only_four_ints[] = {4, 5, 6, 7}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, only_four_ints); + + R empty_range{}; + test_one(empty_range, span{}); + } +}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness. + using test::Common, test::Sized; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template +using move_only_view = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +constexpr void move_only_test() { + using test::Common, test::Sized; + using input = input_iterator_tag; + using fwd = forward_iterator_tag; + using bidi = bidirectional_iterator_tag; + using random = random_access_iterator_tag; + using contiguous = contiguous_iterator_tag; + + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); +} + +constexpr void output_range_test() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 + if (!is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using R = test::range; + int some_writable_ints[] = {0, 1, 2, 3}; + STATIC_ASSERT(same_as>); + ranges::fill(R{some_writable_ints} | views::drop(2), 42); + assert(ranges::equal(some_writable_ints, initializer_list{0, 1, 42, 42})); + } +} + +int main() { + // Validate views + { // ... copyable + // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + + STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); + test_one(ranges::subrange{some_ints}, only_four_ints); + + STATIC_ASSERT(test_one(views::empty, span{})); + test_one(views::empty, span{}); + + STATIC_ASSERT(test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints)); + test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints); + + // TRANSITION, iota_view + // STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); + // test_one(ranges::iota_view{0, 8}, only_four_ints); + } + // ... move-only + STATIC_ASSERT((move_only_test(), true)); + move_only_test(); + + // Validate non-views + { + STATIC_ASSERT(test_one(some_ints, only_four_ints)); + test_one(some_ints, only_four_ints); + } + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, only_four_ints); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, only_four_ints); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + } + + // Validate an output range + STATIC_ASSERT((output_range_test(), true)); + output_range_test(); + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_filter/env.lst b/tests/std/tests/P0896R4_views_filter/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_filter/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_filter/test.cpp b/tests/std/tests/P0896R4_views_filter/test.cpp new file mode 100644 index 00000000000..d28f5addcc3 --- /dev/null +++ b/tests/std/tests/P0896R4_views_filter/test.cpp @@ -0,0 +1,383 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +// Test a silly precomposed range adaptor pipeline +constexpr auto is_even = [](const auto& x) { return x % 2 == 0; }; +using Pred = remove_const_t; +STATIC_ASSERT(is_nothrow_copy_constructible_v&& is_nothrow_move_constructible_v); + +constexpr auto pipeline = + views::filter(is_even) | views::filter(is_even) | views::filter(is_even) | views::filter(is_even); + +template > +using pipeline_t = + ranges::filter_view, Pred>, Pred>, Pred>; + +template +concept CanViewFilter = requires(Rng&& r) { + views::filter(static_cast(r), is_even); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::filter_view, ranges::common_range, ranges::bidirectional_range, ranges::enable_borrowed_range, + ranges::forward_range, ranges::iterator_t, ranges::prev; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using F = filter_view; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(!ranges::random_access_range); + STATIC_ASSERT(!ranges::contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto filter_even = views::filter(is_even); + + // ... with lvalue argument + STATIC_ASSERT(CanViewFilter == (!is_view || copyable) ); + if constexpr (CanViewFilter) { // Validate lvalue + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::filter(rng, is_even)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | filter_even) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewFilter&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::filter(as_const(rng), is_even)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | filter_even) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = filter_view&>, Pred>; + constexpr bool is_noexcept = + is_nothrow_constructible_v&, decltype((is_even))>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::filter(as_const(rng), is_even)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | filter_even) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewFilter> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::filter(move(rng), is_even)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | filter_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = filter_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::filter(move(rng), is_even)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | filter_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewFilter> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::filter(as_const(rng), is_even)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | filter_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = filter_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::filter(move(as_const(rng)), is_even)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | filter_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as auto r = filter_view{forward(rng), is_even}; + assert(ranges::equal(r, expected)); + if constexpr (forward_range) { + // filter_view memoizes the first iterator, let's repeat a few times for coverage. + assert(ranges::equal(r, expected)); + assert(ranges::equal(r, expected)); + assert(ranges::equal(r, expected)); + } + + { // Validate filter_view::pred + [[maybe_unused]] same_as auto pred_copy = as_const(r).pred(); + STATIC_ASSERT(noexcept(as_const(r).pred())); + } + + // Validate view_interface::empty and operator bool + const bool is_empty = ranges::empty(expected); + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanMemberEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } + STATIC_ASSERT(!CanMemberEmpty); + STATIC_ASSERT(!CanBool); + + // Validate filter_view::begin + STATIC_ASSERT(CanMemberBegin); + if (forward_range) { // intentionally not if constexpr + // Ditto "let's make some extra calls because memoization" + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + assert(*r.begin() == *begin(expected)); + assert(*r.begin() == *begin(expected)); + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + assert(*r2.begin() == *i2); + assert(*r2.begin() == *i2); + if (!is_empty) { + assert(*i2 == *i); + } + } + + STATIC_ASSERT(!CanBegin); + } + + // Validate filter_view::end + STATIC_ASSERT(CanMemberEnd); + if (!is_empty) { + if constexpr (common_range) { + same_as> auto i = r.end(); + if constexpr (bidirectional_range) { + assert(*prev(i) == *prev(end(expected))); + } + } else { + [[maybe_unused]] same_as> auto s = r.end(); + } + + if constexpr (bidirectional_range && common_range && copyable) { + auto r2 = r; + assert(*prev(r2.end()) == *prev(end(expected))); + } + + STATIC_ASSERT(!CanEnd); + } + + // Validate view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Validate view_interface::size + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + + // Validate view_interface::operator[] + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); + + if (!is_empty) { + // Validate view_interface::front and back + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (forward_range) { + assert(r.front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + } + + // Validate filter_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == *begin(expected)); + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end(), 2) == *prev(end(expected))); // NB: depends on the test data + } + } + } + + // Validate filter_view::base() && (NB: do this last since it leaves r moved-from) +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == *begin(expected)); + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end(), 2) == *prev(end(expected))); // NB: depends on the test data + } + } + } + + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int only_even_ints[] = {0, 2, 4, 6}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, only_even_ints); + } +}; + +template +using test_range = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category and commonality, but oblivious to size, differencing, and proxyness. + using test::Common; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template > +using move_only_view = test::range}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +int main() { + // Validate views + { // ... copyable + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_even_ints)); + test_one(s, only_even_ints); + } + { // ... move-only + test_one(move_only_view{some_ints}, only_even_ints); + test_one(move_only_view{some_ints}, only_even_ints); + test_one(move_only_view{some_ints}, only_even_ints); + test_one(move_only_view{some_ints}, only_even_ints); + test_one(move_only_view{some_ints}, only_even_ints); + test_one(move_only_view{some_ints}, only_even_ints); + test_one(move_only_view{some_ints}, only_even_ints); + } + + // Validate non-views + { + STATIC_ASSERT(test_one(some_ints, only_even_ints)); + test_one(some_ints, only_even_ints); + } + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, only_even_ints); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, only_even_ints); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_even_ints)); + test_one(s, only_even_ints); + } + + // filter/reverse interaction test + { + auto fr_pipe = views::filter(is_even) | views::reverse; + auto rf_pipe = views::reverse | views::filter(is_even); + + auto r0 = some_ints | fr_pipe; + using R0 = decltype(r0); + STATIC_ASSERT(ranges::bidirectional_range && ranges::view); + assert(ranges::equal(r0, views::reverse(only_even_ints))); + + auto r1 = some_ints | rf_pipe; + using R1 = decltype(r1); + STATIC_ASSERT(ranges::bidirectional_range && ranges::view); + assert(ranges::equal(r1, views::reverse(only_even_ints))); + + assert(ranges::equal(r0, r1)); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_filter_death/env.lst b/tests/std/tests/P0896R4_views_filter_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0896R4_views_filter_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_filter_death/test.cpp b/tests/std/tests/P0896R4_views_filter_death/test.cpp new file mode 100644 index 00000000000..01c4017ca98 --- /dev/null +++ b/tests/std/tests/P0896R4_views_filter_death/test.cpp @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include + +#include +using namespace std; + +static int some_ints[] = {0, 1, 2, 3}; + +[[maybe_unused]] constexpr auto lambda = [x = 42](int) { return x == 42; }; +using FV = decltype(ranges::filter_view{some_ints, lambda}); + +void test_view_predicate() { + FV r; + (void) r.pred(); // value-initialized filter_view has no predicate +} + +void test_view_begin() { + FV r; + (void) r.begin(); // N4861 [range.filter.view]/3 forbids calling begin on a filter_view that holds no predicate +} + +void test_constructor_wrong_range() { + vector vec0{0, 1, 2, 3}; + vector vec1{4, 5, 6, 7}; + auto r0 = views::filter(vec0, lambda); + using R = decltype(r0); + same_as auto r1 = views::filter(vec1, lambda); + ranges::iterator_t i{r0, r1.begin().base()}; // vector iterators in range are from different containers +} + +void test_operator_star_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) (*i); // cannot dereference value-initialized filter_view iterator +} + +void test_operator_star_end_iterator() { + FV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + (void) (*i); // cannot dereference end filter_view iterator +} + +void test_operator_arrow_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) (i.operator->()); // cannot dereference value-initialized filter_view iterator +} + +void test_operator_arrow_end_iterator() { + FV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + (void) (i.operator->()); // cannot dereference end filter_view iterator +} + +void test_operator_preincrement_value_initialized_iterator() { + ranges::iterator_t i{}; + ++i; // cannot increment value-initialized filter_view iterator +} + +void test_operator_preincrement_after_end() { + FV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + ++i; // cannot increment filter_view iterator past end +} + +void test_operator_postincrement_value_initialized_iterator() { + ranges::iterator_t i{}; + i++; // cannot increment value-initialized filter_view iterator +} + +void test_operator_postincrement_after_end() { + FV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + i++; // cannot increment filter_view iterator past end +} + +void test_operator_predecrement_before_begin() { + FV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + --i; // cannot decrement filter_view iterator before begin +} + +void test_operator_postdecrement_before_begin() { + FV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + i--; // cannot decrement filter_view iterator before begin +} + +void test_operator_equal_incompatible_different() { + FV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + FV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 == i1); // cannot compare incompatible filter_view iterators for equality +} + +void test_operator_equal_incompatible_value_initialized() { + FV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i == ranges::iterator_t{}); // cannot compare incompatible filter_view iterators for equality +} + +void test_iter_move_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) ranges::iter_move(i); // cannot dereference value-initialized filter_view iterator +} + +void test_iter_swap_value_initialized_iterators() { + ranges::iterator_t i0{}; + ranges::iterator_t i1{}; + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized filter_view iterator +} + +void test_iter_swap_value_initialized_iterator_left() { + ranges::iterator_t i0{}; + FV r{some_ints, lambda}; + ranges::iterator_t i1 = r.begin(); + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized filter_view iterator +} + +void test_iter_swap_value_initialized_iterator_right() { + FV r{some_ints, lambda}; + ranges::iterator_t i0 = r.begin(); + ranges::iterator_t i1{}; + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized filter_view iterator +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec([] {}); + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_view_predicate, + test_view_begin, + + test_constructor_wrong_range, + test_operator_star_value_initialized_iterator, + test_operator_star_end_iterator, + test_operator_arrow_value_initialized_iterator, + test_operator_arrow_end_iterator, + test_operator_preincrement_value_initialized_iterator, + test_operator_preincrement_after_end, + test_operator_postincrement_value_initialized_iterator, + test_operator_postincrement_after_end, + test_operator_predecrement_before_begin, + test_operator_postdecrement_before_begin, + test_operator_equal_incompatible_different, + test_operator_equal_incompatible_value_initialized, + test_iter_move_value_initialized_iterator, + test_iter_swap_value_initialized_iterators, + test_iter_swap_value_initialized_iterator_left, + test_iter_swap_value_initialized_iterator_right, + }); +#else // ^^^ test everything / test only _CONTAINER_DEBUG_LEVEL cases vvv + exec.add_death_tests({ + test_view_predicate, + test_view_begin, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P0896R4_views_reverse/env.lst b/tests/std/tests/P0896R4_views_reverse/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_reverse/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_reverse/test.cpp b/tests/std/tests/P0896R4_views_reverse/test.cpp new file mode 100644 index 00000000000..c21a1f5bed2 --- /dev/null +++ b/tests/std/tests/P0896R4_views_reverse/test.cpp @@ -0,0 +1,375 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +template +concept CanViewReverse = requires(Rng&& r) { + views::reverse(static_cast(r)); +}; + +// Test a silly precomposed range adaptor pipeline +constexpr auto pipeline = views::all | views::reverse | views::all | views::reverse | views::all | views::reverse; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::common_range, ranges::reverse_view, ranges::sized_range, ranges::begin, ranges::end, ranges::size, + ranges::iterator_t, ranges::range_size_t, ranges::random_access_range, ranges::prev, + ranges::enable_borrowed_range; + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using R = reverse_view; + static_assert(ranges::view); + static_assert(ranges::bidirectional_range); + static_assert(random_access_range == random_access_range); + static_assert(!ranges::contiguous_range); + + // Validate range adapter object + // ...with lvalue argument + static_assert(CanViewReverse == (!is_view || copyable) ); + if constexpr (CanViewReverse) { + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + static_assert(same_as); + static_assert(noexcept(views::reverse(rng)) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(views::reverse(views::reverse(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | views::reverse | views::reverse | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + static_assert(CanViewReverse&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + static_assert(same_as); + static_assert(noexcept(views::reverse(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = reverse_view&>>; + constexpr bool is_noexcept = is_nothrow_constructible_v&>; + + static_assert(same_as); + static_assert(noexcept(views::reverse(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + static_assert(CanViewReverse> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + static_assert(same_as); + static_assert(noexcept(views::reverse(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range) { + using S = decltype(ranges::subrange{declval()}); + using RS = reverse_view; + constexpr bool is_noexcept = noexcept(S{declval()}); + + static_assert(same_as); + static_assert(noexcept(views::reverse(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::reverse | views::reverse | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + static_assert(CanViewReverse> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + static_assert(same_as); + static_assert(noexcept(views::reverse(as_const(rng))) == is_nothrow_copy_constructible_v); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::reverse) == is_nothrow_copy_constructible_v); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::reverse | views::reverse | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = reverse_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + static_assert(same_as); + static_assert(noexcept(views::reverse(move(as_const(rng)))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::reverse | views::reverse | views::reverse) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as auto r = reverse_view{forward(rng)}; + assert(ranges::equal(r, expected)); + + // Validate reverse_view::size + static_assert(CanMemberSize == sized_range); + if constexpr (sized_range) { + assert(r.size() == static_cast>(size(expected))); + static_assert(noexcept(r.size()) == noexcept(size(rng))); + } + + static_assert(CanMemberSize == sized_range); + if constexpr (sized_range) { + assert(as_const(r).size() == static_cast>(size(expected))); + static_assert(noexcept(r.size()) == noexcept(size(as_const(rng)))); + } + + // Validate view_interface::empty and operator bool + const bool is_empty = ranges::empty(expected); + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + static_assert(CanMemberEmpty == common_range); + if constexpr (common_range) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + + // Validate reverse_view::begin + static_assert(CanMemberBegin); + { + // reverse_view sometimes caches begin, so let's make several extra calls + const same_as>> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + assert(r.begin() == i); + assert(r.begin() == i); + // NB: non-const begin is unconditionally noexcept(false) due to caching + static_assert(!noexcept(r.begin())); + + if constexpr (copyable) { + auto r2 = r; + const same_as>> auto i2 = r2.begin(); + assert(r2.begin() == i2); + assert(r2.begin() == i2); + if (!is_empty) { + assert(*i2 == *i); + } + } + + static_assert(CanMemberBegin == common_range); + if constexpr (common_range) { + const same_as>> auto ci = as_const(r).begin(); + assert(as_const(r).begin() == ci); + assert(as_const(r).begin() == ci); + if (!is_empty) { + assert(*ci == *i); + } + static_assert(noexcept(as_const(r).begin()) == noexcept(reverse_iterator{end(as_const(rng))})); + + if constexpr (copyable) { + const auto r2 = r; + const same_as>> auto ci2 = r2.begin(); + assert(r2.begin() == ci2); + assert(r2.begin() == ci2); + if (!is_empty) { + assert(*ci2 == *i); + } + } + } + } + + // Validate reverse_view::end + static_assert(CanMemberEnd); + if (!is_empty) { + assert(*prev(r.end()) == *prev(end(expected))); + + if constexpr (copyable) { + auto r2 = r; + assert(*prev(r2.end()) == *prev(end(expected))); + } + static_assert(noexcept(r.end()) == noexcept(reverse_iterator{begin(rng)})); + + static_assert(CanMemberEnd == common_range); + if constexpr (common_range) { + assert(*prev(as_const(r).end()) == *prev(end(expected))); + static_assert(noexcept(as_const(r).end()) == noexcept(reverse_iterator{begin(as_const(rng))})); + } + } + + // Validate view_interface::data + static_assert(!CanData); + static_assert(!CanData); + + if (!is_empty) { + // Validate view_interface::operator[] + static_assert(CanIndex == random_access_range); + static_assert(CanIndex == (random_access_range && common_range) ); + if constexpr (random_access_range) { + assert(r[0] == *begin(expected)); + + if constexpr (common_range) { + assert(as_const(r)[0] == *begin(expected)); + } + } + + // Validate view_interface::front and back + assert(r.front() == *begin(expected)); + assert(r.back() == *prev(end(expected))); + + static_assert(CanMemberFront == common_range); + static_assert(CanMemberBack == common_range); + if constexpr (common_range) { + assert(as_const(r).front() == *begin(expected)); + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate reverse_view::base() const& + static_assert(CanMemberBase == copy_constructible); + if constexpr (copy_constructible) { + same_as auto b1 = as_const(r).base(); + static_assert(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == *prev(end(expected))); + if constexpr (common_range) { + assert(*prev(b1.end()) == *begin(expected)); + } + } + } + + // Validate reverse_view::base() && (NB: do this last since it leaves r moved-from) +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as auto b2 = move(r).base(); + static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == *prev(end(expected))); + if constexpr (common_range) { + assert(*prev(b2.end()) == *begin(expected)); + } + } + + return true; +} + +struct instantiator { + template + static constexpr void call() { + R r{}; + test_one(r, span{}); + } +}; + +template > +using move_only_view = test::range}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +int main() { + static constexpr int some_ints[] = {0, 1, 2}; + static constexpr int reversed_ints[] = {2, 1, 0}; + + // Validate views + { // ...copyable + constexpr string_view str{"Hello, World!"}; + constexpr auto expected = "!dlroW ,olleH"sv; + static_assert(test_one(str, expected)); + test_one(str, expected); + } + { // ... move-only + test_one(move_only_view{some_ints}, reversed_ints); + test_one(move_only_view{some_ints}, reversed_ints); + test_one(move_only_view{some_ints}, reversed_ints); + test_one(move_only_view{some_ints}, reversed_ints); + test_one(move_only_view{some_ints}, reversed_ints); + test_one(move_only_view{some_ints}, reversed_ints); + } + + // Validate non-views + { // ... C array + static_assert(test_one(some_ints, reversed_ints)); + test_one(some_ints, reversed_ints); + } + { // ... contiguous container + string str{"Hello, World!"}; + constexpr auto expected = "!dlroW ,olleH"sv; + test_one(str, expected); + } + { // ... bidi container + list lst{3, 4, 5}; + static constexpr int reversed[] = {5, 4, 3}; + test_one(lst, reversed); + + static constexpr int reversed_prefix[] = {4, 3}; + assert(ranges::equal( + views::reverse(ranges::subrange{counted_iterator{lst.begin(), 2}, default_sentinel}), reversed_prefix)); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + static_assert(test_one(s, reversed_ints)); + test_one(s, reversed_ints); + } + + // Get full instantiation coverage + static_assert((test_bidi(), true)); + test_bidi(); +} diff --git a/tests/std/tests/P0896R4_views_take/env.lst b/tests/std/tests/P0896R4_views_take/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_take/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_take/test.cpp b/tests/std/tests/P0896R4_views_take/test.cpp new file mode 100644 index 00000000000..b0d35fc3900 --- /dev/null +++ b/tests/std/tests/P0896R4_views_take/test.cpp @@ -0,0 +1,533 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#pragma warning(disable : 6011) // Dereferencing NULL pointer '%s' + +// Test a silly precomposed range adaptor pipeline +constexpr auto pipeline = views::take(7) | views::take(6) | views::take(5) | views::take(4); + +template +inline constexpr bool is_empty_view = false; +template +inline constexpr bool is_empty_view> = true; + +template +inline constexpr bool is_dynamic_span = false; +template +inline constexpr bool is_dynamic_span> = true; + +template +inline constexpr bool is_string_view = false; +template +inline constexpr bool is_string_view> = true; + +template +inline constexpr bool is_subrange = false; +template +inline constexpr bool is_subrange> = true; + +// clang-format off +template +concept reconstructible = ranges::random_access_range + && ranges::sized_range + && (is_empty_view + || is_dynamic_span + || is_string_view + // || is_iota_view // TRANSITION, iota_view + || is_subrange); +// clang-format on + +template +using mapped_t = conditional_t, V, ranges::take_view>; + +template +using pipeline_t = mapped_t>>>>; + +template +concept CanViewTake = requires(Rng&& r) { + views::take(static_cast(r), 42); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::input_range, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range, + ranges::contiguous_range; + using ranges::take_view, ranges::common_range, ranges::enable_borrowed_range, ranges::iterator_t, ranges::prev, + ranges::range, ranges::sentinel_t, ranges::sized_range; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using M = mapped_t; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto take_four = views::take(4); + + // ... with lvalue argument + STATIC_ASSERT(CanViewTake == (!is_view || copyable) ); + if constexpr (CanViewTake) { // Validate lvalue + constexpr bool is_noexcept = !is_view || (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(rng, 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewTake&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = mapped_t&>>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, int>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewTake> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v && !is_subrange; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = take_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewTake> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = take_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(move(as_const(rng)), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + const bool is_empty = ranges::empty(expected); + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as> auto r = take_view{forward(rng), 4}; + using R = decltype(r); + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate take_view::size + STATIC_ASSERT(CanMemberSize == CanSize); + if constexpr (CanMemberSize) { + assert(r.size() == static_cast(ranges::size(expected))); + } else { + STATIC_ASSERT(!CanSize); + } + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(r) == is_empty); + assert(static_cast(r) == !is_empty); + } + } + + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(as_const(r)) == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate content + assert(ranges::equal(r, expected)); + + // Validate take_view::begin + STATIC_ASSERT(CanMemberBegin); + if constexpr (random_access_range && sized_range) { + STATIC_ASSERT(same_as, iterator_t>); + } else { + STATIC_ASSERT(same_as, counted_iterator>>); + } + STATIC_ASSERT(CanBegin == range); + if (forward_range) { // intentionally not if constexpr + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (range) { + if constexpr (random_access_range && sized_range) { + STATIC_ASSERT(same_as, iterator_t>); + } else { + STATIC_ASSERT(same_as, counted_iterator>>); + } + + const same_as> auto i3 = as_const(r).begin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Validate take_view::end + STATIC_ASSERT(CanMemberEnd); + if constexpr (sized_range) { + if constexpr (random_access_range) { + STATIC_ASSERT(same_as, iterator_t>); + } else { + STATIC_ASSERT(same_as, default_sentinel_t>); + } + } else { + // Not much we can do here + STATIC_ASSERT(!same_as, iterator_t>); + STATIC_ASSERT(!same_as, default_sentinel_t>); + STATIC_ASSERT(is_class_v>); + } + STATIC_ASSERT(CanEnd == range); + if (!is_empty) { + same_as> auto i = r.end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i) == *prev(end(expected))); + } + + if constexpr (range) { + same_as> auto i2 = as_const(r).end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i2) == *prev(end(expected))); + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(CanMemberData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + if constexpr (contiguous_range) { + const same_as>*> auto ptr1 = r.data(); + assert(to_address(ptr1) == to_address(r.begin())); + + if constexpr (contiguous_range) { + const same_as>*> auto ptr2 = as_const(r).data(); + assert(to_address(ptr2) == to_address(as_const(r).begin())); + } + } + + // Validate view_interface::front and back + if (!is_empty) { + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (forward_range) { + assert(r.front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (forward_range) { + assert(as_const(r).front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + STATIC_ASSERT(CanIndex == random_access_range); + if (!is_empty) { + if constexpr (CanIndex) { + assert(r[0] == *r.begin()); + } + if constexpr (CanIndex) { + assert(as_const(r)[0] == *as_const(r).begin()); + } + } + + // Validate take_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == *begin(expected)); + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end()) == 7); // NB: depends on the test data + } + } + } + + // Validate take_view::base() && (NB: do this last since it leaves r moved-from) + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == *begin(expected)); + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end()) == 7); // NB: depends on the test data + } + } + } + + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int only_four_ints[] = {0, 1, 2, 3}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, only_four_ints); + + R empty_range{}; + test_one(empty_range, span{}); + } +}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness. + using test::Common, test::Sized; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template +using move_only_view = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +constexpr void move_only_test() { + using test::Common, test::Sized; + using input = input_iterator_tag; + using fwd = forward_iterator_tag; + using bidi = bidirectional_iterator_tag; + using random = random_access_iterator_tag; + using contiguous = contiguous_iterator_tag; + + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); +} + +constexpr void output_range_test() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 + if (!is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using R = test::range; + int some_writable_ints[] = {0, 1, 2, 3}; + STATIC_ASSERT(same_as>); + + // How do I implement "Fill up to n elements in {output range} with {value}"? + ranges::fill(R{some_writable_ints} | views::take(99999), 42); + assert(ranges::equal(some_writable_ints, initializer_list{42, 42, 42, 42})); + + ranges::fill(R{some_writable_ints} | views::take(3), 13); + assert(ranges::equal(some_writable_ints, initializer_list{13, 13, 13, 42})); + } +} + +int main() { + // Validate views + { // ... copyable + // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + + STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); + test_one(ranges::subrange{some_ints}, only_four_ints); + + STATIC_ASSERT(test_one(views::empty, span{})); + test_one(views::empty, span{}); + + STATIC_ASSERT(test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints)); + test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints); + + // TRANSITION, iota_view + // STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); + // test_one(ranges::iota_view{0, 8}, only_four_ints); + } + // ... move-only + STATIC_ASSERT((move_only_test(), true)); + move_only_test(); + + // Validate non-views + { + STATIC_ASSERT(test_one(some_ints, only_four_ints)); + test_one(some_ints, only_four_ints); + } + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, only_four_ints); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, only_four_ints); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + } + + // Validate an output range + STATIC_ASSERT((output_range_test(), true)); + output_range_test(); + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_transform/env.lst b/tests/std/tests/P0896R4_views_transform/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp new file mode 100644 index 00000000000..fb03a17c0f0 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -0,0 +1,716 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#if _ITERATOR_DEBUG_LEVEL == 0 +#define NOEXCEPT_IDL0(...) noexcept(__VA_ARGS__) +#else +#define NOEXCEPT_IDL0(...) true +#endif // _ITERATOR_DEBUG_LEVEL == 0 + +// Test a silly precomposed range adaptor pipeline +constexpr auto add8 = [](const auto& x) noexcept { return x + 8; }; + +using Fun = remove_const_t; +STATIC_ASSERT(is_nothrow_copy_constructible_v&& is_nothrow_move_constructible_v); + +constexpr auto pipeline = + views::transform(add8) | views::transform(add8) | views::transform(add8) | views::transform(add8); + +template > +using pipeline_t = + ranges::transform_view, Fun>, Fun>, + Fun>; + +template +concept CanViewTransform = requires(Rng&& r) { + views::transform(static_cast(r), add8); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::transform_view, ranges::bidirectional_range, ranges::common_range, ranges::contiguous_range, + ranges::enable_borrowed_range, ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::prev, + ranges::random_access_range, ranges::range, ranges::range_reference_t, ranges::sentinel_t; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using TV = transform_view; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(!contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto transform_incr = views::transform(add8); + + // ... with lvalue argument + STATIC_ASSERT(CanViewTransform == (!is_view || copyable) ); + if constexpr (CanViewTransform) { // Validate lvalue + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(rng, add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewTransform&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(as_const(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = transform_view&>, Fun>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, decltype((add8))>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(as_const(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewTransform> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(move(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = transform_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(move(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewTransform> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(as_const(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = transform_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(move(as_const(rng)), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + const bool is_empty = ranges::empty(expected); + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as auto r = transform_view{forward(rng), add8}; + using R = decltype(r); + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(!contiguous_range); + + // Validate transform_view::size + STATIC_ASSERT(CanMemberSize == CanSize); + STATIC_ASSERT(CanSize == CanMemberSize); + STATIC_ASSERT(CanMemberSize == CanSize); + STATIC_ASSERT(CanSize == CanMemberSize); + if constexpr (CanMemberSize) { + assert(r.size() == static_cast(ranges::size(expected))); + if constexpr (CanMemberSize) { + assert(as_const(r).size() == static_cast(ranges::size(expected))); + } + } + + constexpr bool const_invocable = regular_invocable>; + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(r) == is_empty); + assert(static_cast(r) == !is_empty); + } + } + + STATIC_ASSERT(CanMemberEmpty == (forward_range && const_invocable)); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(as_const(r)) == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate content + assert(ranges::equal(r, expected)); + + // Validate transform_view::begin + STATIC_ASSERT(CanMemberBegin); + STATIC_ASSERT(CanBegin == (range && const_invocable)); + if (forward_range) { // intentionally not if constexpr + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (CanBegin) { + const same_as> auto i3 = as_const(r).begin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Validate transform_view::end + STATIC_ASSERT(CanMemberEnd); + STATIC_ASSERT(CanEnd == (range && const_invocable)); + if (!is_empty) { + same_as> auto i = r.end(); + static_assert(is_same_v, iterator_t> == common_range); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i) == *prev(end(expected))); + } + + if constexpr (CanEnd) { + same_as> auto i2 = as_const(r).end(); + static_assert(is_same_v, iterator_t> == common_range); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i2) == *prev(end(expected))); + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Validate view_interface::front and back + if (!is_empty) { + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + STATIC_ASSERT(CanMemberFront == (forward_range && const_invocable)); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + + STATIC_ASSERT( + CanMemberBack == (bidirectional_range && common_range && const_invocable)); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate view_interface::operator[] + if (!is_empty) { + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(r[0] == *r.begin()); + } + + STATIC_ASSERT(CanIndex == (random_access_range && const_invocable)); + if constexpr (CanIndex) { + assert(as_const(r)[0] == *as_const(r).begin()); + } + } + + // Validate transform_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end()) == 7); // NB: depends on the test data + } + } + } + + // Validate transform_view::base() && (NB: do this last since it leaves r moved-from) +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end()) == 7); // NB: depends on the test data + } + } + } + + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int transformed_ints[] = {8, 9, 10, 11, 12, 13, 14, 15}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, transformed_ints); + } +}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category, commonality, size, and differencing, but oblivious to proxyness. + using test::Common, test::Sized; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template +using move_only_view = test::range}, IsCommon, + test::CanCompare{derived_from}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +constexpr void move_only_view_tests() { + using test::Common, test::Sized; + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); +} + +struct iterator_instantiator { + template + static constexpr void call() { + // Pre: Iter is a specialization of test::iterator whose element type is const int + int mutable_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const auto make_view = [&] { + return views::transform( + ranges::subrange{Iter{mutable_ints}, test::sentinel{ranges::end(mutable_ints)}}, add8); + }; + using R = decltype(make_view()); + using I = ranges::iterator_t; + using S = ranges::sentinel_t; + + // Validate nested types + STATIC_ASSERT(is_same_v, random_access_iterator_tag, + conditional_t, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>>); + + using C = typename iterator_traits::iterator_category; + STATIC_ASSERT(is_same_v>>, + conditional_t, random_access_iterator_tag, C>, + input_iterator_tag>>); + + { // Validate iterator special member functions and base + I defaultConstructed{}; + assert(std::move(defaultConstructed).base().peek() == nullptr); + STATIC_ASSERT(is_nothrow_default_constructible_v); + + auto r0 = make_view(); + I valueConstructed{r0, Iter{mutable_ints}}; + STATIC_ASSERT(is_nothrow_constructible_v); + + if constexpr (copyable) { + I copyConstructed{valueConstructed}; + assert(copyConstructed == valueConstructed); + STATIC_ASSERT(is_nothrow_copy_constructible_v); + + defaultConstructed = copyConstructed; + assert(defaultConstructed == valueConstructed); + STATIC_ASSERT(is_nothrow_copy_assignable_v); + } + assert(std::move(valueConstructed).base().peek() == mutable_ints); + + if constexpr (forward_iterator) { + auto r1 = make_view(); + const auto i = r1.begin(); + using CI = ranges::iterator_t; + CI conversionConstructed{i}; + assert(conversionConstructed.base().peek() == mutable_ints); + STATIC_ASSERT(is_nothrow_constructible_v); + } + } + + { // Validate sentinel constructors and base + S defaultConstructed{}; + assert(defaultConstructed.base().peek() == nullptr); + STATIC_ASSERT(is_nothrow_default_constructible_v); + + const test::sentinel s{mutable_ints + 2}; + S valueConstructed{s}; + assert(valueConstructed.base().peek() == s.peek()); + STATIC_ASSERT(is_nothrow_constructible_v&>); + + S copyConstructed{valueConstructed}; + assert(copyConstructed.base().peek() == valueConstructed.base().peek()); + STATIC_ASSERT(is_nothrow_copy_constructible_v); + + defaultConstructed = copyConstructed; + assert(defaultConstructed.base().peek() == valueConstructed.base().peek()); + STATIC_ASSERT(is_nothrow_copy_assignable_v); + + if constexpr (forward_iterator && indirectly_swappable) { + auto r = make_view(); + using CS = ranges::sentinel_t; + const S last = r.end(); + CS conversionConstructed{last}; + assert(conversionConstructed.base().peek() == ranges::end(mutable_ints)); + STATIC_ASSERT(is_nothrow_constructible_v); + } + } + + { // Validate dereference ops + auto r0 = make_view(); + auto i0 = r0.begin(); + assert(*i0 == add8(mutable_ints[0])); + STATIC_ASSERT(NOEXCEPT_IDL0(*i0)); + + assert(ranges::iter_move(i0) == add8(mutable_ints[0])); // NB: moving from int leaves it unchanged + STATIC_ASSERT(NOEXCEPT_IDL0(ranges::iter_move(i0))); + + if constexpr (forward_iterator) { + auto i1 = ranges::next(i0); + ranges::iter_swap(i0, i1); + assert(mutable_ints[0] == 1); + assert(mutable_ints[1] == 0); + ranges::iter_swap(i1, i0); + assert(mutable_ints[0] == 0); + assert(mutable_ints[1] == 1); + STATIC_ASSERT(NOEXCEPT_IDL0(ranges::iter_swap(i0, i1))); + } + } + + { // Validate increments + auto r0 = make_view(); + auto i0 = r0.begin(); + assert(&++i0 == &i0); + assert(std::move(i0).base().peek() == mutable_ints + 1); + STATIC_ASSERT(noexcept(++i0)); + + auto r1 = make_view(); + auto i1 = r1.begin(); + if constexpr (forward_iterator) { + assert(i1++ == r1.begin()); + } else { + i1++; + } + assert(std::move(i1).base().peek() == mutable_ints + 1); + STATIC_ASSERT(noexcept(i0++)); + } + + if constexpr (bidirectional_iterator) { // Validate decrements + auto r = make_view(); + const auto second = ranges::next(r.begin()); + auto i = second; + assert(&--i == &i); + assert(i.base().peek() == mutable_ints); + STATIC_ASSERT(noexcept(--i)); + + i = second; + assert(i-- == second); + assert(i.base().peek() == mutable_ints); + STATIC_ASSERT(noexcept(i--)); + } + + if constexpr (random_access_iterator) { // Validate seek operations and [] + auto r = make_view(); + auto i = r.begin(); + assert((i + 2).base().peek() == mutable_ints + 2); + assert((I{} + 0) == I{}); + STATIC_ASSERT(NOEXCEPT_IDL0(i + 2)); + + assert((2 + i).base().peek() == mutable_ints + 2); + assert((0 + I{}).base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(2 + i)); + + auto vi = I{}; + assert(&(i += 5) == &i); + assert(i.base().peek() == mutable_ints + 5); + assert(&(vi += 0) == &vi); + assert(vi.base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(i += 5)); + + assert((i - 2).base().peek() == mutable_ints + 3); + assert((I{} - 0).base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(i - 2)); + + assert(&(i -= 3) == &i); + assert(i.base().peek() == mutable_ints + 2); + assert(&(vi -= 0) == &vi); + assert(vi.base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(i -= 3)); + + assert(i[4] == add8(mutable_ints[6])); + STATIC_ASSERT(NOEXCEPT_IDL0(i[4])); + } + + if constexpr (equality_comparable) { + // Validate == and != + auto r = make_view(); + const auto first = r.begin(); + const auto last = r.end(); + + assert(first == first); + assert(I{} == I{}); + STATIC_ASSERT(noexcept(first == first)); + + assert(!(first == last)); + STATIC_ASSERT(noexcept(first == last)); + assert(!(last == first)); + STATIC_ASSERT(noexcept(last == first)); + + assert(!(first != first)); + assert(!(I{} != I{})); + STATIC_ASSERT(noexcept(first != first)); + + if constexpr (forward_iterator) { + const auto final = ranges::next(first, last); + assert(!(first == final)); + assert(first != final); + + assert(last == final); + assert(final == last); + + assert(!(last != final)); + assert(!(final != last)); + + if constexpr (sized_sentinel_for) { // Validate difference + assert(first - first == 0); + assert(final - first == ranges::ssize(mutable_ints)); + assert(first - final == -ranges::ssize(mutable_ints)); + assert(I{} - I{} == 0); + STATIC_ASSERT(noexcept(first - first)); + } + + if constexpr (sized_sentinel_for, Iter>) { + assert(last - first == ranges::ssize(mutable_ints)); + assert(first - last == -ranges::ssize(mutable_ints)); + STATIC_ASSERT(noexcept(last - first)); + STATIC_ASSERT(noexcept(first - last)); + } + + if constexpr (random_access_iterator) { // Validate relational operators + assert(!(first < first)); + assert(first < final); + assert(!(final < first)); + assert(!(I{} < I{})); + STATIC_ASSERT(noexcept(first < final)); + + assert(!(first > first)); + assert(!(first > final)); + assert(final > first); + assert(!(I{} > I{})); + STATIC_ASSERT(noexcept(first > final)); + + assert(first <= first); + assert(first <= final); + assert(!(final <= first)); + assert(I{} <= I{}); + STATIC_ASSERT(noexcept(first <= final)); + + assert(first >= first); + assert(!(first >= final)); + assert(final >= first); + assert(I{} >= I{}); + STATIC_ASSERT(noexcept(first >= final)); + + if constexpr (three_way_comparable) { // Validate spaceship + assert((first <=> first) == strong_ordering::equal); + assert((first <=> final) == strong_ordering::less); + assert((final <=> first) == strong_ordering::greater); + assert((I{} <=> I{}) == strong_ordering::equal); + STATIC_ASSERT(noexcept(first <=> final)); + } + } + } + } + } +}; + +template +using test_iterator = + test::iterator}, + test::ProxyRef{!derived_from}>; + +constexpr void iterator_instantiation_test() { + using test::CanDifference; + + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); +} + +int main() { + { // Validate copyable views + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, transformed_ints)); + test_one(s, transformed_ints); + } + + // Validate move-only views + STATIC_ASSERT((move_only_view_tests(), true)); + move_only_view_tests(); + + { // Validate non-views + STATIC_ASSERT(test_one(some_ints, transformed_ints)); + test_one(some_ints, transformed_ints); + + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, transformed_ints); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, transformed_ints); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); + } + + { // Validate a non-view borrowed range + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, transformed_ints)); + test_one(s, transformed_ints); + } + + STATIC_ASSERT((iterator_instantiation_test(), true)); + iterator_instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_transform_death/env.lst b/tests/std/tests/P0896R4_views_transform_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_transform_death/test.cpp b/tests/std/tests/P0896R4_views_transform_death/test.cpp new file mode 100644 index 00000000000..5dfa72da1b3 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform_death/test.cpp @@ -0,0 +1,395 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include + +#include +using namespace std; + +static int some_ints[] = {0, 1, 2, 3}; + +[[maybe_unused]] constexpr auto lambda = [x = 42](int) { return x == 42; }; +using TV = decltype(ranges::transform_view{some_ints, lambda}); + +void test_constructor_wrong_range() { + vector vec0{0, 1, 2, 3}; + vector vec1{4, 5, 6, 7}; + auto r0 = views::transform(vec0, lambda); + using R = decltype(r0); + same_as auto r1 = views::transform(vec1, lambda); + ranges::iterator_t i{r0, r1.begin().base()}; // vector iterators in range are from different containers +} + +void test_operator_star_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) (*i); // cannot dereference value-initialized transform_view iterator +} + +void test_operator_star_end_iterator() { + TV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + (void) (*i); // cannot dereference end transform_view iterator +} + +void test_operator_preincrement_value_initialized_iterator() { + ranges::iterator_t i{}; + ++i; // cannot increment value-initialized transform_view iterator +} + +void test_operator_preincrement_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + ++i; // cannot increment transform_view iterator past end +} + +void test_operator_postincrement_value_initialized_iterator() { + ranges::iterator_t i{}; + i++; // cannot increment value-initialized transform_view iterator +} + +void test_operator_postincrement_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + i++; // cannot increment transform_view iterator past end +} + +void test_operator_predecrement_value_initialized_iterator() { + ranges::iterator_t i{}; + --i; // cannot decrement value-initialized transform_view iterator +} + +void test_operator_predecrement_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + --i; // cannot decrement transform_view iterator before begin +} + +void test_operator_postdecrement_value_initialized_iterator() { + ranges::iterator_t i{}; + i--; // cannot decrement value-initialized transform_view iterator +} + +void test_operator_postdecrement_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + i--; // cannot decrement transform_view iterator before begin +} + +void test_operator_seek_forward_value_initialized_iterator() { + ranges::iterator_t i{}; + i += 42; // cannot seek value-initialized transform_view iterator +} + +void test_operator_seek_forward_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + i += ranges::distance(some_ints); // cannot seek transform_view iterator past end +} + +void test_operator_seek_backward_value_initialized_iterator() { + ranges::iterator_t i{}; + i -= 42; // cannot seek value-initialized transform_view iterator +} + +void test_operator_seek_backward_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + i -= ranges::distance(some_ints); // cannot seek transform_view iterator before begin +} + +void test_operator_bracket_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) i[42]; // cannot seek value-initialized transform_view iterator +} + +void test_operator_bracket_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) i[-ranges::distance(some_ints)]; // cannot seek transform_view iterator before begin +} + +void test_operator_bracket_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) i[ranges::distance(some_ints)]; // cannot seek transform_view iterator after end +} + +void test_operator_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 == i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i == ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_not_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 != i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_not_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i != ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 < i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i < ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 > i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i > ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 <= i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i <= ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 >= i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i >= ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_spaceship_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 <=> i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_spaceship_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i <=> ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_plus_value_initialized() { + ranges::iterator_t i{}; + (void) (i + 42); // cannot seek value-initialized transform_view iterator +} + +void test_operator_plus_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i + -ranges::distance(some_ints)); // cannot seek transform_view iterator before begin +} + +void test_operator_plus_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i + ranges::distance(some_ints)); // cannot seek transform_view iterator after end +} + +void test_flipped_operator_plus_value_initialized() { + ranges::iterator_t i{}; + (void) (42 + i); // cannot seek value-initialized transform_view iterator +} + +void test_flipped_operator_plus_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (-ranges::distance(some_ints) + i); // cannot seek transform_view iterator before begin +} + +void test_flipped_operator_plus_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (ranges::distance(some_ints) + i); // cannot seek transform_view iterator after end +} + +void test_operator_minus_value_initialized() { + ranges::iterator_t i{}; + (void) (i - 42); // cannot seek value-initialized transform_view iterator +} + +void test_operator_minus_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i - ranges::distance(some_ints)); // cannot seek transform_view iterator before begin +} + +void test_operator_minus_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i - -ranges::distance(some_ints)); // cannot seek transform_view iterator after end +} + +void test_operator_minus_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 - i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_minus_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i - ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_iter_move_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) ranges::iter_move(i); // cannot dereference value-initialized transform_view iterator +} + +void test_iter_swap_value_initialized_iterators() { + ranges::iterator_t i0{}; + ranges::iterator_t i1{}; + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized transform_view iterator +} + +void test_iter_swap_value_initialized_iterator_left() { + ranges::iterator_t i0{}; + TV r{some_ints, lambda}; + ranges::iterator_t i1 = r.begin(); + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized transform_view iterator +} + +void test_iter_swap_value_initialized_iterator_right() { + TV r{some_ints, lambda}; + ranges::iterator_t i0 = r.begin(); + ranges::iterator_t i1{}; + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized transform_view iterator +} + +void test_sentinel_compare_value_initialized() { + auto r = ranges::subrange{counted_iterator{some_ints, ranges::distance(some_ints)}, default_sentinel} + | views::transform(lambda); + using R = decltype(r); + static_assert(!ranges::common_range); + (void) (ranges::iterator_t{} == r.end()); +} + +void test_sentinel_difference_value_initialized() { + auto r = ranges::subrange{counted_iterator{some_ints, ranges::distance(some_ints)}, default_sentinel} + | views::transform(lambda); + using R = decltype(r); + static_assert(!ranges::common_range); + static_assert(sized_sentinel_for, ranges::iterator_t>); + (void) (ranges::iterator_t{} - r.end()); +} + +void test_flipped_sentinel_difference_value_initialized() { + auto r = ranges::subrange{counted_iterator{some_ints, ranges::distance(some_ints)}, default_sentinel} + | views::transform(lambda); + using R = decltype(r); + static_assert(!ranges::common_range); + static_assert(sized_sentinel_for, ranges::iterator_t>); + (void) (r.end() - ranges::iterator_t{}); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec([] {}); + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_constructor_wrong_range, + test_operator_star_value_initialized_iterator, + test_operator_star_end_iterator, + test_operator_preincrement_value_initialized_iterator, + test_operator_preincrement_after_end, + test_operator_postincrement_value_initialized_iterator, + test_operator_postincrement_after_end, + test_operator_predecrement_value_initialized_iterator, + test_operator_predecrement_before_begin, + test_operator_postdecrement_value_initialized_iterator, + test_operator_postdecrement_before_begin, + test_operator_seek_forward_value_initialized_iterator, + test_operator_seek_forward_after_end, + test_operator_seek_backward_value_initialized_iterator, + test_operator_seek_backward_before_begin, + test_operator_bracket_value_initialized_iterator, + test_operator_bracket_before_begin, + test_operator_bracket_after_end, + test_operator_equal_incompatible_different, + test_operator_equal_incompatible_value_initialized, + test_operator_not_equal_incompatible_different, + test_operator_not_equal_incompatible_value_initialized, + test_operator_less_incompatible_different, + test_operator_less_incompatible_value_initialized, + test_operator_greater_incompatible_different, + test_operator_greater_incompatible_value_initialized, + test_operator_less_equal_incompatible_different, + test_operator_less_equal_incompatible_value_initialized, + test_operator_greater_equal_incompatible_different, + test_operator_greater_equal_incompatible_value_initialized, + test_operator_spaceship_incompatible_different, + test_operator_spaceship_incompatible_value_initialized, + test_operator_plus_value_initialized, + test_operator_plus_before_begin, + test_operator_plus_after_end, + test_flipped_operator_plus_value_initialized, + test_flipped_operator_plus_before_begin, + test_flipped_operator_plus_after_end, + test_operator_minus_value_initialized, + test_operator_minus_before_begin, + test_operator_minus_after_end, + test_operator_minus_incompatible_different, + test_operator_minus_incompatible_value_initialized, + test_iter_move_value_initialized_iterator, + test_iter_swap_value_initialized_iterators, + test_iter_swap_value_initialized_iterator_left, + test_iter_swap_value_initialized_iterator_right, + test_sentinel_compare_value_initialized, + test_sentinel_difference_value_initialized, + test_flipped_sentinel_difference_value_initialized, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P1135R6_barrier/env.lst b/tests/std/tests/P1135R6_barrier/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1135R6_barrier/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P1135R6_barrier/test.cpp b/tests/std/tests/P1135R6_barrier/test.cpp new file mode 100644 index 00000000000..3ba5944e4fe --- /dev/null +++ b/tests/std/tests/P1135R6_barrier/test.cpp @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +void test() { + std::barrier b(2); + + std::atomic c{0}; + + std::thread t1([&] { + for (int i = 0; i < 5; ++i) { + auto token = b.arrive(); + b.wait(std::move(token)); + c.fetch_add(1, std::memory_order_relaxed); + } + }); + + std::thread t2([&] { + for (int i = 0; i < 3; ++i) { + b.arrive_and_wait(); + c.fetch_add(1, std::memory_order_relaxed); + } + b.arrive_and_drop(); + }); + + t1.join(); + t2.join(); + + assert(c.load(std::memory_order_relaxed) == 8); +} + +void test_with_functor() { + std::atomic c{0}; + std::atomic called_times{0}; + + struct Functor { + void operator()() noexcept { + switch (called_times->fetch_add(1, std::memory_order_relaxed) + 1) { + case 1: + assert(c->load(std::memory_order_relaxed) == 0); + break; + case 2: + assert(c->load(std::memory_order_relaxed) == 2); + break; + case 3: + assert(c->load(std::memory_order_relaxed) == 4); + break; + case 4: + assert(c->load(std::memory_order_relaxed) == 6); + break; + case 5: + assert(c->load(std::memory_order_relaxed) == 7); + break; + default: + assert(false); + break; + } + } + + std::atomic* called_times; + std::atomic* c; + } f = {&called_times, &c}; + + std::barrier b(2, f); + + std::thread t1([&] { + for (int i = 0; i < 5; ++i) { + auto token = b.arrive(); + b.wait(std::move(token)); + c.fetch_add(1, std::memory_order_relaxed); + } + }); + + std::thread t2([&] { + for (int i = 0; i < 3; ++i) { + b.arrive_and_wait(); + c.fetch_add(1, std::memory_order_relaxed); + } + b.arrive_and_drop(); + }); + + t1.join(); + t2.join(); + + assert(c.load(std::memory_order_relaxed) == 8); + assert(called_times.load(std::memory_order_relaxed) == 5); +} + + +void test_token() { + std::atomic called_times{0}; + + auto f = [&]() noexcept { called_times.fetch_add(1, std::memory_order_relaxed); }; + + std::barrier b(2, f); + auto t1 = b.arrive(); + auto t2 = std::move(t1); + + assert(called_times.load(std::memory_order_relaxed) == 0); + auto t3 = b.arrive(); + auto t4 = std::move(t3); + + assert(called_times.load(std::memory_order_relaxed) == 1); + b.wait(std::move(t4)); + assert(called_times.load(std::memory_order_relaxed) == 1); + b.wait(std::move(t2)); + assert(called_times.load(std::memory_order_relaxed) == 1); +} + +void barrier_callback_function() noexcept {} + +void test_functor_types() { + struct f1 { + void operator()() noexcept {} + + f1(int, int, int) {} + + f1(f1&&) noexcept = default; + f1& operator=(f1&&) = delete; + }; + std::barrier b1{1, f1{0, 0, 0}}; + b1.arrive_and_wait(); + + std::barrier b2{1, barrier_callback_function}; + b2.arrive_and_wait(); + + std::barrier b3{1, []() noexcept {}}; + b3.arrive_and_wait(); +} + +int main() { + static_assert(std::barrier<>::max() >= 5, "barrier should support some number of arrivals"); + + test(); + test_with_functor(); + test_token(); + test_functor_types(); +} diff --git a/tests/std/tests/P1135R6_latch/env.lst b/tests/std/tests/P1135R6_latch/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1135R6_latch/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P1135R6_latch/test.cpp b/tests/std/tests/P1135R6_latch/test.cpp new file mode 100644 index 00000000000..991869c1f99 --- /dev/null +++ b/tests/std/tests/P1135R6_latch/test.cpp @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +void test(const bool release_wait) { + std::latch l(5); + + std::thread t1([&] { l.wait(); }); + + std::thread t2([&] { l.arrive_and_wait(2); }); + + l.count_down(); + + if (release_wait) { + l.arrive_and_wait(2); + } else { + l.count_down(2); + } + + t1.join(); + t2.join(); +} + +int main() { + static_assert(std::latch::max() >= 5, "latch should support some number of count downs"); + + test(true); + test(false); +} diff --git a/tests/std/tests/P1135R6_semaphore/env.lst b/tests/std/tests/P1135R6_semaphore/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1135R6_semaphore/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P1135R6_semaphore/test.cpp b/tests/std/tests/P1135R6_semaphore/test.cpp new file mode 100644 index 00000000000..dfccaf23a55 --- /dev/null +++ b/tests/std/tests/P1135R6_semaphore/test.cpp @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +void wait_and_expect(std::atomic& v, const int val, const std::chrono::milliseconds delay_duration) { +#ifdef CAN_FAIL_ON_TIMING_ASSUMPTION + std::this_thread::sleep_for(delay_duration); + assert(v.load() == val); +#else // ^^^ CAN_FAIL_ON_TIMING_ASSUMPTION / !CAN_FAIL_ON_TIMING_ASSUMPTION vvv + while (v.load() < val) { + std::this_thread::sleep_for(delay_duration); + } + assert(v.load() == val); +#endif // ^^^ !CAN_FAIL_ON_TIMING_ASSUMPTION ^^^ +} + +void test_counting_semaphore_count(const std::chrono::milliseconds delay_duration) { + std::latch start{4}; + + std::counting_semaphore<4> s{2}; + std::atomic v{0}; + + auto thread_function = [&] { + start.arrive_and_wait(); + for (int i = 0; i < 3; ++i) { + s.acquire(); + v.fetch_add(1); + } + }; + + std::thread t1{thread_function}; + std::thread t2{thread_function}; + std::thread t3{thread_function}; + + start.arrive_and_wait(); + + wait_and_expect(v, 2, delay_duration); + + s.release(); + + wait_and_expect(v, 3, delay_duration); + + s.release(4); + + wait_and_expect(v, 7, delay_duration); + + s.release(4); + + wait_and_expect(v, 9, delay_duration); + + t1.join(); + t2.join(); + t3.join(); +} + +void test_binary_semaphore_count(const std::chrono::milliseconds delay_duration) { + std::latch start{3}; + + std::binary_semaphore s{1}; + + std::atomic v{0}; + + auto thread_function = [&] { + start.arrive_and_wait(); + for (int i = 0; i < 2; ++i) { + s.acquire(); + v.fetch_add(1); + } + }; + + std::thread t1{thread_function}; + std::thread t2{thread_function}; + + start.arrive_and_wait(); + + wait_and_expect(v, 1, delay_duration); + + s.release(); + wait_and_expect(v, 2, delay_duration); + + s.release(); + wait_and_expect(v, 3, delay_duration); + + s.release(); + wait_and_expect(v, 4, delay_duration); + + s.release(); + wait_and_expect(v, 4, delay_duration); + + t1.join(); + t2.join(); +} + +template +void test_semaphore_wait_for(const std::chrono::milliseconds delay_duration) { + std::latch start{2}; + + Semaphore s{0}; + + std::thread t([&] { + start.arrive_and_wait(); + + assert(s.try_acquire_for(delay_duration)); + assert(!s.try_acquire_for(delay_duration * 16)); + }); + + start.arrive_and_wait(); + + s.release(); + + std::this_thread::sleep_for(delay_duration * 4); + + t.join(); +} + +template +void test_semaphore_wait_until(const std::chrono::milliseconds delay_duration) { + std::latch start{2}; + + Semaphore s{0}; + + std::thread t([&] { + start.arrive_and_wait(); + + assert(s.try_acquire_until(std::chrono::steady_clock::now() + delay_duration)); + assert(!s.try_acquire_until(std::chrono::steady_clock::now() + delay_duration * 8)); + }); + + start.arrive_and_wait(); + + s.release(); + + std::this_thread::sleep_for(delay_duration * 4); + + t.join(); +} + +int main() { + constexpr auto max = std::numeric_limits::max(); + + static_assert(std::counting_semaphore::max() >= max, "semaphore should support some number of count downs"); + static_assert(std::counting_semaphore<5>::max() >= 5, "semaphore should support some number of count downs"); + static_assert(std::binary_semaphore::max() >= 1, "semaphore should support some number of count downs"); + + constexpr auto delay_duration = 200ms; + + test_counting_semaphore_count(delay_duration); + test_binary_semaphore_count(delay_duration); + +#ifdef CAN_FAIL_ON_TIMING_ASSUMPTION + test_semaphore_wait_for>(delay_duration); + test_semaphore_wait_until>(delay_duration); + test_semaphore_wait_for(delay_duration); + test_semaphore_wait_until(delay_duration); +#endif // CAN_FAIL_ON_TIMING_ASSUMPTION +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp index c3eaa1b5777..455494fdd26 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp @@ -189,6 +189,20 @@ STATIC_ASSERT(__cpp_lib_atomic_wait == 201907L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_barrier +#error __cpp_lib_barrier is not defined +#elif __cpp_lib_barrier != 201907L +#error __cpp_lib_barrier is not 201907L +#else +STATIC_ASSERT(__cpp_lib_barrier == 201907L); +#endif +#else +#ifdef __cpp_lib_barrier +#error __cpp_lib_barrier is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_bind_front #error __cpp_lib_bind_front is not defined @@ -848,6 +862,20 @@ STATIC_ASSERT(__cpp_lib_is_swappable == 201603L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_latch +#error __cpp_lib_latch is not defined +#elif __cpp_lib_latch != 201907L +#error __cpp_lib_latch is not 201907L +#else +STATIC_ASSERT(__cpp_lib_latch == 201907L); +#endif +#else +#ifdef __cpp_lib_latch +#error __cpp_lib_latch is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_launder #error __cpp_lib_launder is not defined @@ -1116,6 +1144,20 @@ STATIC_ASSERT(__cpp_lib_scoped_lock == 201703L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_semaphore +#error __cpp_lib_semaphore is not defined +#elif __cpp_lib_semaphore != 201907L +#error __cpp_lib_semaphore is not 201907L +#else +STATIC_ASSERT(__cpp_lib_semaphore == 201907L); +#endif +#else +#ifdef __cpp_lib_semaphore +#error __cpp_lib_semaphore is defined +#endif +#endif + #ifndef __cpp_lib_shared_mutex #error __cpp_lib_shared_mutex is not defined #elif __cpp_lib_shared_mutex != 201505L diff --git a/tests/std/tests/VSO_0180469_fill_family/test.cpp b/tests/std/tests/VSO_0180469_fill_family/test.cpp index 164e0318043..2ec62a9404f 100644 --- a/tests/std/tests/VSO_0180469_fill_family/test.cpp +++ b/tests/std/tests/VSO_0180469_fill_family/test.cpp @@ -19,8 +19,8 @@ using namespace std; // This thing is a workaround for C4309 "truncation of constant value" template -T cast(U i) { - return static_cast(i); +remove_volatile_t cast(U i) { + return static_cast>(i); } // Tests that `fillCall`(buffer, value, startIndex, endIndex) fills [startIndex, endIndex) with `value` @@ -131,9 +131,41 @@ int main() { test_fill(); test_fill(); + test_fill(); // Test GH-1183 + test_uninitialized_fill( [](count_copies* buff, size_t n, const count_copies& src) { uninitialized_fill(buff, buff + n, src); }); test_uninitialized_fill( [](count_copies* buff, size_t n, const count_copies& src) { uninitialized_fill_n(buff, n, src); }); + + // Validate int is properly converted to bool + { + bool output[] = {false, true, false}; + fill(output, output + 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + { + bool output[] = {false, true, false}; + fill_n(output, 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + { + bool output[] = {false, true, false}; + uninitialized_fill(output, output + 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + { + bool output[] = {false, true, false}; + uninitialized_fill_n(output, 3, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } } diff --git a/tests/std/tests/VSO_0180469_ptr_cat/test.cpp b/tests/std/tests/VSO_0180469_ptr_cat/test.cpp index e0747b48caf..993ac08ecaf 100644 --- a/tests/std/tests/VSO_0180469_ptr_cat/test.cpp +++ b/tests/std/tests/VSO_0180469_ptr_cat/test.cpp @@ -257,23 +257,33 @@ void test_case_Equal_memcmp_is_safe_comparator() { #ifdef __cpp_lib_concepts // contiguous iterators should not change the answer - STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename vector::iterator, Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, typename vector::const_iterator, - Pr> == Expected); + if constexpr (!is_same_v && !is_same_v) { // vector::iterator is not contiguous + STATIC_ASSERT( + _Equal_memcmp_is_safe::iterator, typename vector::iterator, Pr> == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + typename vector::const_iterator, Pr> == Expected); + } STATIC_ASSERT( _Equal_memcmp_is_safe::iterator, typename array::iterator, Pr> == Expected); STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, typename array::const_iterator, Pr> == Expected); // Mixing contiguous iterators should not change the answer - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename vector::const_iterator, - Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, - typename vector::const_iterator, Pr> == Expected); - STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename array::iterator, Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename array::const_iterator, - Pr> == Expected); + if constexpr (!is_same_v && !is_same_v) { + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename vector::const_iterator, + Pr> == Expected); + } + + if constexpr (!is_same_v) { + STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + typename vector::const_iterator, Pr> == Expected); + } + + if constexpr (!is_same_v) { + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename array::iterator, + Pr> == Expected); + STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename array::const_iterator, + Pr> == Expected); + } // span iterators are contiguous STATIC_ASSERT( _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == Expected); @@ -335,7 +345,8 @@ void test_case_Equal_memcmp_is_safe() { } void equal_safe_test_cases() { - // memcmp is safe for non-bool integral types + // memcmp is safe for integral types + test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); @@ -377,10 +388,10 @@ void equal_safe_test_cases() { test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); - // memcmp is not safe for bool types - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + // memcmp is safe between bool and other integral types with the same size because we don't care about + // representations other than 0 and 1 + test_case_Equal_memcmp_is_safe(); + test_case_Equal_memcmp_is_safe(); // No enums test_case_Equal_memcmp_is_safe(); test_case_Equal_memcmp_is_safe(); diff --git a/tests/std/tests/VSO_0971246_legacy_await_headers/env.lst b/tests/std/tests/VSO_0971246_legacy_await_headers/env.lst index e162f31bdd7..ad91bfa4754 100644 --- a/tests/std/tests/VSO_0971246_legacy_await_headers/env.lst +++ b/tests/std/tests/VSO_0971246_legacy_await_headers/env.lst @@ -7,5 +7,4 @@ PM_CL="/EHsc /MT /std:c++latest /permissive-" PM_CL="/EHsc /MT /std:c++latest /permissive" PM_CL="/EHsc /MT /std:c++latest /permissive- /await" PM_CL="/EHsc /MT /std:c++latest /permissive /await" -PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive- /await" PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 34cc098956c..71cb23f8d3d 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -9,6 +9,7 @@ PM_CL="/DMEOW_HEADER=algorithm" PM_CL="/DMEOW_HEADER=any" PM_CL="/DMEOW_HEADER=array" PM_CL="/DMEOW_HEADER=atomic" +PM_CL="/DMEOW_HEADER=barrier" PM_CL="/DMEOW_HEADER=bit" PM_CL="/DMEOW_HEADER=bitset" PM_CL="/DMEOW_HEADER=charconv" @@ -35,6 +36,7 @@ PM_CL="/DMEOW_HEADER=iostream" PM_CL="/DMEOW_HEADER=iso646.h" PM_CL="/DMEOW_HEADER=istream" PM_CL="/DMEOW_HEADER=iterator" +PM_CL="/DMEOW_HEADER=latch" PM_CL="/DMEOW_HEADER=limits" PM_CL="/DMEOW_HEADER=list" PM_CL="/DMEOW_HEADER=locale" @@ -53,6 +55,7 @@ PM_CL="/DMEOW_HEADER=ranges" PM_CL="/DMEOW_HEADER=ratio" PM_CL="/DMEOW_HEADER=regex" PM_CL="/DMEOW_HEADER=scoped_allocator" +PM_CL="/DMEOW_HEADER=semaphore" PM_CL="/DMEOW_HEADER=set" PM_CL="/DMEOW_HEADER=shared_mutex" PM_CL="/DMEOW_HEADER=span"