From 75474b6eb4d8ca5a8590524959b48351377e7a7f Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 7 Nov 2020 20:21:12 +0100 Subject: [PATCH 01/20] Optimizations for contiguous iterators --- stl/inc/algorithm | 91 ++++++-- stl/inc/functional | 14 -- stl/inc/memory | 4 +- stl/inc/xmemory | 11 +- stl/inc/xutility | 209 +++++++++++++----- .../test.cpp | 7 + .../tests/P0896R4_ranges_alg_equal/test.cpp | 8 + .../tests/P0896R4_ranges_alg_find/test.cpp | 10 + .../test.cpp | 9 + 9 files changed, 262 insertions(+), 101 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 29d6bcbefe4..e6b54d63424 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -418,6 +418,29 @@ namespace ranges { template _Se, class _Ty, class _Pj> requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { + if constexpr (contiguous_iterator<_It> && sized_sentinel_for<_Se, _It> && + ((integral<_Ty> && _Is_character_or_bool>::value) +#ifdef __cpp_lib_byte + || (same_as<_Ty, byte> && same_as, byte>) +#endif // __cpp_lib_byte + ) && !is_volatile_v>>) { + if (!_STD is_constant_evaluated()) { + if (!_Within_limits(_First, _Val)) { + return _RANGES next(_STD move(_First), _Last); + } + + const auto _First_ptr = _STD to_address(_First); + const auto _Result = static_cast*>(_CSTD memchr(_First_ptr, + static_cast(_Val), static_cast(_Last - _First))); + if constexpr (is_pointer_v<_It>) { + return _Result ? _Result : _RANGES next(_First, _Last); + } else { + return _Result ? _RANGES next(_STD move(_First), _Result - _First_ptr) + : _RANGES next(_STD move(_First), _Last); + } + } + } + for (; _First != _Last; ++_First) { if (_STD invoke(_Proj, *_First) == _Val) { break; @@ -927,6 +950,16 @@ namespace ranges { template _NODISCARD static constexpr bool _Equal_count( _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + if constexpr (_Equal_memcmp_is_safe<_It1, _It2, + _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + if (!_STD is_constant_evaluated()) { + const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); + const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); + const auto _ByteSize = static_cast(_Count) * sizeof(iter_value_t<_It1>); + return _CSTD memcmp(_First1_ch, _First2_ch, _ByteSize) == 0; + } + } + for (; _Count != 0; ++_First1, (void) ++_First2, --_Count) { if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { return false; @@ -2161,9 +2194,9 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = reinterpret_cast(_First1); - const auto _First2_ch = reinterpret_cast(_First2); - const auto _Count = static_cast(reinterpret_cast(_Last2) - _First2_ch); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + const auto _Count = static_cast(reinterpret_cast(_To_address(_Last2)) - _First2_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } } @@ -2192,9 +2225,9 @@ bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Las template , int> = 0> bool _Equal_rev_pred_unchecked(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr) { // compare [_First1, ...) to [_First2, _Last2), memcmp optimization - const auto _First1_ch = reinterpret_cast(_First1); - const auto _First2_ch = reinterpret_cast(_First2); - const auto _Count = static_cast(reinterpret_cast(_Last2) - _First2_ch); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + const auto _Count = static_cast(reinterpret_cast(_To_address(_Last2)) - _First2_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } #endif // _HAS_IF_CONSTEXPR @@ -3878,17 +3911,19 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); const auto _ULast = _Get_unwrapped(_STD move(_Last)); if (!_STD is_constant_evaluated()) { - if constexpr (_Fill_memset_is_safe) { - const auto _Distance = static_cast(_ULast - _UFirst); - _Fill_memset(_UFirst, _Value, _Distance); - _Seek_wrapped(_First, _UFirst + _Distance); - return _First; - } else if constexpr (_Fill_zero_memset_is_safe) { - if (_Is_all_bits_zero(_Value)) { + if constexpr (sized_sentinel_for) { + if constexpr (_Fill_memset_is_safe) { const auto _Distance = static_cast(_ULast - _UFirst); - _Fill_zero_memset(_UFirst, _Distance); + _Fill_memset(_UFirst, _Value, _Distance); _Seek_wrapped(_First, _UFirst + _Distance); return _First; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Value)) { + const auto _Distance = static_cast(_ULast - _UFirst); + _Fill_zero_memset(_UFirst, _Distance); + _Seek_wrapped(_First, _UFirst + _Distance); + return _First; + } } } } @@ -4841,10 +4876,11 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_BidIt>(_UFirst, _ULast)); #if _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_pointer_t; - using _DestElem = remove_pointer_t; + using _Elem = remove_reference_t<_Iter_ref_t>; + using _DestElem = remove_reference_t<_Iter_ref_t>; constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, - is_pointer, is_trivially_copyable<_Elem>, negation>>; + bool_constant<_Iterators_are_contiguous>, is_trivially_copyable<_Elem>, + negation>>; constexpr size_t _Nx = sizeof(_Elem); #pragma warning(suppress : 6326) // Potential comparison of a constant with another constant @@ -4854,13 +4890,13 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { #endif // __cpp_lib_is_constant_evaluated { if constexpr (_Nx == 1) { - __std_reverse_copy_trivially_copyable_1(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_1(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else if constexpr (_Nx == 2) { - __std_reverse_copy_trivially_copyable_2(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_2(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else if constexpr (_Nx == 4) { - __std_reverse_copy_trivially_copyable_4(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_4(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else { - __std_reverse_copy_trivially_copyable_8(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_8(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } _UDest += _ULast - _UFirst; @@ -10131,6 +10167,19 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + using _Memcmp_classification_pred = + typename decltype(_Lex_compare_memcmp_classify(_First1, _First2, _Pred))::_Pred; + if constexpr (!same_as<_Memcmp_classification_pred, + void> && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + if (!_STD is_constant_evaluated()) { + const auto _Num1 = static_cast(_Last1 - _First1); + const auto _Num2 = static_cast(_Last2 - _First2); + const int _Ans = + _CSTD memcmp(_STD to_address(_First1), _STD to_address(_First2), (_STD min)(_Num1, _Num2)); + return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + } + } + for (;; ++_First1, (void) ++_First2) { if (_First2 == _Last2) { return false; diff --git a/stl/inc/functional b/stl/inc/functional index bfc928f5dd2..a218e139d41 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2245,20 +2245,6 @@ namespace ranges { using is_transparent = int; }; - // STRUCT ranges::greater - struct greater { - // clang-format off - template - requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489 - _NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept( - static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ { - return static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)); - } - // clang-format on - - using is_transparent = int; - }; - // STRUCT ranges::greater_equal struct greater_equal { // clang-format off diff --git a/stl/inc/memory b/stl/inc/memory index 006c97a1342..e41df954446 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -954,7 +954,7 @@ namespace ranges { if constexpr (_Use_memset_value_construct_v<_It>) { const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); const auto _Count = static_cast(_OFinal - _OFirst); - _CSTD memset(_OFirst, 0, _Count); + _CSTD memset(_STD to_address(_OFirst), 0, _Count); return _OFinal; } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -1004,7 +1004,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); if constexpr (_Use_memset_value_construct_v<_It>) { - _CSTD memset(_UFirst, 0, static_cast(_Count)); + _CSTD memset(_STD to_address(_UFirst), 0, static_cast(_Count)); _Seek_wrapped(_First, _UFirst + _Count); } else { _Uninitialized_backout _Backout{_STD move(_UFirst)}; diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 0d9926f45d4..30236993637 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1839,14 +1839,15 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c // FUNCTION TEMPLATE _Uninitialized_value_construct_n WITH ALLOCATOR template -_INLINE_VAR constexpr bool _Use_memset_value_construct_v = conjunction_v, - is_scalar<_Iter_value_t<_NoThrowFwdIt>>, negation>>>, - negation>>>; +_INLINE_VAR constexpr bool _Use_memset_value_construct_v = + conjunction_v>, is_scalar<_Iter_value_t<_NoThrowFwdIt>>, + negation>>>, + negation>>>; template _Ptr _Zero_range(const _Ptr _First, const _Ptr _Last) { // fill [_First, _Last) with zeroes - char* const _First_ch = reinterpret_cast(_First); - char* const _Last_ch = reinterpret_cast(_Last); + char* const _First_ch = reinterpret_cast(_To_address(_First)); + char* const _Last_ch = reinterpret_cast(_To_address(_Last)); _CSTD memset(_First_ch, 0, static_cast(_Last_ch - _First_ch)); return _Last; } diff --git a/stl/inc/xutility b/stl/inc/xutility index 03eca83ff36..c12e55d7572 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3451,6 +3451,20 @@ namespace ranges { using is_transparent = int; }; + // STRUCT ranges::greater + struct greater { + // clang-format off + template + requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489 + _NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept( + static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ { + return static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)); + } + // clang-format on + + using is_transparent = int; + }; + // CONCEPT ranges::common_range // clang-format off template @@ -4782,6 +4796,30 @@ _BidIt2 move_backward(_ExPo&&, _BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) noe #endif // _HAS_CXX17 // FUNCTION TEMPLATE fill + +// _Iterator_is_contiguous<_Iter> reports whether iterator is known to be contiguous. +// (Without concepts, this detection is limited, which will limit when we can activate the memset optimization.) + +#ifdef __cpp_lib_concepts +// When concepts are available, we can detect arbitrary contiguous iterators. +template +_INLINE_VAR constexpr bool _Iterator_is_contiguous = contiguous_iterator<_Iter>; + +template +_NODISCARD constexpr auto _To_address(const _Ptr& _Val) noexcept { + return _STD to_address(_Val); +} +#else // ^^^ defined(__cpp_lib_concepts) ^^^ / vvv !defined(__cpp_lib_concepts) vvv +// When concepts aren't available, we can detect pointers. (Iterators should be unwrapped before using this.) +template +_INLINE_VAR constexpr bool _Iterator_is_contiguous = is_pointer_v<_Iter>; + +template +_NODISCARD constexpr auto _To_address(const _Ptr& _Val) noexcept { + return _Val; +} +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + template struct _Is_character : false_type {}; // by default, not a character type @@ -4800,18 +4838,21 @@ struct _Is_character : true_type {}; // UTF-8 code units are sort-of ch #endif // __cpp_char8_t template -struct _Is_character_or_byte_or_bool : _Is_character<_Ty>::type {}; +struct _Is_character_or_bool : _Is_character<_Ty>::type {}; + +template <> +struct _Is_character_or_bool : true_type {}; + +template +struct _Is_character_or_byte_or_bool : _Is_character_or_bool<_Ty>::type {}; #ifdef __cpp_lib_byte template <> 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 > +template > _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&>>; @@ -4819,7 +4860,7 @@ _INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, template _INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; -template > +template > _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = conjunction_v, is_scalar<_Iter_value_t<_FwdIt>>, negation>>, negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; @@ -4827,15 +4868,16 @@ _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = template _INLINE_VAR constexpr bool _Fill_zero_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); +template +void _Fill_memset(_CtgIt _Dest, const _Ty _Val, const size_t _Count) { + // implicitly convert (a cast would suppress warnings); also handles _DestTy being bool + _Iter_value_t<_CtgIt> _Dest_val = _Val; + _CSTD memset(_To_address(_Dest), static_cast(_Dest_val), _Count); } -template -void _Fill_zero_memset(_DestTy* const _Dest, const size_t _Count) { - _CSTD memset(_Dest, 0, _Count * sizeof(_DestTy)); +template +void _Fill_zero_memset(_CtgIt _Dest, const size_t _Count) { + _CSTD memset(_To_address(_Dest), 0, _Count * sizeof(_Iter_value_t<_CtgIt>)); } template @@ -5096,9 +5138,10 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = reinterpret_cast(_UFirst1); - const auto _First2_ch = reinterpret_cast(_UFirst2); - const auto _Count = static_cast(reinterpret_cast(_ULast1) - _First1_ch); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + const auto _Count = + static_cast(reinterpret_cast(_To_address(_ULast1)) - _First1_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } } @@ -5127,9 +5170,9 @@ bool _Equal_unchecked(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr _ template , int> = 0> bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr) { // compare [_First1, _Last1) to [_First2, ...), memcmp optimization - const auto _First1_ch = reinterpret_cast(_First1); - const auto _First2_ch = reinterpret_cast(_First2); - const auto _Count = static_cast(reinterpret_cast(_Last1) - _First1_ch); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + const auto _Count = static_cast(reinterpret_cast(_To_address(_Last1)) - _First1_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } @@ -5297,6 +5340,8 @@ struct _Lex_compare_check_element_types_helper : true_type { template struct _Lex_compare_optimize { explicit _Lex_compare_optimize() = default; + + using _Pred = _Memcmp_pr; }; // optimization tag for lexicographical_compare template @@ -5310,18 +5355,36 @@ constexpr auto _Lex_compare_memcmp_classify(const _InIt1&, const _InIt2&, const return _Lex_compare_optimize{}; } -template -constexpr auto _Lex_compare_memcmp_classify(_Obj1* const&, _Obj2* const&, const less<_FTy>&) { - // return lex_compare optimization category for pointer iterators and less<_FTy> - return _Lex_compare_check_element_types, _Obj1, _Obj2, _FTy>{}; +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const less<_FTy>&) { + // return lex_compare optimization category for contiguous iterators and less<_FTy> + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; +} + +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const greater<_FTy>&) { + // return lex_compare optimization category for contiguous iterators and greater<_FTy> + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; } -template -constexpr auto _Lex_compare_memcmp_classify(_Obj1* const&, _Obj2* const&, const greater<_FTy>&) { - // return lex_compare optimization category for pointer iterators and greater<_FTy> - return _Lex_compare_check_element_types, _Obj1, _Obj2, _FTy>{}; +#ifdef __cpp_lib_concepts +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES less&) { + // return lex_compare optimization category for contiguous iterators and ranges::less + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; } +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES greater&) { + // return lex_compare optimization category for contiguous iterators and ranges::greater + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; +} +#endif // __cpp_lib_concepts + template _NODISCARD constexpr bool _Lex_compare_unchecked( _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Pr _Pred, _Lex_compare_optimize) { @@ -5337,9 +5400,9 @@ _NODISCARD constexpr bool _Lex_compare_unchecked( return _First1 == _Last1 && _First2 != _Last2; } -template +template _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( - _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Pr _Pred, _Lex_compare_optimize<_Memcmp_pr>) { + _CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2, _CtgIt2 _Last2, _Pr _Pred, _Lex_compare_optimize<_Memcmp_pr>) { // order [_First1, _Last1) vs. [_First2, _Last2) memcmp optimization #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -5349,7 +5412,7 @@ _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( (void) _Pred; const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _CSTD memcmp(_First1, _First2, _Num1 < _Num2 ? _Num1 : _Num2); + const int _Ans = _CSTD memcmp(_To_address(_First1), _To_address(_First2), _Num1 < _Num2 ? _Num1 : _Num2); return _Memcmp_pr{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } @@ -5411,17 +5474,17 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( using _Ty1 = remove_const_t>; using _Ty2 = remove_const_t>; - if constexpr (conjunction_v, is_pointer<_UIt1>, is_pointer<_UIt2>, - disjunction< + if constexpr ( + conjunction_v, bool_constant<_Iterators_are_contiguous<_UIt1, _UIt2>>, + disjunction< #ifdef __cpp_lib_byte - conjunction, is_same<_Ty2, byte>>, + conjunction, is_same<_Ty2, byte>>, #endif // __cpp_lib_byte - conjunction<_Is_character<_Ty1>, is_unsigned<_Ty1>, _Is_character<_Ty2>, - is_unsigned<_Ty2>>>>) { + conjunction<_Is_character<_Ty1>, is_unsigned<_Ty1>, _Is_character<_Ty2>, is_unsigned<_Ty2>>>>) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); - const int _Ans = _CSTD memcmp(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); + const int _Ans = _CSTD memcmp(_To_address(_UFirst1), _To_address(_UFirst2), (_STD min)(_Num1, _Num2)); if (_Ans == 0) { return _Num1 <=> _Num2; } else { @@ -5457,46 +5520,60 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( // FUNCTION TEMPLATE find template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, true_type, _Any_tag) { // signed _Elem, signed _Ty +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, true_type, _Any_tag, false_type) { // signed _Elem, signed _Ty return SCHAR_MIN <= _Val && _Val <= SCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, true_type) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, true_type, false_type) { // signed _Elem, unsigned _Ty, -1 == static_cast<_Ty>(-1) return _Val <= SCHAR_MAX || static_cast<_Ty>(SCHAR_MIN) <= _Val; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, false_type) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, false_type, false_type) { // signed _Elem, unsigned _Ty, -1 != static_cast<_Ty>(-1) return _Val <= SCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, true_type, _Any_tag) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, true_type, _Any_tag, false_type) { // unsigned _Elem, signed _Ty return 0 <= _Val && _Val <= UCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, false_type, _Any_tag) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, false_type, _Any_tag, false_type) { // unsigned _Elem, unsigned _Ty return _Val <= UCHAR_MAX; } +template +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, _Any_tag, _Any_tag, _Any_tag, true_type) { + // bool _Elem + return _Val == true || _Val == false; +} + template -_NODISCARD constexpr bool _Within_limits(_InIt, const _Ty& _Val) { // check whether _Val is within the limits of _Elem - using _Elem = remove_pointer_t<_InIt>; +_NODISCARD constexpr bool _Within_limits( + const _InIt&, const _Ty& _Val) { // check whether _Val is within the limits of _Elem + using _Elem = _Iter_value_t<_InIt>; return _Within_limits(_Val, bool_constant>{}, bool_constant>{}, - bool_constant<-1 == static_cast<_Ty>(-1)>{}); + bool_constant<-1 == static_cast<_Ty>(-1)>{}, bool_constant>{}); } template -_NODISCARD constexpr bool _Within_limits(_InIt, const bool&) { // bools are always within the limits of _Elem +_NODISCARD constexpr bool _Within_limits(const _InIt&, const bool&) { // bools are always within the limits of _Elem return true; } +#ifdef __cpp_lib_byte +template +_NODISCARD constexpr bool _Within_limits(const _InIt&, const byte&) { // bytes are only comparable with other bytes + return true; +} +#endif // __cpp_lib_byte + template _NODISCARD constexpr _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, const _Ty& _Val, false_type) { // find first matching _Val @@ -5518,22 +5595,36 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { - using _Elem = remove_pointer_t<_InIt>; + using _Elem = _Iter_value_t<_InIt>; return _Find_unchecked1(_First, _Last, static_cast<_Elem>(_Val), false_type{}); } #endif // __cpp_lib_is_constant_evaluated - _First = - static_cast<_InIt>(_CSTD memchr(_First, static_cast(_Val), static_cast(_Last - _First))); - return _First ? _First : _Last; + const auto _First_ptr = _To_address(_First); + const auto _Result = static_cast<_Iter_value_t<_InIt>*>( + _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); +#if _HAS_IF_CONSTEXPR + if constexpr (is_pointer_v<_InIt>) { + return _Result ? _Result : _Last; + } else +#endif // _HAS_IF_CONSTEXPR + { + return _Result ? _First + (_Result - _First_ptr) : _Last; + } } template _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(const _InIt _First, const _InIt _Last, const _Ty& _Val) { // find first matching _Val; choose optimization // activate optimization for pointers to (const) bytes and integral values - using _Memchr_opt = bool_constant< - is_integral_v<_Ty> && _Is_any_of_v<_InIt, char*, signed char*, unsigned char*, // - const char*, const signed char*, const unsigned char*>>; + using _Memchr_opt = conjunction>, + disjunction< // + conjunction, _Is_character_or_bool<_Iter_value_t<_InIt>>> +#ifdef __cpp_lib_byte + , + conjunction, is_same<_Iter_value_t<_InIt>, byte>> +#endif // __cpp_lib_byte + >, + is_volatile>>>; return _Find_unchecked1(_First, _Last, _Val, _Memchr_opt{}); } @@ -5662,9 +5753,9 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); #if _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_pointer_t; - constexpr bool _Allow_vectorization = - conjunction_v, _Is_trivially_swappable<_Elem>, negation>>; + using _Elem = remove_reference_t<_Iter_ref_t>; + constexpr bool _Allow_vectorization = conjunction_v>, + _Is_trivially_swappable<_Elem>, negation>>; constexpr size_t _Nx = sizeof(_Elem); #pragma warning(suppress : 6326) // Potential comparison of a constant with another constant @@ -5674,13 +5765,13 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse #endif // __cpp_lib_is_constant_evaluated { if constexpr (_Nx == 1) { - __std_reverse_trivially_swappable_1(_UFirst, _ULast); + __std_reverse_trivially_swappable_1(_To_address(_UFirst), _To_address(_ULast)); } else if constexpr (_Nx == 2) { - __std_reverse_trivially_swappable_2(_UFirst, _ULast); + __std_reverse_trivially_swappable_2(_To_address(_UFirst), _To_address(_ULast)); } else if constexpr (_Nx == 4) { - __std_reverse_trivially_swappable_4(_UFirst, _ULast); + __std_reverse_trivially_swappable_4(_To_address(_UFirst), _To_address(_ULast)); } else { - __std_reverse_trivially_swappable_8(_UFirst, _ULast); + __std_reverse_trivially_swappable_8(_To_address(_UFirst), _To_address(_ULast)); } return; diff --git a/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp b/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp index e621519332d..39bd5af4eca 100644 --- a/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp +++ b/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp @@ -345,4 +345,11 @@ int main() { assert(find(begin(sc), end(sc), 0xFFFFFFFFFFFFFF7FULL) == end(sc)); assert(find(begin(sc), end(sc), 0xFFFFFFFFFFFFFF00ULL) == end(sc)); } + + { // Test bools + const bool arr[]{true, true, true, false, true, false}; + assert(find(begin(arr), end(arr), false) == begin(arr) + 3); + assert(find(begin(arr), end(arr), true) == begin(arr)); + assert(find(begin(arr), end(arr), 2) == end(arr)); + } } diff --git a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp index c61c58e6227..cf6c3e49fc0 100644 --- a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp @@ -63,6 +63,14 @@ constexpr void smoke_test() { int const two_ints[] = {0, 1}; assert(!equal(one_int, two_ints, comp, proj, proj)); } + { + // Validate memcmp case + int arr1[3]{0, 2, 5}; + int arr2[3]{0, 2, 5}; + assert(equal(arr1, arr2)); + arr2[1] = 7; + assert(!equal(arr1, arr2)); + } } int main() { diff --git a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp index 768de98b7fc..f364a6823d2 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp @@ -48,6 +48,16 @@ struct instantiator { STATIC_ASSERT(same_as>); assert(result == wrapped_input.end()); } + { // Validate memchr case [found case] + char arr[5]{4, 8, 1, -15, 125}; + auto result = find(arr, 1); + assert(*result == 1); + } + { // Validate memchr case [not found case] + char arr[5]{4, 8, 1, -15, 125}; + auto result = find(arr, 10); + assert(result == end(arr)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp index 3d37682ccd9..5bc0e34358b 100644 --- a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp @@ -193,6 +193,15 @@ struct instantiator { empty1.begin(), empty1.end(), empty2.begin(), empty2.end(), less{}, get_first, get_second); assert(!result); } + { // Validate memcmp case + unsigned char arr1[3]{0, 1, 2}; + unsigned char arr2[3]{0, 1, 3}; + assert(lexicographical_compare(arr1, arr2)); + arr2[2] = 2; + assert(!lexicographical_compare(arr1, arr2)); + arr2[2] = 1; + assert(!lexicographical_compare(arr1, arr2)); + } } }; From b2d7591a7c5cbe119461e3f88e4ff7af92583b02 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 7 Nov 2020 20:32:07 +0100 Subject: [PATCH 02/20] Projections need to be same as identity --- stl/inc/algorithm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index e6b54d63424..07f56dc04e0 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -418,7 +418,7 @@ namespace ranges { template _Se, class _Ty, class _Pj> requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { - if constexpr (contiguous_iterator<_It> && sized_sentinel_for<_Se, _It> && + if constexpr (contiguous_iterator<_It> && sized_sentinel_for<_Se, _It> && same_as<_Pj, identity> && ((integral<_Ty> && _Is_character_or_bool>::value) #ifdef __cpp_lib_byte || (same_as<_Ty, byte> && same_as, byte>) @@ -10170,7 +10170,8 @@ namespace ranges { using _Memcmp_classification_pred = typename decltype(_Lex_compare_memcmp_classify(_First1, _First2, _Pred))::_Pred; if constexpr (!same_as<_Memcmp_classification_pred, - void> && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + void> && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2> && + same_as<_Pj1, identity> && same_as<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); From 30da25d63a36e10e17566d941b448dc23aef51f4 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 7 Nov 2020 21:16:32 +0100 Subject: [PATCH 03/20] clang-format --- stl/inc/algorithm | 6 +++--- stl/inc/xutility | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 07f56dc04e0..101b47b4e15 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2196,7 +2196,7 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F { const auto _First1_ch = reinterpret_cast(_To_address(_First1)); const auto _First2_ch = reinterpret_cast(_To_address(_First2)); - const auto _Count = static_cast(reinterpret_cast(_To_address(_Last2)) - _First2_ch); + const auto _Count = static_cast(reinterpret_cast(_To_address(_Last2)) - _First2_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } } @@ -10170,8 +10170,8 @@ namespace ranges { using _Memcmp_classification_pred = typename decltype(_Lex_compare_memcmp_classify(_First1, _First2, _Pred))::_Pred; if constexpr (!same_as<_Memcmp_classification_pred, - void> && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2> && - same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + void> && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2> // + && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); diff --git a/stl/inc/xutility b/stl/inc/xutility index c12e55d7572..c567fd7a20d 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5140,8 +5140,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co { const auto _First1_ch = reinterpret_cast(_To_address(_First1)); const auto _First2_ch = reinterpret_cast(_To_address(_First2)); - const auto _Count = - static_cast(reinterpret_cast(_To_address(_ULast1)) - _First1_ch); + const auto _Count = static_cast(reinterpret_cast(_To_address(_ULast1)) - _First1_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } } @@ -5520,7 +5519,8 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( // FUNCTION TEMPLATE find template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, true_type, _Any_tag, false_type) { // signed _Elem, signed _Ty +_NODISCARD constexpr bool _Within_limits( + const _Ty& _Val, true_type, true_type, _Any_tag, false_type) { // signed _Elem, signed _Ty return SCHAR_MIN <= _Val && _Val <= SCHAR_MAX; } @@ -5600,7 +5600,7 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, } #endif // __cpp_lib_is_constant_evaluated const auto _First_ptr = _To_address(_First); - const auto _Result = static_cast<_Iter_value_t<_InIt>*>( + const auto _Result = static_cast<_Iter_value_t<_InIt>*>( _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); #if _HAS_IF_CONSTEXPR if constexpr (is_pointer_v<_InIt>) { @@ -5756,7 +5756,7 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse using _Elem = remove_reference_t<_Iter_ref_t>; constexpr bool _Allow_vectorization = conjunction_v>, _Is_trivially_swappable<_Elem>, negation>>; - constexpr size_t _Nx = sizeof(_Elem); + constexpr size_t _Nx = sizeof(_Elem); #pragma warning(suppress : 6326) // Potential comparison of a constant with another constant if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) { From b1b6a83462bbcd7783df4a6b8d80d0804b966dd9 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 7 Nov 2020 21:19:52 +0100 Subject: [PATCH 04/20] clang-format please cooperate --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index c567fd7a20d..8af58975e39 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5753,7 +5753,7 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); #if _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_reference_t<_Iter_ref_t>; + using _Elem = remove_reference_t<_Iter_ref_t>; constexpr bool _Allow_vectorization = conjunction_v>, _Is_trivially_swappable<_Elem>, negation>>; constexpr size_t _Nx = sizeof(_Elem); From 6450aaf4d29deccb46fd174b33f4c77163b1d987 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 7 Nov 2020 21:46:25 +0100 Subject: [PATCH 05/20] Wrong iterators --- stl/inc/algorithm | 11 +++++++---- stl/inc/xutility | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 101b47b4e15..942dcb42295 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -432,11 +432,14 @@ namespace ranges { const auto _First_ptr = _STD to_address(_First); const auto _Result = static_cast*>(_CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); - if constexpr (is_pointer_v<_It>) { - return _Result ? _Result : _RANGES next(_First, _Last); + if (_Result) { + if constexpr (is_pointer_v<_It>) { + return _Result; + } else { + return _RANGES next(_STD move(_First), _Result - _First_ptr); + } } else { - return _Result ? _RANGES next(_STD move(_First), _Result - _First_ptr) - : _RANGES next(_STD move(_First), _Last); + return _RANGES next(_STD move(_First), _Last); } } } diff --git a/stl/inc/xutility b/stl/inc/xutility index 8af58975e39..d66058ff58f 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5138,8 +5138,8 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = reinterpret_cast(_To_address(_First1)); - const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + const auto _First1_ch = reinterpret_cast(_To_address(_UFirst1)); + const auto _First2_ch = reinterpret_cast(_To_address(_UFirst2)); const auto _Count = static_cast(reinterpret_cast(_To_address(_ULast1)) - _First1_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } From e6058d447be69736d28593c956649916611a548d Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 7 Nov 2020 22:13:48 +0100 Subject: [PATCH 06/20] remove_const --- stl/inc/algorithm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 942dcb42295..27a781f2343 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4879,7 +4879,7 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_BidIt>(_UFirst, _ULast)); #if _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_reference_t<_Iter_ref_t>; + using _Elem = remove_reference_t<_Iter_ref_t>>; using _DestElem = remove_reference_t<_Iter_ref_t>; constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, bool_constant<_Iterators_are_contiguous>, is_trivially_copyable<_Elem>, From 29529068924fd3aa22cc1dc7e56a22b221ca7442 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 7 Nov 2020 22:34:59 +0100 Subject: [PATCH 07/20] Fix find --- stl/inc/xutility | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index d66058ff58f..6555d7ab5f8 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5600,7 +5600,7 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, } #endif // __cpp_lib_is_constant_evaluated const auto _First_ptr = _To_address(_First); - const auto _Result = static_cast<_Iter_value_t<_InIt>*>( + const auto _Result = static_cast>*>( _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); #if _HAS_IF_CONSTEXPR if constexpr (is_pointer_v<_InIt>) { @@ -5624,7 +5624,7 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(const _InIt _First, const _InIt _L conjunction, is_same<_Iter_value_t<_InIt>, byte>> #endif // __cpp_lib_byte >, - is_volatile>>>; + negation>>>>; return _Find_unchecked1(_First, _Last, _Val, _Memchr_opt{}); } From 876358d4abc7d6dd40dee7bf931492a57d590cd0 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 14 Nov 2020 16:14:36 +0100 Subject: [PATCH 08/20] _Memcmp_ranges --- stl/inc/algorithm | 25 +++++-------------------- stl/inc/xutility | 21 +++++++++++---------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 27a781f2343..c64d13d6e34 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -956,10 +956,7 @@ namespace ranges { if constexpr (_Equal_memcmp_is_safe<_It1, _It2, _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { - const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); - const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); - const auto _ByteSize = static_cast(_Count) * sizeof(iter_value_t<_It1>); - return _CSTD memcmp(_First1_ch, _First2_ch, _ByteSize) == 0; + return _Memcmp_ranges(_First1, _First2, static_cast(_Count)) == 0; } } @@ -2197,10 +2194,7 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = reinterpret_cast(_To_address(_First1)); - const auto _First2_ch = reinterpret_cast(_To_address(_First2)); - const auto _Count = static_cast(reinterpret_cast(_To_address(_Last2)) - _First2_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_First1, _First2, static_cast(_Last2 - _First2)) == 0; } } @@ -2228,10 +2222,7 @@ bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Las template , int> = 0> bool _Equal_rev_pred_unchecked(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr) { // compare [_First1, ...) to [_First2, _Last2), memcmp optimization - const auto _First1_ch = reinterpret_cast(_To_address(_First1)); - const auto _First2_ch = reinterpret_cast(_To_address(_First2)); - const auto _Count = static_cast(reinterpret_cast(_To_address(_Last2)) - _First2_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_First1, _First2, static_cast(_Last2 - _First2)) == 0; } #endif // _HAS_IF_CONSTEXPR @@ -2377,12 +2368,7 @@ namespace ranges { constexpr bool _Optimize = _Equal_rev_pred_can_memcmp<_It1, _It2, _Se2, _Pr, _Pj1, _Pj2>; if constexpr (_Optimize) { if (!_STD is_constant_evaluated()) { - const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); - const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); - const auto _Count = - static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); - const bool _Eq = _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; - if (_Eq) { + if (_Memcmp_ranges(_First1, _First2, static_cast(_Last2 - _First2)) == 0) { _First1 += (_Last2 - _First2); return {true, _STD move(_First1)}; } else { @@ -10178,8 +10164,7 @@ namespace ranges { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = - _CSTD memcmp(_STD to_address(_First1), _STD to_address(_First2), (_STD min)(_Num1, _Num2)); + const int _Ans = _Memcmp_ranges(_First1, _First2, (_STD min)(_Num1, _Num2)); return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } } diff --git a/stl/inc/xutility b/stl/inc/xutility index b6702c5defd..d185443ed39 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5121,6 +5121,13 @@ template _INLINE_VAR constexpr bool _Equal_memcmp_is_safe = _Equal_memcmp_is_safe_helper, remove_const_t<_Iter2>, _Pr>; +template +_NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt2 _First2, const size_t _Count) { + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + return _CSTD memcmp(_First1_ch, _First2_ch, _Count * sizeof(_Iter_value_t<_CtgIt1>)); +} + #if _HAS_IF_CONSTEXPR template _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred) { @@ -5134,10 +5141,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = reinterpret_cast(_To_address(_UFirst1)); - const auto _First2_ch = reinterpret_cast(_To_address(_UFirst2)); - const auto _Count = static_cast(reinterpret_cast(_To_address(_ULast1)) - _First1_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_UFirst1, _UFirst2, static_cast(_ULast1 - _UFirst1)) == 0; } } @@ -5165,10 +5169,7 @@ bool _Equal_unchecked(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr _ template , int> = 0> bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr) { // compare [_First1, _Last1) to [_First2, ...), memcmp optimization - const auto _First1_ch = reinterpret_cast(_To_address(_First1)); - const auto _First2_ch = reinterpret_cast(_To_address(_First2)); - const auto _Count = static_cast(reinterpret_cast(_To_address(_Last1)) - _First1_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_First1, _First2, static_cast(_Last1 - _First1)) == 0; } template @@ -5407,7 +5408,7 @@ _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( (void) _Pred; const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _CSTD memcmp(_To_address(_First1), _To_address(_First2), _Num1 < _Num2 ? _Num1 : _Num2); + const int _Ans = _Memcmp_ranges(_First1, _First2, (_STD min)(_Num1, _Num2)); return _Memcmp_pr{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } @@ -5479,7 +5480,7 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); - const int _Ans = _CSTD memcmp(_To_address(_UFirst1), _To_address(_UFirst2), (_STD min)(_Num1, _Num2)); + const int _Ans = _Memcmp_ranges(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); if (_Ans == 0) { return _Num1 <=> _Num2; } else { From 3fbfec8a10c27ff5e3fff486819691a5dedad58c Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 14 Nov 2020 16:56:04 +0100 Subject: [PATCH 09/20] _Memchr_in_find_is_safe --- stl/inc/algorithm | 7 +------ stl/inc/xutility | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c64d13d6e34..8201f0894e5 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -418,12 +418,7 @@ namespace ranges { template _Se, class _Ty, class _Pj> requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { - if constexpr (contiguous_iterator<_It> && sized_sentinel_for<_Se, _It> && same_as<_Pj, identity> && - ((integral<_Ty> && _Is_character_or_bool>::value) -#ifdef __cpp_lib_byte - || (same_as<_Ty, byte> && same_as, byte>) -#endif // __cpp_lib_byte - ) && !is_volatile_v>>) { + if constexpr (_Memchr_in_find_is_safe<_It, _Ty> && sized_sentinel_for<_Se, _It> && same_as<_Pj, identity>) { if (!_STD is_constant_evaluated()) { if (!_Within_limits(_First, _Val)) { return _RANGES next(_STD move(_First), _Last); diff --git a/stl/inc/xutility b/stl/inc/xutility index d185443ed39..bddabf86fa1 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5571,6 +5571,16 @@ _NODISCARD constexpr bool _Within_limits(const _InIt&, const byte&) { // bytes a } #endif // __cpp_lib_byte +template +_INLINE_VAR constexpr bool _Memchr_in_find_is_safe = + _Iterator_is_contiguous<_Iter>&& + disjunction_v, _Is_character_or_bool<_Iter_value_t<_Iter>>> +#ifdef __cpp_lib_byte + , + conjunction, is_same<_Iter_value_t<_Iter>, byte>> +#endif // __cpp_lib_byte + > && !is_volatile_v>>; + template _NODISCARD constexpr _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, const _Ty& _Val, false_type) { // find first matching _Val @@ -5612,18 +5622,8 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, template _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(const _InIt _First, const _InIt _Last, const _Ty& _Val) { // find first matching _Val; choose optimization - // activate optimization for pointers to (const) bytes and integral values - using _Memchr_opt = conjunction>, - disjunction< // - conjunction, _Is_character_or_bool<_Iter_value_t<_InIt>>> -#ifdef __cpp_lib_byte - , - conjunction, is_same<_Iter_value_t<_InIt>, byte>> -#endif // __cpp_lib_byte - >, - negation>>>>; - - return _Find_unchecked1(_First, _Last, _Val, _Memchr_opt{}); + // activate optimization for contiguous iterators to (const) bytes and integral values + return _Find_unchecked1(_First, _Last, _Val, bool_constant<_Memchr_in_find_is_safe<_InIt, _Ty>>{}); } template From 094b2b28818649aba11eec0d83ed2f810605185f Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 5 Dec 2020 10:09:03 +0100 Subject: [PATCH 10/20] Simplify _Within_limits --- stl/inc/xutility | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index bddabf86fa1..bf2d4a2e0d0 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5555,8 +5555,8 @@ template _NODISCARD constexpr bool _Within_limits( const _InIt&, const _Ty& _Val) { // check whether _Val is within the limits of _Elem using _Elem = _Iter_value_t<_InIt>; - return _Within_limits(_Val, bool_constant>{}, bool_constant>{}, - bool_constant<-1 == static_cast<_Ty>(-1)>{}, bool_constant>{}); + return _Within_limits(_Val, is_signed<_Elem>{}, is_signed<_Ty>{}, bool_constant<-1 == static_cast<_Ty>(-1)>{}, + is_same<_Elem, bool>{}); } template From 0d417ea150acaf88279dca5d69c454bf90208e1a Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Wed, 6 Jan 2021 16:12:57 +0100 Subject: [PATCH 11/20] remove _HAS_IF_CONSTEXPR --- stl/inc/xutility | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index afbf0b06b9d..c527aea85f0 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5024,12 +5024,9 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, const auto _First_ptr = _To_address(_First); const auto _Result = static_cast>*>( _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); -#if _HAS_IF_CONSTEXPR if constexpr (is_pointer_v<_InIt>) { return _Result ? _Result : _Last; - } else -#endif // _HAS_IF_CONSTEXPR - { + } else { return _Result ? _First + (_Result - _First_ptr) : _Last; } } From acd8c3fba6d51d57c09f947983a72c6a48b611b3 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 9 Jan 2021 11:21:27 +0100 Subject: [PATCH 12/20] Apply suggestions from code review Co-authored-by: Stephan T. Lavavej --- stl/inc/xutility | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 9ace772ff9c..a13e8aa550b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4348,13 +4348,13 @@ _BidIt2 move_backward(_ExPo&&, _BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) noe // FUNCTION TEMPLATE fill -// _Iterator_is_contiguous<_Iter> reports whether iterator is known to be contiguous. +// _Iterator_is_contiguous<_Iter> reports whether an iterator is known to be contiguous. // (Without concepts, this detection is limited, which will limit when we can activate the memset optimization.) #ifdef __cpp_lib_concepts // When concepts are available, we can detect arbitrary contiguous iterators. template -_INLINE_VAR constexpr bool _Iterator_is_contiguous = contiguous_iterator<_Iter>; +inline constexpr bool _Iterator_is_contiguous = contiguous_iterator<_Iter>; template _NODISCARD constexpr auto _To_address(const _Iter& _Val) noexcept { @@ -4423,7 +4423,7 @@ _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe<_FwdIt, _Ty, false> = false template void _Fill_memset(_CtgIt _Dest, const _Ty _Val, const size_t _Count) { - // implicitly convert (a cast would suppress warnings); also handles _DestTy being bool + // implicitly convert (a cast would suppress warnings); also handles _Iter_value_t<_CtgIt> being bool _Iter_value_t<_CtgIt> _Dest_val = _Val; _CSTD memset(_To_address(_Dest), static_cast(_Dest_val), _Count); } From 23bf9228fcaac2fc6ddcb60eed400bc45232964c Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 9 Jan 2021 11:55:06 +0100 Subject: [PATCH 13/20] Code review --- stl/inc/algorithm | 16 +++++++++++----- stl/inc/xutility | 38 +++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 74ad5b8400f..c02acf2eb7d 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -914,7 +914,7 @@ namespace ranges { if constexpr (_Equal_memcmp_is_safe<_It1, _It2, _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { - return _Memcmp_ranges(_First1, _First2, static_cast(_Count)) == 0; + return _Memcmp_count(_First1, _First2, static_cast(_Count)) == 0; } } @@ -2151,7 +2151,7 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - return _Memcmp_ranges(_First1, _First2, static_cast(_Last2 - _First2)) == 0; + return _Memcmp_ranges(_First2, _Last2, _First1) == 0; } } @@ -2243,7 +2243,7 @@ namespace ranges { // clang-format off template concept _Equal_rev_pred_can_memcmp = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity> - && is_same_v<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; + && sized_sentinel_for<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; template _Se2, class _Pr, class _Pj1, class _Pj2> requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> @@ -2254,7 +2254,13 @@ namespace ranges { constexpr bool _Optimize = _Equal_rev_pred_can_memcmp<_It1, _It2, _Se2, _Pr, _Pj1, _Pj2>; if constexpr (_Optimize) { if (!_STD is_constant_evaluated()) { - if (_Memcmp_ranges(_First1, _First2, static_cast(_Last2 - _First2)) == 0) { + bool _Ans; + if constexpr (contiguous_iterator<_Se2>) { + _Ans = _Memcmp_ranges(_First2, _Last2, _First1) == 0; + } else { + _Ans = _Memcmp_count(_First1, _First2, static_cast(_Last2 - _First2)) == 0; + } + if (_Ans) { _First1 += (_Last2 - _First2); return {true, _STD move(_First1)}; } else { @@ -9733,7 +9739,7 @@ namespace ranges { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _Memcmp_ranges(_First1, _First2, (_STD min)(_Num1, _Num2)); + const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } } diff --git a/stl/inc/xutility b/stl/inc/xutility index a13e8aa550b..22f79e423ea 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4349,7 +4349,7 @@ _BidIt2 move_backward(_ExPo&&, _BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) noe // FUNCTION TEMPLATE fill // _Iterator_is_contiguous<_Iter> reports whether an iterator is known to be contiguous. -// (Without concepts, this detection is limited, which will limit when we can activate the memset optimization.) +// (Without concepts, this detection is limited, which will limit when we can activate optimizations.) #ifdef __cpp_lib_concepts // When concepts are available, we can detect arbitrary contiguous iterators. @@ -4373,6 +4373,12 @@ _NODISCARD constexpr auto _To_address(const _Iter& _Val) noexcept { } #endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +// _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. + +template +_INLINE_VAR constexpr bool _Iterators_are_contiguous = + _Iterator_is_contiguous<_Iter1>&& _Iterator_is_contiguous<_Iter2>; + template struct _Is_character : false_type {}; // by default, not a character type @@ -4596,20 +4602,6 @@ template _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = _Can_memcmp_elements<_Elem1, _Elem2> // && _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr>; -// _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. -// (Without concepts, this detection is limited, which will limit when we can activate the memcmp optimization.) - -#ifdef __cpp_lib_concepts -// When concepts are available, we can detect arbitrary contiguous iterators. -template -_INLINE_VAR constexpr bool _Iterators_are_contiguous = contiguous_iterator<_Iter1> // - && contiguous_iterator<_Iter2>; -#else // ^^^ defined(__cpp_lib_concepts) ^^^ / vvv !defined(__cpp_lib_concepts) vvv -// When concepts aren't available, we can detect pointers. (Iterators should be unwrapped before using this.) -template -_INLINE_VAR constexpr bool _Iterators_are_contiguous = conjunction_v, is_pointer<_Iter2>>; -#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ - // _Equal_memcmp_is_safe<_Iter1, _Iter2, _Pr> reports whether we can activate the memcmp optimization // for arbitrary iterators and predicates. // It ignores top-level constness on the iterators and on the elements. @@ -4623,7 +4615,15 @@ _INLINE_VAR constexpr bool _Equal_memcmp_is_safe = _Equal_memcmp_is_safe_helper, remove_const_t<_Iter2>, _Pr>; template -_NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt2 _First2, const size_t _Count) { +_NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2) { + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _Last1_ch = reinterpret_cast(_To_address(_Last1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + return _CSTD memcmp(_First1_ch, _First2_ch, _Last1_ch - _First1_ch); +} + +template +_NODISCARD int _Memcmp_count(_CtgIt1 _First1, _CtgIt2 _First2, const size_t _Count) { const auto _First1_ch = reinterpret_cast(_To_address(_First1)); const auto _First2_ch = reinterpret_cast(_To_address(_First2)); return _CSTD memcmp(_First1_ch, _First2_ch, _Count * sizeof(_Iter_value_t<_CtgIt1>)); @@ -4641,7 +4641,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - return _Memcmp_ranges(_UFirst1, _UFirst2, static_cast(_ULast1 - _UFirst1)) == 0; + return _Memcmp_ranges(_UFirst1, _ULast1, _UFirst2) == 0; } } @@ -4835,7 +4835,7 @@ _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( (void) _Pred; const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _Memcmp_ranges(_First1, _First2, (_STD min)(_Num1, _Num2)); + const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); return _Memcmp_pr{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } @@ -4907,7 +4907,7 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); - const int _Ans = _Memcmp_ranges(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); + const int _Ans = _Memcmp_count(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); if (_Ans == 0) { return _Num1 <=> _Num2; } else { From 9e83f105361d862524336b6ea29526837def0367 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 9 Jan 2021 11:56:55 +0100 Subject: [PATCH 14/20] clang-format --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 22f79e423ea..0ae73344901 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4617,7 +4617,7 @@ _INLINE_VAR constexpr bool _Equal_memcmp_is_safe = template _NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2) { const auto _First1_ch = reinterpret_cast(_To_address(_First1)); - const auto _Last1_ch = reinterpret_cast(_To_address(_Last1)); + const auto _Last1_ch = reinterpret_cast(_To_address(_Last1)); const auto _First2_ch = reinterpret_cast(_To_address(_First2)); return _CSTD memcmp(_First1_ch, _First2_ch, _Last1_ch - _First1_ch); } From f706346dfef6fcc1d4049bd8fbbf5321349e56af Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Sat, 9 Jan 2021 12:08:37 +0100 Subject: [PATCH 15/20] static_cast --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 0ae73344901..97bc4273971 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4619,7 +4619,7 @@ _NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2) const auto _First1_ch = reinterpret_cast(_To_address(_First1)); const auto _Last1_ch = reinterpret_cast(_To_address(_Last1)); const auto _First2_ch = reinterpret_cast(_To_address(_First2)); - return _CSTD memcmp(_First1_ch, _First2_ch, _Last1_ch - _First1_ch); + return _CSTD memcmp(_First1_ch, _First2_ch, static_cast(_Last1_ch - _First1_ch)); } template From 65128e6fe12b660ba3f6f5e362313a87aa2c332f Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Mon, 15 Mar 2021 09:36:58 +0100 Subject: [PATCH 16/20] Merge branch 'master' into contiguous-iterator-optimizations --- .gitattributes | 4 + CMakeLists.txt | 4 +- README.md | 24 +- azure-devops/checkout-sources.yml | 71 + azure-devops/cmake-configure-build.yml | 52 + azure-devops/create-vmss.ps1 | 5 +- azure-devops/cross-build.yml | 51 + azure-devops/native-build-test.yml | 45 + azure-devops/provision-image.ps1 | 65 +- azure-devops/run-build.yml | 130 -- azure-devops/run-tests.yml | 48 + azure-devops/vcpkg-dependencies.yml | 38 + azure-pipelines.yml | 134 +- docs/cgmanifest.json | 2 +- llvm-project | 2 +- stl/CMakeLists.txt | 5 +- stl/inc/__msvc_all_public_headers.hpp | 2 + stl/inc/algorithm | 1131 +++++++++-- stl/inc/array | 40 +- stl/inc/atomic | 130 +- stl/inc/bit | 31 +- stl/inc/bitset | 2 + stl/inc/charconv | 14 + stl/inc/chrono | 1612 ++++++++++++++- stl/inc/cmath | 88 +- stl/inc/compare | 29 + stl/inc/complex | 4 + stl/inc/condition_variable | 7 + stl/inc/coroutine | 33 +- stl/inc/deque | 23 + stl/inc/execution | 23 +- stl/inc/filesystem | 48 +- stl/inc/forward_list | 27 +- stl/inc/functional | 2 + stl/inc/future | 12 + stl/inc/hash_map | 37 +- stl/inc/hash_set | 32 +- stl/inc/header-units.json | 2 + stl/inc/iosfwd | 16 + stl/inc/iterator | 18 + stl/inc/list | 20 +- stl/inc/map | 22 + stl/inc/memory | 152 +- stl/inc/memory_resource | 2 +- stl/inc/mutex | 30 +- stl/inc/optional | 40 + stl/inc/ostream | 39 + stl/inc/queue | 14 + stl/inc/random | 60 +- stl/inc/ranges | 431 +++- stl/inc/regex | 110 +- stl/inc/scoped_allocator | 21 +- stl/inc/semaphore | 2 + stl/inc/set | 22 + stl/inc/shared_mutex | 32 +- stl/inc/source_location | 66 + stl/inc/stack | 14 + stl/inc/syncstream | 381 ++++ stl/inc/system_error | 103 +- stl/inc/thread | 14 + stl/inc/tuple | 36 +- stl/inc/type_traits | 32 + stl/inc/typeindex | 8 + stl/inc/unordered_map | 4 + stl/inc/unordered_set | 4 + stl/inc/utility | 13 + stl/inc/valarray | 6 + stl/inc/variant | 79 +- stl/inc/vector | 972 +++++---- stl/inc/xatomic.h | 4 + stl/inc/xcharconv.h | 3 + stl/inc/xcharconv_ryu.h | 96 +- stl/inc/xhash | 3 + stl/inc/xkeycheck.h | 5 +- stl/inc/xlocale | 4 +- stl/inc/xloctime | 64 +- stl/inc/xmemory | 627 ++++-- stl/inc/xpolymorphic_allocator.h | 10 + stl/inc/xstring | 1111 ++++++---- stl/inc/xtree | 6 + stl/inc/xutility | 47 +- stl/inc/yvals_core.h | 106 +- .../stl_atomic_wait.files.settings.targets | 1 + .../stl_base/stl.files.settings.targets | 14 +- stl/src/memory_resource.cpp | 2 +- stl/src/msvcp_atomic_wait.src | 2 + stl/src/special_math.cpp | 88 +- stl/src/syncstream.cpp | 84 + tests/CMakeLists.txt | 2 + tests/libcxx/expected_results.txt | 345 +--- tests/libcxx/lit.site.cfg.in | 1 + tests/libcxx/skipped_tests.txt | 352 ++-- tests/std/include/test_atomic_wait.hpp | 7 +- tests/std/include/test_death.hpp | 7 + tests/std/lit.site.cfg.in | 1 + tests/std/test.lst | 24 +- .../test.cpp | 2 + .../env.lst | 0 .../test.cpp | 162 ++ .../test.cpp | 82 - .../std/tests/Dev11_0836436_get_time/test.cpp | 58 + .../test.cpp | 1 + .../GH_000690_overaligned_function/test.cpp | 2 + .../tests/GH_001530_binomial_accuracy/env.lst | 4 + .../GH_001530_binomial_accuracy/test.cpp | 48 + .../env.lst | 4 + .../test.compile.pass.cpp | 52 + .../env.lst | 4 + .../test.cpp | 385 ++++ .../test.hpp | 156 ++ tests/std/tests/P0067R5_charconv/test.cpp | 10 +- tests/std/tests/P0088R3_variant/env.lst | 10 +- tests/std/tests/P0088R3_variant/test.cpp | 57 +- tests/std/tests/P0218R1_filesystem/test.cpp | 17 +- tests/std/tests/P0220R1_optional/test.cpp | 4 + tests/std/tests/P0220R1_searchers/test.cpp | 2 +- .../env.lst | 4 + .../test.compile.pass.cpp | 86 + .../env.lst | 4 + .../test.cpp | 1174 +++++++++++ .../env.lst | 4 + .../test.compile.pass.cpp | 186 ++ .../env.lst | 4 + .../test.cpp | 225 +++ .../env.lst | 4 + .../test.cpp | 64 + .../env.lst | 4 + .../test.cpp | 254 +++ .../env.lst | 4 + .../test.cpp | 181 ++ .../env.lst | 4 + .../test.cpp | 263 +++ .../env.lst | 4 + .../test.cpp | 106 + .../tests/P0784R7_library_machinery/env.lst | 4 + .../tests/P0784R7_library_machinery/test.cpp | 150 ++ .../test.cpp | 10 + .../P0896R4_ranges_alg_inplace_merge/env.lst | 4 + .../P0896R4_ranges_alg_inplace_merge/test.cpp | 60 + .../env.lst | 4 + .../test.cpp | 67 + .../P0896R4_ranges_alg_stable_sort/env.lst | 4 + .../P0896R4_ranges_alg_stable_sort/test.cpp | 55 + .../test.cpp | 129 +- .../test.cpp | 141 +- .../test.cpp | 15 + .../test.cpp | 15 + .../test.cpp | 114 +- .../test.cpp | 140 +- .../test.cpp | 15 + .../test.cpp | 15 + .../P0896R4_ranges_range_machinery/test.cpp | 5 +- tests/std/tests/P0896R4_views_drop/test.cpp | 131 +- tests/std/tests/P0896R4_views_iota/env.lst | 4 + tests/std/tests/P0896R4_views_iota/test.cpp | 299 +++ tests/std/tests/P0896R4_views_take/test.cpp | 137 +- tests/std/tests/P0898R3_concepts/test.cpp | 38 +- tests/std/tests/P0912R5_coroutine/env.lst | 19 +- tests/std/tests/P0912R5_coroutine/test.cpp | 29 +- .../tests/P0980R1_constexpr_strings/env.lst | 4 + .../tests/P0980R1_constexpr_strings/test.cpp | 1799 +++++++++++++++++ .../tests/P1004R2_constexpr_vector/env.lst | 4 + .../tests/P1004R2_constexpr_vector/test.cpp | 723 +++++++ .../P1004R2_constexpr_vector_bool/env.lst | 4 + .../P1004R2_constexpr_vector_bool/test.cpp | 643 ++++++ .../std/tests/P1208R6_source_location/env.lst | 4 + .../tests/P1208R6_source_location/header.h | 16 + .../tests/P1208R6_source_location/test.cpp | 160 ++ .../custom_format.py | 4 +- .../custombuild.pl | 9 +- .../test.cpp | 30 +- tests/std/tests/P1614R2_spaceship/env.lst | 7 + tests/std/tests/P1614R2_spaceship/test.cpp | 1144 +++++++++++ .../test.compile.pass.cpp | 6 + .../test.compile.pass.cpp | 1 + .../VSO_0102478_moving_allocators/test.cpp | 104 +- .../test.compile.pass.cpp | 101 +- .../VSO_0224478_scoped_allocator/test.cpp | 2 +- .../include_each_header_alone_matrix.lst | 3 + tests/tr1/expected_results.txt | 3 + tests/tr1/lit.site.cfg.in | 1 + tests/tr1/test.lst | 2 +- tests/tr1/tests/memory/test.cpp | 1 + tests/tr1/tests/memory1/test.cpp | 3 + tests/utils/stl/test/config.py | 29 +- tests/utils/stl/test/format.py | 30 +- tests/utils/stl/test/params.py | 2 +- tests/utils/stl/test/tests.py | 35 +- tools/CMakeLists.txt | 2 +- tools/inc/stljobs.h | 16 +- vcpkg | 2 +- 191 files changed, 17025 insertions(+), 2896 deletions(-) create mode 100644 azure-devops/checkout-sources.yml create mode 100644 azure-devops/cmake-configure-build.yml create mode 100644 azure-devops/cross-build.yml create mode 100644 azure-devops/native-build-test.yml delete mode 100644 azure-devops/run-build.yml create mode 100644 azure-devops/run-tests.yml create mode 100644 azure-devops/vcpkg-dependencies.yml create mode 100644 stl/inc/source_location create mode 100644 stl/inc/syncstream create mode 100644 stl/src/syncstream.cpp rename tests/std/tests/{Dev11_0135139_vector_bool_equality_perf => Dev11_0135139_vector_bool_comparisons}/env.lst (100%) create mode 100644 tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp delete mode 100644 tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp create mode 100644 tests/std/tests/GH_001530_binomial_accuracy/env.lst create mode 100644 tests/std/tests/GH_001530_binomial_accuracy/test.cpp create mode 100644 tests/std/tests/GH_001638_dllexport_derived_classes/env.lst create mode 100644 tests/std/tests/GH_001638_dllexport_derived_classes/test.compile.pass.cpp create mode 100644 tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst create mode 100644 tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp create mode 100644 tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_clocks/env.lst create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_dates/env.lst create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_dates_literals/env.lst create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_dates_literals/test.compile.pass.cpp create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_hms/env.lst create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_time_point_and_durations/env.lst create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_time_point_and_durations/test.cpp create mode 100644 tests/std/tests/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/env.lst create mode 100644 tests/std/tests/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/test.cpp create mode 100644 tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst create mode 100644 tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp create mode 100644 tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst create mode 100644 tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp create mode 100644 tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst create mode 100644 tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp create mode 100644 tests/std/tests/P0784R7_library_machinery/env.lst create mode 100644 tests/std/tests/P0784R7_library_machinery/test.cpp create mode 100644 tests/std/tests/P0896R4_ranges_alg_inplace_merge/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_partition/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_sort/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp create mode 100644 tests/std/tests/P0896R4_views_iota/env.lst create mode 100644 tests/std/tests/P0896R4_views_iota/test.cpp create mode 100644 tests/std/tests/P0980R1_constexpr_strings/env.lst create mode 100644 tests/std/tests/P0980R1_constexpr_strings/test.cpp create mode 100644 tests/std/tests/P1004R2_constexpr_vector/env.lst create mode 100644 tests/std/tests/P1004R2_constexpr_vector/test.cpp create mode 100644 tests/std/tests/P1004R2_constexpr_vector_bool/env.lst create mode 100644 tests/std/tests/P1004R2_constexpr_vector_bool/test.cpp create mode 100644 tests/std/tests/P1208R6_source_location/env.lst create mode 100644 tests/std/tests/P1208R6_source_location/header.h create mode 100644 tests/std/tests/P1208R6_source_location/test.cpp create mode 100644 tests/std/tests/P1614R2_spaceship/env.lst create mode 100644 tests/std/tests/P1614R2_spaceship/test.cpp diff --git a/.gitattributes b/.gitattributes index dc85e5fa21f..938b5267f7a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,6 +7,10 @@ # Ensure GitHub detects our C++ code as C++ code. /stl/inc/** linguist-language=C++ /stl/src/** linguist-language=C++ +*.h linguist-language=C++ + +# Ensure GitHub detects lit.cfg and lit.local.cfg as Python instead of HAProxy. +*.cfg linguist-language=Python # Ensure GitHub detects our Perl legacy test harness code as Perl code instead of Raku. *.pl linguist-language=Perl diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e712f36ad8..d5b7eba7e0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,13 @@ if (NOT DEFINED CMAKE_TOOLCHAIN_FILE AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/vcpkg set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake") endif() -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.19) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) project(msvc_standard_libraries LANGUAGES CXX) find_package(Boost REQUIRED) -set(VCLIBS_MIN_BOOST_VERSION 1.74.0) +set(VCLIBS_MIN_BOOST_VERSION 1.75.0) if("${Boost_VERSION}" VERSION_LESS "${VCLIBS_MIN_BOOST_VERSION}") message(FATAL_ERROR "Detected Boost version is too old (older than ${VCLIBS_MIN_BOOST_VERSION}).") endif() diff --git a/README.md b/README.md index 570e9cedcf9..f81f691f560 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ which ships as part of the MSVC toolset and the Visual Studio IDE. * Our [Status Chart][] displays our overall progress over time. * Join our [Discord server][]. -[![Build Status](https://dev.azure.com/vclibs/STL/_apis/build/status/microsoft.STL?branchName=master)][Pipelines] +[![Build Status](https://dev.azure.com/vclibs/STL/_apis/build/status/microsoft.STL?branchName=main)][Pipelines] # What This Repo Is Useful For @@ -57,12 +57,12 @@ issue. The [bug tag][] and [enhancement tag][] are being populated. # Goals -We're implementing the latest C++ Working Draft, currently [N4868][], which will eventually become the next C++ -International Standard, C++20. The terms Working Draft (WD) and Working Paper (WP) are interchangeable; we often +We're implementing the latest C++ Working Draft, currently [N4878][], which will eventually become the next C++ +International Standard. The terms Working Draft (WD) and Working Paper (WP) are interchangeable; we often informally refer to these drafts as "the Standard" while being aware of the difference. (There are other relevant Standards; for example, supporting `/std:c++14` and `/std:c++17` involves understanding how the C++14 and C++17 Standards differ from the Working Paper, and we often need to refer to the C Standard Library and ECMAScript regular -expression specifications.) +expression specifications.) We're currently prioritizing C++20 features before starting any work on C++23. Our primary goals are conformance, performance, usability, and compatibility. @@ -143,10 +143,10 @@ 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.9 Preview 2 or later. +1. Install Visual Studio 2019 16.10 Preview 1 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.18 or later, and [Ninja][] 1.8.2 or later. + * Otherwise, install [CMake][] 3.19 or later, and [Ninja][] 1.10.2 or later. 2. Open Visual Studio, and choose the "Clone or check out code" option. Enter the URL of this repository, `https://github.com/microsoft/STL`. 3. Open a terminal in the IDE with `` Ctrl + ` `` (by default) or press on "View" in the top bar, and then "Terminal". @@ -158,10 +158,10 @@ acquire this dependency. # How To Build With A Native Tools Command Prompt -1. Install Visual Studio 2019 16.9 Preview 2 or later. +1. Install Visual Studio 2019 16.10 Preview 1 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.18 or later, and [Ninja][] 1.8.2 or later. + * Otherwise, install [CMake][] 3.19 or later, and [Ninja][] 1.10.2 or later. 2. Open a command prompt. 3. Change directories to a location where you'd like a clone of this STL repository. 4. `git clone https://github.com/microsoft/STL` @@ -234,7 +234,7 @@ C:\Users\username\Desktop>dumpbin /IMPORTS .\example.exe | findstr msvcp # How To Run The Tests With A Native Tools Command Prompt 1. Follow either [How To Build With A Native Tools Command Prompt][] or [How To Build With The Visual Studio IDE][]. -2. Acquire [Python][] 3.9 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). +2. Acquire [Python][] 3.9.2 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). 3. Have LLVM's `bin` directory on the `PATH` (so `clang-cl.exe` is available). * We recommend selecting "C++ Clang tools for Windows" in the VS Installer. This will automatically add LLVM to the `PATH` of the x86 and x64 Native Tools Command Prompts, and will ensure that you're using a supported version. @@ -354,7 +354,7 @@ those features first the tests will begin passing unexpectedly for us and return this it is necessary to add a `PASS` entry to the `expected_results.txt` of the testsuite in question. The `UNSUPPORTED` result code means that the requirements for a test are not met and so it will not be run. Currently -all tests which use the `/BE` or `/clr:pure` options are unsupported. +all tests which use the `/clr` or `/clr:pure` options are unsupported. Also, the `/BE` option is unsupported for x64. The `SKIPPED` result code indicates that a given test was explicitly skipped by adding a `SKIPPED` entry to the `expected_results.txt`. A test may be skipped for a number of reasons, which include, but are not limited to: @@ -405,10 +405,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception [LWG issues]: https://cplusplus.github.io/LWG/lwg-toc.html [LWG tag]: https://github.com/microsoft/STL/issues?q=is%3Aopen+is%3Aissue+label%3ALWG [Microsoft Open Source Code of Conduct]: https://opensource.microsoft.com/codeofconduct/ -[N4868]: https://wg21.link/n4868 +[N4878]: https://wg21.link/n4878 [NOTICE.txt]: NOTICE.txt [Ninja]: https://ninja-build.org -[Pipelines]: https://dev.azure.com/vclibs/STL/_build/latest?definitionId=4&branchName=master +[Pipelines]: https://dev.azure.com/vclibs/STL/_build/latest?definitionId=4&branchName=main [Python]: https://www.python.org/downloads/windows/ [Roadmap]: https://github.com/microsoft/STL/wiki/Roadmap [Status Chart]: https://microsoft.github.io/STL/ diff --git a/azure-devops/checkout-sources.yml b/azure-devops/checkout-sources.yml new file mode 100644 index 00000000000..916f72572fa --- /dev/null +++ b/azure-devops/checkout-sources.yml @@ -0,0 +1,71 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +parameters: +- name: vcpkgSHAVar + type: string + default: vcpkgSHA +- name: llvmSHAVar + type: string + default: llvmSHA +steps: +- checkout: self + clean: true + submodules: false +- task: PowerShell@2 + displayName: 'Get submodule SHAs' + timeoutInMinutes: 1 + inputs: + targetType: inline + script: | + cd $(Build.SourcesDirectory) + $regexSubmoduleSHA = '^[ \-+]([0-9a-f]+) .*$' + $llvmSHA = git submodule status --cached llvm-project | %{$_ -replace $regexSubmoduleSHA, '$1'} + Write-Host "##vso[task.setvariable variable=${{ parameters.llvmSHAVar }};]$llvmSHA" + $vcpkgSHA = git submodule status --cached vcpkg | %{$_ -replace $regexSubmoduleSHA, '$1'} + Write-Host "##vso[task.setvariable variable=${{ parameters.vcpkgSHAVar }};]$vcpkgSHA" +- script: | + cd $(Build.SourcesDirectory) + if not exist "llvm-project" ( + mkdir llvm-project + ) + cd llvm-project + + if not exist ".git" ( + del /S /Q * + git init + ) + + git remote get-url llvm + if errorlevel 1 ( + git remote add llvm https://github.com/llvm/llvm-project.git + git config --local extensions.partialClone llvm + ) + + git fetch --filter=tree:0 --depth=1 llvm $(${{ parameters.llvmSHAVar }}) + git sparse-checkout init --cone + git sparse-checkout set libcxx/test libcxx/utils/libcxx llvm/utils/lit + git reset --quiet --hard FETCH_HEAD + git clean --quiet -x -d -f + displayName: "Checkout LLVM source" +- script: | + cd $(Build.SourcesDirectory) + if not exist "vcpkg" ( + mkdir vcpkg + ) + cd vcpkg + + if not exist ".git" ( + del /S /Q * + git init + ) + + git remote get-url vcpkg + if errorlevel 1 ( + git remote add vcpkg https://github.com/Microsoft/vcpkg.git + git config --local extensions.partialClone vcpkg + ) + + git fetch --filter=tree:0 --depth=1 vcpkg $(${{ parameters.vcpkgSHAVar }}) + git checkout -f FETCH_HEAD + displayName: "Checkout vcpkg source" diff --git a/azure-devops/cmake-configure-build.yml b/azure-devops/cmake-configure-build.yml new file mode 100644 index 00000000000..02e1cd93fa4 --- /dev/null +++ b/azure-devops/cmake-configure-build.yml @@ -0,0 +1,52 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +parameters: +- name: hostArch + type: string +- name: targetArch + type: string +- name: vcpkgLocationVar + type: string + default: vcpkgLocation +- name: targetPlatform + type: string +- name: buildOutputLocationVar + type: string + default: buildOutputLocation +- name: cmakeAdditionalFlags + type: string + default: '' +steps: +- task: PowerShell@2 + displayName: 'Get Test Parallelism' + timeoutInMinutes: 1 + inputs: + targetType: inline + script: | + $testParallelism = $env:NUMBER_OF_PROCESSORS - 2 + Write-Host "##vso[task.setvariable variable=testParallelism;]$testParallelism" +- script: | + if exist "$(${{ parameters.buildOutputLocationVar }})" ( + rmdir /S /Q "$(${{ parameters.buildOutputLocationVar }})" + ) + call "%PROGRAMFILES(X86)%\Microsoft Visual Studio\2019\Preview\Common7\Tools\VsDevCmd.bat" ^ + -host_arch=${{ parameters.hostArch }} -arch=${{ parameters.targetArch }} -no_logo + cmake ${{ parameters.cmakeAdditionalFlags}} -G Ninja ^ + -DCMAKE_TOOLCHAIN_FILE=$(${{ parameters.vcpkgLocationVar }})\scripts\buildsystems\vcpkg.cmake ^ + -DVCPKG_TARGET_TRIPLET=${{ parameters.targetPlatform }}-windows ^ + -DCMAKE_CXX_COMPILER=cl ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DLIT_FLAGS=$(litFlags) ^ + -DCMAKE_CXX_FLAGS=/analyze:autolog- ^ + -S $(Build.SourcesDirectory) -B $(${{ parameters.buildOutputLocationVar }}) + displayName: 'Configure the STL' + timeoutInMinutes: 2 + env: { TMP: $(tmpDir), TEMP: $(tmpDir) } +- script: | + call "%PROGRAMFILES(X86)%\Microsoft Visual Studio\2019\Preview\Common7\Tools\VsDevCmd.bat" ^ + -host_arch=${{ parameters.hostArch }} -arch=${{ parameters.targetArch }} -no_logo + cmake --build $(${{ parameters.buildOutputLocationVar }}) + displayName: 'Build the STL' + timeoutInMinutes: 10 + env: { TMP: $(tmpDir), TEMP: $(tmpDir) } diff --git a/azure-devops/create-vmss.ps1 b/azure-devops/create-vmss.ps1 index 1434ce0ac48..64fa971fdc8 100644 --- a/azure-devops/create-vmss.ps1 +++ b/azure-devops/create-vmss.ps1 @@ -17,9 +17,12 @@ or are running from Azure Cloud Shell. $ErrorActionPreference = 'Stop' +# https://aka.ms/azps-changewarnings +$Env:SuppressAzurePowerShellBreakingChangeWarnings = 'true' + $Location = 'westus2' $Prefix = 'StlBuild-' + (Get-Date -Format 'yyyy-MM-dd') -$VMSize = 'Standard_D32as_v4' +$VMSize = 'Standard_D32ds_v4' $ProtoVMName = 'PROTOTYPE' $LiveVMPrefix = 'BUILD' $WindowsServerSku = '2019-Datacenter' diff --git a/azure-devops/cross-build.yml b/azure-devops/cross-build.yml new file mode 100644 index 00000000000..6cbcb08adb6 --- /dev/null +++ b/azure-devops/cross-build.yml @@ -0,0 +1,51 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +parameters: +- name: hostArch + type: string + default: amd64 +- name: targetPlatform + type: string +- name: vsDevCmdArch + type: string +- name: buildOutputLocationVar + type: string + default: buildOutputLocation +- name: numShards + type: number + default: 8 +jobs: +- job: '${{ parameters.targetPlatform }}' + variables: + fixedFlags: '--timeout=240;--shuffle' + parallelismFlag: '-j$(testParallelism)' + xmlOutputFlag: '--xunit-xml-output=$(${{ parameters.buildOutputLocationVar }})/test-results.xml' + shardFlags: '--num-shards=$(System.TotalJobsInPhase);--run-shard=$(System.JobPositionInPhase)' + litFlags: '$(fixedFlags);$(parallelismFlag);$(xmlOutputFlag);$(shardFlags)' + strategy: + parallel: ${{ parameters.numShards }} + timeoutInMinutes: 360 + steps: + - script: | + if exist "$(tmpDir)" (rmdir /S /Q $(tmpDir)) + mkdir $(tmpDir) + displayName: 'Setup TMP Directory' + + - template: checkout-sources.yml + - template: vcpkg-dependencies.yml + parameters: + targetPlatform: ${{ parameters.targetPlatform }} + - template: cmake-configure-build.yml + parameters: + targetPlatform: ${{ parameters.targetPlatform }} + hostArch: ${{ parameters.hostArch }} + targetArch: ${{ parameters.vsDevCmdArch }} + cmakeAdditionalFlags: '-DTESTS_BUILD_ONLY=ON' + - template: run-tests.yml + parameters: + hostArch: ${{ parameters.hostArch }} + targetPlatform: ${{ parameters.targetPlatform }} + targetArch: ${{ parameters.vsDevCmdArch }} + displayName: 'Build Tests' + publishArtifact: false # disabled due to GH-1653 diff --git a/azure-devops/native-build-test.yml b/azure-devops/native-build-test.yml new file mode 100644 index 00000000000..17fa03732ae --- /dev/null +++ b/azure-devops/native-build-test.yml @@ -0,0 +1,45 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +parameters: +- name: targetPlatform + type: string +- name: vsDevCmdArch + type: string +- name: buildOutputLocationVar + type: string + default: buildOutputLocation +- name: numShards + type: number + default: 8 +jobs: +- job: '${{ parameters.targetPlatform }}' + variables: + fixedFlags: '--timeout=240;--shuffle' + parallelismFlag: '-j$(testParallelism)' + xmlOutputFlag: '--xunit-xml-output=$(${{ parameters.buildOutputLocationVar }})/test-results.xml' + shardFlags: '--num-shards=$(System.TotalJobsInPhase);--run-shard=$(System.JobPositionInPhase)' + litFlags: '$(fixedFlags);$(parallelismFlag);$(xmlOutputFlag);$(shardFlags)' + strategy: + parallel: ${{ parameters.numShards }} + timeoutInMinutes: 360 + steps: + - script: | + if exist "$(tmpDir)" (rmdir /S /Q $(tmpDir)) + mkdir $(tmpDir) + displayName: 'Setup TMP Directory' + + - template: checkout-sources.yml + - template: vcpkg-dependencies.yml + parameters: + targetPlatform: ${{ parameters.targetPlatform }} + - template: cmake-configure-build.yml + parameters: + targetPlatform: ${{ parameters.targetPlatform }} + targetArch: ${{ parameters.vsDevCmdArch }} + hostArch: ${{ parameters.vsDevCmdArch }} + - template: run-tests.yml + parameters: + hostArch: ${{ parameters.vsDevCmdArch }} + targetPlatform: ${{ parameters.targetPlatform }} + targetArch: ${{ parameters.vsDevCmdArch }} diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index ba9e940c575..66988bfd7ac 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -50,36 +50,75 @@ Function Get-TempFilePath { return Join-Path $tempPath $tempName } +<# +.SYNOPSIS +Downloads and extracts a ZIP file to a newly created temporary subdirectory. + +.DESCRIPTION +DownloadAndExtractZip returns a path containing the extracted contents. + +.PARAMETER Url +The URL of the ZIP file to download. +#> +Function DownloadAndExtractZip { + Param( + [String]$Url + ) + + if ([String]::IsNullOrWhiteSpace($Url)) { + throw 'Missing Url' + } + + $ZipPath = Get-TempFilePath -Extension 'zip' + & curl.exe -L -o $ZipPath -s -S $Url + $TempSubdirPath = Get-TempFilePath -Extension 'dir' + Expand-Archive -Path $ZipPath -DestinationPath $TempSubdirPath -Force + + return $TempSubdirPath +} + $TranscriptPath = 'C:\provision-image-transcript.txt' if ([string]::IsNullOrEmpty($AdminUserPassword)) { - Start-Transcript -Path $TranscriptPath + Start-Transcript -Path $TranscriptPath -UseMinimalHeader } else { Write-Host 'AdminUser password supplied; switching to AdminUser.' - $PsExecPath = Get-TempFilePath -Extension 'exe' - Write-Host "Downloading psexec to: $PsExecPath" - & curl.exe -L -o $PsExecPath -s -S https://live.sysinternals.com/PsExec64.exe + + # https://docs.microsoft.com/en-us/sysinternals/downloads/psexec + $PsToolsZipUrl = 'https://download.sysinternals.com/files/PSTools.zip' + Write-Host "Downloading: $PsToolsZipUrl" + $ExtractedPsToolsPath = DownloadAndExtractZip -Url $PsToolsZipUrl + $PsExecPath = Join-Path $ExtractedPsToolsPath 'PsExec64.exe' + + # https://github.com/PowerShell/PowerShell/releases/latest + $PowerShellZipUrl = 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.2/PowerShell-7.1.2-win-x64.zip' + Write-Host "Downloading: $PowerShellZipUrl" + $ExtractedPowerShellPath = DownloadAndExtractZip -Url $PowerShellZipUrl + $PwshPath = Join-Path $ExtractedPowerShellPath 'pwsh.exe' + $PsExecArgs = @( '-u', 'AdminUser', '-p', - $AdminUserPassword, + 'AdminUserPassword_REDACTED', '-accepteula', + '-i', '-h', - 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe', + $PwshPath, '-ExecutionPolicy', 'Unrestricted', '-File', $PSCommandPath ) - Write-Host "Executing: $PsExecPath $PsExecArgs" + $PsExecArgs[3] = $AdminUserPassword $proc = Start-Process -FilePath $PsExecPath -ArgumentList $PsExecArgs -Wait -PassThru Write-Host 'Reading transcript...' Get-Content -Path $TranscriptPath Write-Host 'Cleaning up...' - Remove-Item $PsExecPath + Remove-Item -Recurse -Path $ExtractedPsToolsPath + Remove-Item -Recurse -Path $ExtractedPowerShellPath exit $proc.ExitCode } @@ -100,7 +139,7 @@ $Workloads = @( $ReleaseInPath = 'Preview' $Sku = 'Enterprise' $VisualStudioBootstrapperUrl = 'https://aka.ms/vs/16/pre/vs_enterprise.exe' -$PythonUrl = 'https://www.python.org/ftp/python/3.9.0/python-3.9.0-amd64.exe' +$PythonUrl = 'https://www.python.org/ftp/python/3.9.2/python-3.9.2-amd64.exe' # https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk $WindowsDriverKitUrl = 'https://go.microsoft.com/fwlink/?linkid=2128854' @@ -315,7 +354,7 @@ Function PipInstall { try { Write-Host "Installing or upgrading $Package..." - python.exe -m pip install --upgrade $Package + python.exe -m pip install --progress-bar off --upgrade $Package Write-Host "Done installing or upgrading $Package." } catch { @@ -358,3 +397,9 @@ Write-Host 'Finished updating PATH!' PipInstall pip PipInstall psutil + +# https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/bcdedit--set#verification-settings +Write-Host 'Enabling test-signed kernel-mode drivers...' +bcdedit /set testsigning on + +Write-Host 'Done!' diff --git a/azure-devops/run-build.yml b/azure-devops/run-build.yml deleted file mode 100644 index 767b5c77bfe..00000000000 --- a/azure-devops/run-build.yml +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -jobs: -- job: '${{ parameters.targetPlatform }}_${{ parameters.shardNum }}' - timeoutInMinutes: 360 - - variables: - buildOutputLocation: 'D:\build\${{ parameters.targetPlatform }}' - litFlags: '-j$(testParallelism);--timeout=240;--shuffle;--xunit-xml-output=$(buildOutputLocation)/test-results.xml' - shardFlags: '--num-shards=${{ parameters.numShards }};--run-shard=${{ parameters.shardNum }}' - vcpkgLocation: '$(Build.SourcesDirectory)/vcpkg' - steps: - - script: | - if exist "$(tmpDir)" ( - rmdir /S /Q $(tmpDir) - ) - mkdir $(tmpDir) - displayName: 'Setup TMP Directory' - - checkout: self - clean: true - submodules: false - - task: PowerShell@2 - displayName: 'Get submodule SHAs' - timeoutInMinutes: 1 - inputs: - targetType: inline - script: | - cd $(Build.SourcesDirectory) - $regexSubmoduleSHA = '^[ \-+]([0-9a-f]+) .*$' - $llvmSHA = git submodule status --cached llvm-project | %{$_ -replace $regexSubmoduleSHA, '$1'} - Write-Host "##vso[task.setvariable variable=llvmSHA;]$llvmSHA" - $vcpkgSHA = git submodule status --cached vcpkg | %{$_ -replace $regexSubmoduleSHA, '$1'} - Write-Host "##vso[task.setvariable variable=vcpkgSHA;]$vcpkgSHA" - - script: | - cd $(Build.SourcesDirectory) - if not exist "llvm-project" ( - mkdir llvm-project - ) - cd llvm-project - git init - git remote add llvm https://github.com/llvm/llvm-project - git config --local extensions.partialClone llvm - git fetch --filter=tree:0 --depth=1 llvm $(llvmSHA) - git reset --quiet $(llvmSHA) - git sparse-checkout init --cone - git sparse-checkout set libcxx/test libcxx/utils/libcxx llvm/utils/lit - displayName: "Checkout LLVM source" - - script: | - cd $(Build.SourcesDirectory) - if not exist "vcpkg" ( - mkdir vcpkg - ) - cd vcpkg - git init - git remote add vcpkg https://github.com/Microsoft/vcpkg - git config --local extensions.partialClone vcpkg - git fetch --filter=tree:0 --depth=1 vcpkg $(vcpkgSHA) - git checkout $(vcpkgSHA) - displayName: "Checkout vcpkg source" - - task: Cache@2 - displayName: vcpkg/installed Caching - timeoutInMinutes: 10 - inputs: - key: '"${{ parameters.targetPlatform }}" | "$(vcpkgSHA)" | "2020-03-01.01"' - path: '$(vcpkgLocation)/installed' - cacheHitVar: CACHE_RESTORED - - task: run-vcpkg@0 - displayName: 'Run vcpkg to Install boost-build' - condition: and(ne(variables.CACHE_RESTORED, 'true'), contains('${{ parameters.targetPlatform }}', 'arm')) - timeoutInMinutes: 10 - inputs: - doNotUpdateVcpkg: true - vcpkgArguments: 'boost-build' - vcpkgDirectory: '$(vcpkgLocation)' - vcpkgTriplet: 'x86-windows' - env: { TMP: $(tmpDir), TEMP: $(tmpDir) } - - task: run-vcpkg@0 - displayName: 'Run vcpkg to Install boost-math' - condition: ne(variables.CACHE_RESTORED, 'true') - timeoutInMinutes: 10 - inputs: - doNotUpdateVcpkg: true - vcpkgArguments: 'boost-math' - vcpkgDirectory: '$(vcpkgLocation)' - vcpkgTriplet: '${{ parameters.targetPlatform }}-windows' - env: { TMP: $(tmpDir), TEMP: $(tmpDir) } - - task: PowerShell@2 - displayName: 'Get Test Parallelism' - timeoutInMinutes: 1 - inputs: - targetType: inline - script: | - $testParallelism = $env:NUMBER_OF_PROCESSORS - 2 - Write-Host "##vso[task.setvariable variable=testParallelism;]$testParallelism" - - script: | - if exist "$(buildOutputLocation)" ( - rmdir /S /Q "$(buildOutputLocation)" - ) - call "%PROGRAMFILES(X86)%\Microsoft Visual Studio\2019\Preview\Common7\Tools\VsDevCmd.bat" ^ - -host_arch=amd64 -arch=${{ parameters.vsDevCmdArch }} -no_logo - cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=$(vcpkgLocation)\scripts\buildsystems\vcpkg.cmake ^ - -DVCPKG_TARGET_TRIPLET=${{ parameters.targetPlatform }}-windows -DCMAKE_CXX_COMPILER=cl ^ - -DCMAKE_BUILD_TYPE=Release -DLIT_FLAGS=$(litFlags);$(shardFlags) ^ - -DCMAKE_CXX_FLAGS=/analyze:autolog- ^ - -S $(Build.SourcesDirectory) -B $(buildOutputLocation) - cmake --build $(buildOutputLocation) - displayName: 'Build the STL' - timeoutInMinutes: 10 - env: { TMP: $(tmpDir), TEMP: $(tmpDir) } - - task: CmdLine@2 - displayName: 'Run Tests' - timeoutInMinutes: 120 - condition: and(succeeded(), in('${{ parameters.targetPlatform }}', 'x64', 'x86')) - inputs: - workingDirectory: $(buildOutputLocation) - script: | - call "%PROGRAMFILES(X86)%\Microsoft Visual Studio\2019\Preview\Common7\Tools\VsDevCmd.bat" ^ - -host_arch=${{ parameters.vsDevCmdArch }} -arch=${{ parameters.vsDevCmdArch }} -no_logo - ctest -V - env: { TMP: $(tmpDir), TEMP: $(tmpDir) } - - task: PublishTestResults@2 - displayName: 'Publish Tests' - timeoutInMinutes: 10 - condition: and(succeededOrFailed(), in('${{ parameters.targetPlatform }}', 'x64', 'x86')) - inputs: - searchFolder: $(buildOutputLocation) - testResultsFormat: JUnit - testResultsFiles: '**/test-results.xml' - testRunTitle: 'test-${{ parameters.targetPlatform }}-${{ parameters.shardNum }}' diff --git a/azure-devops/run-tests.yml b/azure-devops/run-tests.yml new file mode 100644 index 00000000000..a247bfc43c4 --- /dev/null +++ b/azure-devops/run-tests.yml @@ -0,0 +1,48 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +parameters: +- name: buildOutputLocationVar + type: string + default: buildOutputLocation +- name: targetPlatform + type: string +- name: hostArch + type: string +- name: targetArch + type: string +- name: displayName + type: string + default: 'Run Tests' +- name: publishArtifact + type: boolean + default: false +steps: +- task: CmdLine@2 + displayName: ${{ parameters.displayName }} + timeoutInMinutes: 120 + condition: succeeded() + inputs: + workingDirectory: $(${{ parameters.buildOutputLocationVar }}) + script: | + call "%PROGRAMFILES(X86)%\Microsoft Visual Studio\2019\Preview\Common7\Tools\VsDevCmd.bat" ^ + -host_arch=${{ parameters.hostArch }} -arch=${{ parameters.targetArch }} -no_logo + ctest -V + env: { TMP: $(tmpDir), TEMP: $(tmpDir) } +- task: PublishTestResults@2 + displayName: 'Publish Tests' + timeoutInMinutes: 10 + condition: succeededOrFailed() + inputs: + searchFolder: $(${{ parameters.buildOutputLocationVar }}) + testResultsFormat: JUnit + testResultsFiles: '**/test-results.xml' + testRunTitle: 'test-${{ parameters.targetPlatform }}-$(System.JobPositionInPhase)' +- publish: $(${{ parameters.buildOutPutLocationVar }})/out + artifact: '${{ parameters.targetPlatform }}-$(System.JobPositionInPhase)-libs-$(System.JobId)' + condition: ${{ parameters.publishArtifact }} + displayName: 'Publish Libs and Headers Artifact' +- publish: $(${{ parameters.buildOutPutLocationVar }})/tests + artifact: '${{ parameters.targetPlatform }}-$(System.JobPositionInPhase)-tests-$(System.JobId)' + condition: ${{ parameters.publishArtifact }} + displayName: 'Publish Tests Artifact' diff --git a/azure-devops/vcpkg-dependencies.yml b/azure-devops/vcpkg-dependencies.yml new file mode 100644 index 00000000000..bd22c161de1 --- /dev/null +++ b/azure-devops/vcpkg-dependencies.yml @@ -0,0 +1,38 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +parameters: +- name: targetPlatform + type: string +- name: vcpkgLocationVar + type: string + default: vcpkgLocation +- name: vcpkgSHAVar + type: string + default: vcpkgSHA +steps: +- task: Cache@2 + displayName: vcpkg/installed Caching + timeoutInMinutes: 10 + inputs: + key: '"${{ parameters.targetPlatform }}" | "$(${{ parameters.vcpkgSHAVar }})" | "2020-03-01.01"' + path: '$(${{ parameters.vcpkgLocationVar }})/installed' + cacheHitVar: CACHE_RESTORED +- task: run-vcpkg@0 + displayName: 'Run vcpkg to Install boost-build' + condition: ne(variables.CACHE_RESTORED, 'true') + timeoutInMinutes: 10 + inputs: + doNotUpdateVcpkg: true + vcpkgArguments: 'boost-build' + vcpkgDirectory: '$(${{ parameters.vcpkgLocationVar }})' + vcpkgTriplet: 'x86-windows' +- task: run-vcpkg@0 + displayName: 'Run vcpkg to Install boost-math' + condition: ne(variables.CACHE_RESTORED, 'true') + timeoutInMinutes: 10 + inputs: + doNotUpdateVcpkg: true + vcpkgArguments: 'boost-math' + vcpkgDirectory: '$(${{ parameters.vcpkgLocationVar }})' + vcpkgTriplet: '${{ parameters.targetPlatform }}-windows' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3fa907668b9..8e8832a925f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,8 +5,10 @@ variables: tmpDir: 'D:\Temp' + buildOutputLocation: 'D:\build' + vcpkgLocation: '$(Build.SourcesDirectory)/vcpkg' -pool: 'StlBuild-2020-12-08-1' +pool: 'StlBuild-2021-03-02' stages: - stage: Code_Format @@ -56,131 +58,39 @@ stages: failOnStandardError: true arguments: '$(buildOutputLocation)/validate/validate.exe' env: { TMP: $(tmpDir), TEMP: $(tmpDir) } - - stage: Build_And_Test + + - stage: Build_And_Test_x86 + dependsOn: Code_Format displayName: 'Build and Test' jobs: - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x86 - vsDevCmdArch: x86 - numShards: 8 - shardNum: 1 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x86 - vsDevCmdArch: x86 - numShards: 8 - shardNum: 2 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x86 - vsDevCmdArch: x86 - numShards: 8 - shardNum: 3 - - - template: azure-devops/run-build.yml + - template: azure-devops/native-build-test.yml parameters: targetPlatform: x86 vsDevCmdArch: x86 - numShards: 8 - shardNum: 4 - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x86 - vsDevCmdArch: x86 - numShards: 8 - shardNum: 5 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x86 - vsDevCmdArch: x86 - numShards: 8 - shardNum: 6 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x86 - vsDevCmdArch: x86 - numShards: 8 - shardNum: 7 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x86 - vsDevCmdArch: x86 - numShards: 8 - shardNum: 8 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x64 - vsDevCmdArch: amd64 - numShards: 8 - shardNum: 1 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x64 - vsDevCmdArch: amd64 - numShards: 8 - shardNum: 2 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x64 - vsDevCmdArch: amd64 - numShards: 8 - shardNum: 3 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x64 - vsDevCmdArch: amd64 - numShards: 8 - shardNum: 4 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x64 - vsDevCmdArch: amd64 - numShards: 8 - shardNum: 5 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x64 - vsDevCmdArch: amd64 - numShards: 8 - shardNum: 6 - - - template: azure-devops/run-build.yml - parameters: - targetPlatform: x64 - vsDevCmdArch: amd64 - numShards: 8 - shardNum: 7 - - - template: azure-devops/run-build.yml + - stage: Build_And_Test_x64 + dependsOn: Build_And_Test_x86 + displayName: 'Build and Test' + jobs: + - template: azure-devops/native-build-test.yml parameters: targetPlatform: x64 vsDevCmdArch: amd64 - numShards: 8 - shardNum: 8 - - template: azure-devops/run-build.yml + - stage: Build_ARM + dependsOn: Build_And_Test_x86 + displayName: 'Build' + jobs: + - template: azure-devops/cross-build.yml parameters: targetPlatform: arm vsDevCmdArch: arm - numShards: 1 - shardNum: 1 - - template: azure-devops/run-build.yml + - stage: Build_ARM64 + dependsOn: Build_And_Test_x86 + displayName: 'Build' + jobs: + - template: azure-devops/cross-build.yml parameters: targetPlatform: arm64 vsDevCmdArch: arm64 - numShards: 1 - shardNum: 1 diff --git a/docs/cgmanifest.json b/docs/cgmanifest.json index 89ca6e7b688..6e1b6634269 100644 --- a/docs/cgmanifest.json +++ b/docs/cgmanifest.json @@ -14,7 +14,7 @@ "type": "git", "git": { "repositoryUrl": "https://github.com/microsoft/STL.git", - "commitHash": "c70b7a830eda523a69934ba949ac700da2c0dfd2" + "commitHash": "355f8f560ecbde3a8832a5893ce7b4da7840549d" } } }, diff --git a/llvm-project b/llvm-project index a668ad92d5e..60575179041 160000 --- a/llvm-project +++ b/llvm-project @@ -1 +1 @@ -Subproject commit a668ad92d5e2161e07e1a435a19ea5072f52a989 +Subproject commit 605751790418ca4fb1df1e94dfbac34cfcc1b96f diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 6f2686e6de2..ba137d5fefb 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -177,6 +177,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/semaphore ${CMAKE_CURRENT_LIST_DIR}/inc/set ${CMAKE_CURRENT_LIST_DIR}/inc/shared_mutex + ${CMAKE_CURRENT_LIST_DIR}/inc/source_location ${CMAKE_CURRENT_LIST_DIR}/inc/span ${CMAKE_CURRENT_LIST_DIR}/inc/sstream ${CMAKE_CURRENT_LIST_DIR}/inc/stack @@ -186,6 +187,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/string ${CMAKE_CURRENT_LIST_DIR}/inc/string_view ${CMAKE_CURRENT_LIST_DIR}/inc/strstream + ${CMAKE_CURRENT_LIST_DIR}/inc/syncstream ${CMAKE_CURRENT_LIST_DIR}/inc/system_error ${CMAKE_CURRENT_LIST_DIR}/inc/thread ${CMAKE_CURRENT_LIST_DIR}/inc/tuple @@ -396,6 +398,7 @@ set(SOURCES_SATELLITE_2 set(SOURCES_SATELLITE_ATOMIC_WAIT ${CMAKE_CURRENT_LIST_DIR}/src/atomic_wait.cpp ${CMAKE_CURRENT_LIST_DIR}/src/parallel_algorithms.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/syncstream.cpp ) set(SOURCES_SATELLITE_CODECVT_IDS @@ -495,7 +498,7 @@ function(add_stl_dlls D_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIO file(WRITE "${_ATOMIC_WAIT_DEF_NAME}" "${_ATOMIC_WAIT_DEF_CONTENTS}") add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${_ATOMIC_WAIT_DEF_NAME}") - target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") + target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_NAME "msvcp140_atomic_wait${D_SUFFIX}${VCLIBS_SUFFIX}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES OUTPUT_NAME "${_ATOMIC_WAIT_OUTPUT_NAME}") diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index b238fdb7602..9889036b41b 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 3ffd654f651..73a76c91cec 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -41,6 +41,9 @@ _STD_BEGIN // COMMON SORT PARAMETERS _INLINE_VAR constexpr int _ISORT_MAX = 32; // maximum size for insertion sort +template +_INLINE_VAR constexpr auto _Isort_max = _Iter_diff_t<_It>{_ISORT_MAX}; + // STRUCT TEMPLATE _Optimistic_temporary_buffer template constexpr ptrdiff_t _Temporary_buffer_size(const _Diff _Value) noexcept { @@ -556,7 +559,7 @@ namespace ranges { template _Se, class _Pj = identity, indirect_binary_predicate, projected<_It, _Pj>> _Pr = ranges::equal_to> - _NODISCARD constexpr _It operator()(_It _First, const _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); auto _UResult = _Adjacent_find_unchecked( @@ -1725,6 +1728,19 @@ namespace ranges { using move_result = in_out_result<_In, _Out>; // VARIABLE ranges::move + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_movable<_It, _Out> + constexpr move_result<_It, _Out> _Move_unchecked(_It _First, const _Se _Last, _Out _Result) { + // clang-format on + + for (; _First != _Last; ++_First, (void) ++_Result) { + *_Result = _RANGES iter_move(_First); + } + + return {_STD move(_First), _STD move(_Result)}; + } + class _Move_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -1733,39 +1749,27 @@ namespace ranges { template _Se, weakly_incrementable _Out> requires indirectly_movable<_It, _Out> constexpr move_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const { + // clang-format on _Adl_verify_range(_First, _Last); - auto _UResult = _Move_unchecked( + auto _UResult = _RANGES _Move_unchecked( _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Result)); _Seek_wrapped(_First, _STD move(_UResult.in)); return {_STD move(_First), _STD move(_UResult.out)}; } + // clang-format off template requires indirectly_movable, _Out> constexpr move_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { - auto _First = _RANGES begin(_Range); - auto _UResult = _Move_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result)); + // clang-format on + auto _First = _RANGES begin(_Range); + auto _UResult = + _RANGES _Move_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result)); _Seek_wrapped(_First, _STD move(_UResult.in)); return {_STD move(_First), _STD move(_UResult.out)}; } - // clang-format on - - private: - template - _NODISCARD static constexpr move_result<_It, _Out> _Move_unchecked(_It _First, const _Se _Last, _Out _Result) { - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); - _STL_INTERNAL_STATIC_ASSERT(indirectly_movable<_It, _Out>); - - for (; _First != _Last; ++_First, (void) ++_Result) { - *_Result = _RANGES iter_move(_First); - } - - return {_STD move(_First), _STD move(_Result)}; - } }; inline constexpr _Move_fn move{_Not_quite_object::_Construct_tag{}}; @@ -1779,7 +1783,7 @@ namespace ranges { // concept-constrained for strict enforcement as it is used by several algorithms template requires indirectly_movable<_It1, _It2> - _NODISCARD constexpr _It2 _Move_backward_common(const _It1 _First, _It1 _Last, _It2 _Result) { + constexpr _It2 _Move_backward_common(const _It1 _First, _It1 _Last, _It2 _Result) { if constexpr (_Ptr_move_cat<_It1, _It2>::_Trivially_copyable) { if (!_STD is_constant_evaluated()) { return _Copy_backward_memmove(_First, _Last, _Result); @@ -4646,74 +4650,71 @@ namespace ranges { #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::rotate - class _Rotate_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; + template + _NODISCARD constexpr subrange<_It> _Reverse_until_mid_unchecked(_It _First, const _It _Mid, _It _Last) { + // reverse until either _First or _Last hits _Mid + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); - template _Se> - constexpr subrange<_It> operator()(_It _First, _It _Mid, _Se _Last) const { - _Adl_verify_range(_First, _Mid); - _Adl_verify_range(_Mid, _Last); - auto _UResult = _Rotate_unchecked( - _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), _Get_unwrapped(_STD move(_Last))); + do { + _RANGES iter_swap(_First, --_Last); + } while (++_First != _Mid && _Last != _Mid); - return _Rewrap_subrange>(_First, _STD move(_UResult)); - } + return {_STD move(_First), _STD move(_Last)}; + } - // clang-format off - template - requires permutable> - constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, iterator_t<_Rng> _Mid) const { - // clang-format on - _Adl_verify_range(_RANGES begin(_Range), _Mid); - _Adl_verify_range(_Mid, _RANGES end(_Range)); - auto _UResult = _Rotate_unchecked(_Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range)); + template _Se> + _NODISCARD constexpr subrange<_It> _Rotate_unchecked(_It _First, _It _Mid, _Se _Last) { + // Exchange the ranges [_First, _Mid) and [_Mid, _Last) + // that is, rotates [_First, _Last) left by distance(_First, _Mid) positions - return _Rewrap_subrange>(_Mid, _STD move(_UResult)); + if (_First == _Mid) { + auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); + return {_Final, _Final}; } - private: - template - _NODISCARD static constexpr subrange<_It> _Rotate_unchecked(_It _First, _It _Mid, _Se _Last) { - // Exchange the ranges [_First, _Mid) and [_Mid, _Last) - // that is, rotates [_First, _Last) left by distance(_First, _Mid) positions - _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - - if (_First == _Mid) { - auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); - return {_Final, _Final}; - } + if (_Mid == _Last) { + return {_STD move(_First), _STD move(_Mid)}; + } - if (_Mid == _Last) { - return {_STD move(_First), _STD move(_Mid)}; - } + if constexpr (bidirectional_iterator<_It>) { + _Reverse_common(_First, _Mid); + auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); + _Reverse_common(_Mid, _Final); - if constexpr (bidirectional_iterator<_It>) { - _Reverse_common(_First, _Mid); - auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); - _Reverse_common(_Mid, _Final); + if constexpr (random_access_iterator<_It>) { + _Reverse_common(_First, _Final); + _First += _Final - _Mid; - if constexpr (random_access_iterator<_It>) { - _Reverse_common(_First, _Final); - _First += _Final - _Mid; + return {_STD move(_First), _STD move(_Final)}; + } else { + const auto _Result = _RANGES _Reverse_until_mid_unchecked(_STD move(_First), _Mid, _Final); + auto _Mid_first = _Result.begin(); + auto _Mid_last = _Result.end(); + _Reverse_common(_Mid_first, _Mid_last); - return {_STD move(_First), _STD move(_Final)}; + if (_Mid_first == _Mid) { + return {_STD move(_Mid_last), _STD move(_Final)}; } else { - const auto _Result = _Reverse_until_mid_unchecked(_STD move(_First), _Mid, _Final); - auto _Mid_first = _Result.begin(); - auto _Mid_last = _Result.end(); - _Reverse_common(_Mid_first, _Mid_last); - - if (_Mid_first == _Mid) { - return {_STD move(_Mid_last), _STD move(_Final)}; - } else { - return {_STD move(_Mid_first), _STD move(_Final)}; - } + return {_STD move(_Mid_first), _STD move(_Final)}; } - } else { - auto _Next = _Mid; - do { // rotate the first cycle + } + } else { + auto _Next = _Mid; + do { // rotate the first cycle + _RANGES iter_swap(_First, _Next); + ++_First; + ++_Next; + if (_First == _Mid) { + _Mid = _Next; + } + } while (_Next != _Last); + + auto _Begin = _First; + + while (_Mid != _Last) { // rotate subsequent cycles + _Next = _Mid; + do { _RANGES iter_swap(_First, _Next); ++_First; ++_Next; @@ -4721,36 +4722,35 @@ namespace ranges { _Mid = _Next; } } while (_Next != _Last); - - auto _Begin = _First; - - while (_Mid != _Last) { // rotate subsequent cycles - _Next = _Mid; - do { - _RANGES iter_swap(_First, _Next); - ++_First; - ++_Next; - if (_First == _Mid) { - _Mid = _Next; - } - } while (_Next != _Last); - } - return {_STD move(_Begin), _STD move(_Mid)}; } + return {_STD move(_Begin), _STD move(_Mid)}; } + } - template - _NODISCARD static constexpr subrange<_It> _Reverse_until_mid_unchecked(_It _First, const _It _Mid, _It _Last) { - // reverse until either _First or _Last hits _Mid - _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); - _STL_INTERNAL_CHECK(_First != _Mid); - _STL_INTERNAL_CHECK(_Mid != _Last); + class _Rotate_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; - do { - _RANGES iter_swap(_First, --_Last); - } while (++_First != _Mid && _Last != _Mid); + template _Se> + constexpr subrange<_It> operator()(_It _First, _It _Mid, _Se _Last) const { + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + auto _UResult = _RANGES _Rotate_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), _Get_unwrapped(_STD move(_Last))); - return {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template + requires permutable> + constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, iterator_t<_Rng> _Mid) const { + // clang-format on + _Adl_verify_range(_RANGES begin(_Range), _Mid); + _Adl_verify_range(_Mid, _RANGES end(_Range)); + auto _UResult = _RANGES _Rotate_unchecked(_Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range)); + + return _Rewrap_subrange>(_Mid, _STD move(_UResult)); } }; @@ -5566,7 +5566,7 @@ template _BidIt _Stable_partition_unchecked(_BidIt _First, _BidIt _Last, _Pr _Pred) { // partition preserving order of equivalents for (;;) { - if (_First == _Last) { // the input range range is true (already partitioned) + if (_First == _Last) { // the input range is true (already partitioned) return _First; } @@ -5612,6 +5612,209 @@ _BidIt stable_partition(_ExPo&&, _BidIt _First, _BidIt _Last, _Pr _Pred) noexcep } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::stable_partition + template + _It _Buffered_rotate_common(const _It _First, const _It _Mid, const _It _Last, const iter_difference_t<_It> _Count1, + const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity) { + // rotate [_First, _Last) using temp buffer + _STL_INTERNAL_CHECK(_Count1 == _RANGES distance(_First, _Mid)); + _STL_INTERNAL_CHECK(_Count2 == _RANGES distance(_Mid, _Last)); + + if (_Count1 == 0) { + return _Last; + } + + if (_Count2 == 0) { + return _First; + } + + if (_Count1 <= _Count2 && _Count1 <= _Capacity) { // buffer left range, then move parts + _Uninitialized_backout*> _Backout{ + _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Temp_ptr, _Temp_ptr + _Count1).out}; + const _It _New_mid = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)).out; + _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _New_mid); + return _New_mid; + } + + if (_Count2 <= _Capacity) { // buffer right range, then move parts + _Uninitialized_backout*> _Backout{ + _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Temp_ptr, _Temp_ptr + _Count2).out}; + _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); + return _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _STD move(_First)).out; + } + + // buffer too small, rotate in place + return _RANGES _Rotate_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_Last)).begin(); + } + + class _Stable_partition_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + requires permutable<_It> + subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + + auto _UResult = + _Stable_partition_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template , _Pj>> _Pr> + requires permutable> + borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + // clang-format on + auto _ULast = _Get_final_iterator_unwrapped(_Range); + auto _UResult = + _Stable_partition_common(_Ubegin(_Range), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + + private: + template + _NODISCARD static subrange<_It> _Stable_partition_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + // partition preserving order of equivalents + for (;;) { // skip in-place elements at front + if (_First == _Last) { // the input range is true (already partitioned) + return {_STD move(_First), _STD move(_Last)}; + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + break; + } + ++_First; + } + + auto _Saved_last = _Last; + do { // skip in-place elements at end + --_Last; + if (_First == _Last) { + return {_STD move(_First), _STD move(_Saved_last)}; + } + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last))); + + const iter_difference_t<_It> _Temp_count = _RANGES distance(_First, _Last); + _Optimistic_temporary_buffer> _Temp_buf{_Temp_count}; + + // _Temp_count + 1 since we work on closed ranges + const auto _Total_count = static_cast>(_Temp_count + 1); + auto _Result = _Stable_partition_common_buffered( + _STD move(_First), _STD move(_Last), _Pred, _Proj, _Total_count, _Temp_buf._Data, _Temp_buf._Capacity); + return {_STD move(_Result.first), _STD move(_Saved_last)}; + } + + template + _NODISCARD static pair<_It, iter_difference_t<_It>> _Stable_partition_common_buffered(_It _First, _It _Last, + _Pr _Pred, _Pj _Proj, const iter_difference_t<_It> _Count, iter_value_t<_It>* const _Temp_ptr, + const ptrdiff_t _Capacity) { + // implement stable_partition of [_First, _Last] (note: closed range) + // note: _Count >= 2 and _First != _Last + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_CHECK(!_STD invoke(_Pred, _STD invoke(_Proj, *_First))); + _STL_INTERNAL_CHECK(_STD invoke(_Pred, _STD invoke(_Proj, *_Last))); + _STL_INTERNAL_CHECK(_Count == _RANGES distance(_First, _Last) + 1); + + using _Diff = iter_difference_t<_It>; + if (_Count - 1 <= _Capacity) { // - 1 since we never need to store *_Last + _Uninitialized_backout*> _Backout{_Temp_ptr}; + _It _Next = _First; + _Backout._Emplace_back(_RANGES iter_move(_First)); + while (++_First != _Last) { + // test each element, moving into the temporary buffer if it's in the false range, or + // assigning backwards if it's in the true range + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + *_Next = _RANGES iter_move(_First); + ++_Next; + } else { + _Backout._Emplace_back(_RANGES iter_move(_First)); + } + } + + // move the last true element, *_Last, to the end of the true range + *_Next = _RANGES iter_move(_Last); + ++_Next; + // copy back the false range + _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _Next); + const auto _True_distance = static_cast<_Diff>(_Count - (_Backout._Last - _Backout._First)); + return {_STD move(_Next), _True_distance}; + } + + const _Diff _Mid_offset = _Count >> 1; // _Mid_offset >= 1 because _Count >= 2 + const _It _Mid = _RANGES next(_First, _Mid_offset); + // form [_First, _Left) true range, [_Left, _Mid) false range + _It _Left = _Mid; + _Diff _Left_true_count = _Mid_offset; + for (;;) { // skip over the trailing false range before _Mid + --_Left; + --_Left_true_count; + if (_First == _Left) { // the entire left range is false + break; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Left))) { + // excluded the false range before _Mid, invariants reestablished, recurse + ++_Left_true_count; // to include *_First + const auto _Low = _Stable_partition_common_buffered( + _First, _STD move(_Left), _Pred, _Proj, _Left_true_count, _Temp_ptr, _Capacity); + _Left = _STD move(_Low.first); + _Left_true_count = _Low.second; + break; + } + } + + // form [_Mid, _Right) true range, [_Right, next(_Last)) false range + _It _Right = _Mid; + _Diff _Right_true_count = 0; + for (;;) { // skip over the leading true range after and including _Mid + if (_Right == _Last) { // the entire right range is true + ++_Right; // to include _Last + ++_Right_true_count; + break; + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Right))) { + // excluded the true range after and including _Mid, invariants reestablished, recurse + const auto _Right_count = static_cast<_Diff>(_Count - _Mid_offset); + const auto _Remaining = static_cast<_Diff>(_Right_count - _Right_true_count); + const auto _High = _Stable_partition_common_buffered( + _STD move(_Right), _Last, _Pred, _Proj, _Remaining, _Temp_ptr, _Capacity); + _Right = _STD move(_High.first); + _Right_true_count += _High.second; + break; + } + + ++_Right; + ++_Right_true_count; + } + + // swap the [_Left, _Mid) false range with the [_Mid, _Right) true range + auto _Partition_point = + _RANGES _Buffered_rotate_common(_STD move(_Left), _STD move(_Mid), _STD move(_Right), + static_cast<_Diff>(_Mid_offset - _Left_true_count), _Right_true_count, _Temp_ptr, _Capacity); + return {_STD move(_Partition_point), static_cast<_Diff>(_Left_true_count + _Right_true_count)}; + } + }; + + inline constexpr _Stable_partition_fn stable_partition{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE push_heap template _CONSTEXPR20 void _Push_heap_by_index( @@ -6950,49 +7153,391 @@ void inplace_merge(_ExPo&&, _BidIt _First, _BidIt _Mid, _BidIt _Last) noexcept / } #endif // _HAS_CXX17 -// FUNCTION TEMPLATE sort -template -_CONSTEXPR20 _BidIt _Insertion_sort_unchecked(const _BidIt _First, const _BidIt _Last, _Pr _Pred) { - // insertion sort [_First, _Last) - if (_First != _Last) { - for (_BidIt _Mid = _First; ++_Mid != _Last;) { // order next element - _BidIt _Hole = _Mid; - _Iter_value_t<_BidIt> _Val = _STD move(*_Mid); - - if (_DEBUG_LT_PRED(_Pred, _Val, *_First)) { // found new earliest element, move to front - _Move_backward_unchecked(_First, _Mid, ++_Hole); - *_First = _STD move(_Val); - } else { // look for insertion point after first - for (_BidIt _Prev = _Hole; _DEBUG_LT_PRED(_Pred, _Val, *--_Prev); _Hole = _Prev) { - *_Hole = _STD move(*_Prev); // move hole down - } +#ifdef __cpp_lib_concepts +namespace ranges { + // FUNCTION TEMPLATE _Is_sorted_until_unchecked + // clang-format off + template _Se, class _Pr, class _Pj> + requires indirect_strict_weak_order<_Pr, projected<_It, _Pj>> + _NODISCARD constexpr _It _Is_sorted_until_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on + if (_First == _Last) { + return _First; + } - *_Hole = _STD move(_Val); // insert element in hole + for (auto _Prev = _First; ++_First != _Last; ++_Prev) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) { + break; } } + + return _First; } - return _Last; -} + // VARIABLE ranges::inplace_merge + template + void _Rotate_one_right(_It _First, _It _Mid, _It _Last) { + // exchanges the range [_First, _Mid) with [_Mid, _Last) + _STL_INTERNAL_CHECK(_RANGES next(_Mid) == _Last); + auto _Temp = _RANGES iter_move(_Mid); + _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); + *_First = _STD move(_Temp); + } -template -_CONSTEXPR20 void _Med3_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { - // sort median of three elements to middle - if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { - _STD iter_swap(_Mid, _First); + template + void _Rotate_one_left(_It _First, _It _Mid, _It _Last) { + // exchanges the range [_First, _Mid) with [_Mid, _Last) + _STL_INTERNAL_CHECK(_RANGES next(_First) == _Mid); + auto _Temp = _RANGES iter_move(_Mid); + auto _Result = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)); + *_Result.out = _STD move(_Temp); } - if (_DEBUG_LT_PRED(_Pred, *_Last, *_Mid)) { // swap middle and last, then test first again - _STD iter_swap(_Last, _Mid); + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Inplace_merge_buffer_left(_It _First, _It _Mid, _It _Last, iter_value_t<_It>* _Left_first, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj) { + // clang-format on + // move the range [_First, _Mid) to _Left_first, and merge it with [_Mid, _Last) to _First + // usual invariants apply + using _Ty = iter_value_t<_It>; - if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { - _STD iter_swap(_Mid, _First); - } - } -} + _Ty* _Left_last = _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Left_first, _Left_first + _Capacity).out; + _Uninitialized_backout<_Ty*> _Backout{_Left_first, _Left_last}; -template -_CONSTEXPR20 void _Guess_median_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { + // We already know that _Backout._Last - 1 is the highest element, so do not compare against it again. + --_Left_last; + + // We already know that _Mid points to the lowest element and that there is more than 1 element left. + *_First = _RANGES iter_move(_Mid); + ++_First; + ++_Mid; + + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_Left_first))) { + *_First = _RANGES iter_move(_Mid); // the lowest element is now in position + ++_First; + ++_Mid; + if (_Mid == _Last) { + // move the remaining left partition + _RANGES _Move_unchecked(_Left_first, _Backout._Last, _First); + return; + } + } else { + *_First = _RANGES iter_move(_Left_first); + ++_First; + ++_Left_first; + if (_Left_first == _Left_last) { + // move the remaining right partition and highest element, since *_Left_first is highest + const auto _Final = _RANGES _Move_unchecked(_Mid, _Last, _First); + *_Final.out = _RANGES iter_move(_Left_first); + return; + } + } + } + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Inplace_merge_buffer_right(_It _First, _It _Mid, _It _Last, iter_value_t<_It>* _Right_first, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj) { + // clang-format on + // move the range [_Mid, _Last) to _Right_first, and merge it with [_First, _Mid) to _Last + // usual invariants apply + using _Ty = iter_value_t<_It>; + + _Ty* _Right_last = + _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Right_first, _Right_first + _Capacity).out; + _Uninitialized_backout<_Ty*> _Backout{_Right_first, _Right_last}; + + // We already know that _Mid points to the next highest element and that there is more than 1 element left. + *--_Last = _RANGES iter_move(--_Mid); + + // We already know that _Backout._Last - 1 is the highest element, so do not compare against it again. + --_Mid; + --_Right_last; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Right_last), _STD invoke(_Proj, *_Mid))) { + *--_Last = _RANGES iter_move(_Mid); // the lowest element is now in position + if (_First == _Mid) { + ++_Right_last; // to make [_Right_first, _Right_last) a half-open range + _RANGES _Move_backward_common(_Right_first, _Right_last, _STD move(_Last)); + return; + } + --_Mid; + } else { + *--_Last = _RANGES iter_move(_Right_last); + --_Right_last; + if (_Right_first == _Right_last) { // we can't compare with *_Right_first, but we know it is lowest + ++_Mid; // restore half-open range [_First, _Mid) + _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); + *_First = _RANGES iter_move(_Right_first); + return; + } + } + } + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_common(_It _First, _It _Mid, _It _Last, iter_difference_t<_It> _Count1, + iter_difference_t<_It> _Count2, iter_value_t<_It>* _Temp_ptr, ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj); + // clang-format on + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_divide_and_conquer2(_It _First, _It _Mid, _It _Last, + const iter_difference_t<_It> _Count1, const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj, _It _Firstn, _It _Lastn, const iter_difference_t<_It> _Count1n, + const iter_difference_t<_It> _Count2n) { + // clang-format on + // common block of _Buffered_inplace_merge_divide_and_conquer, below + _It _Midn = _RANGES _Buffered_rotate_common(_Firstn, _Mid, _Lastn, + static_cast>(_Count1 - _Count1n), _Count2n, _Temp_ptr, + _Capacity); // rearrange middle + _RANGES _Buffered_inplace_merge_common( + _First, _Firstn, _Midn, _Count1n, _Count2n, _Temp_ptr, _Capacity, _Pred, _Proj); // merge each new part + _RANGES _Buffered_inplace_merge_common(_Midn, _Lastn, _Last, + static_cast>(_Count1 - _Count1n), + static_cast>(_Count2 - _Count2n), _Temp_ptr, _Capacity, _Pred, _Proj); + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_divide_and_conquer(_It _First, _It _Mid, _It _Last, + const iter_difference_t<_It> _Count1, const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj) { + // clang-format on + // merge sorted [_First, _Mid) with sorted [_Mid, _Last) + // usual invariants apply + if (_Count1 <= _Count2) { + const iter_difference_t<_It> _Count1n = _Count1 >> 1; // shift for codegen + _It _Firstn = _RANGES next(_First, _Count1n); + _It _Lastn = _RANGES _Lower_bound_unchecked(_Mid, _Count1, _STD invoke(_Proj, *_Firstn), _Pred, _Proj); + const auto _Count2n = _RANGES distance(_Mid, _Lastn); + _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj, _STD move(_Firstn), _STD move(_Lastn), _Count1n, + _Count2n); + } else { + const iter_difference_t<_It> _Count2n = _Count2 >> 1; // shift for codegen + _It _Lastn = _RANGES next(_Mid, _Count2n); + _It _Firstn = _RANGES _Upper_bound_unchecked(_First, _Count2, _STD invoke(_Proj, *_Lastn), _Pred, _Proj); + const auto _Count1n = _RANGES distance(_First, _Firstn); + _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj, _STD move(_Firstn), _STD move(_Lastn), _Count1n, + _Count2n); + } + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_common(_It _First, _It _Mid, _It _Last, iter_difference_t<_It> _Count1, + iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity, _Pr _Pred, + _Pj _Proj) { + // clang-format on + // merge sorted [_First, _Mid) with sorted [_Mid, _Last) + // usual invariants *do not* apply; only sortedness applies + // establish the usual invariants + if (_First == _Mid || _Mid == _Last) { + return; + } + + // Find first element in [_First, _Mid) that is greater than *_Mid + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { + --_Count1; + if (++_First == _Mid) { + return; + } + } + + // Find last element in [_Mid, _Last) that is less than *--_Mid + const auto _Highest = _RANGES prev(_Mid); + do { + // Fast early return if there is only one element to be moved + if (_Mid == --_Last) { + // rotate only element remaining in right partition to the beginning, without allocating + _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); + return; + } + --_Count2; + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))); + ++_Last; + ++_Count2; + + if (_Count1 == 1) { + _RANGES _Rotate_one_left(_STD move(_First), _STD move(_Mid), _STD move(_Last)); + return; + } + + if (_Count1 <= _Count2 && _Count1 <= _Capacity) { + _RANGES _Inplace_merge_buffer_left( + _STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, _Capacity, _Pred, _Proj); + } else if (_Count2 <= _Capacity) { + _RANGES _Inplace_merge_buffer_right( + _STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, _Capacity, _Pred, _Proj); + } else { + _RANGES _Buffered_inplace_merge_divide_and_conquer(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj); + } + } + + class _Inplace_merge_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> + _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); + + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + + _Inplace_merge_common(_STD move(_UFirst), _Get_unwrapped(_STD move(_Mid)), _STD move(_ULast), + _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + borrowed_iterator_t<_Rng> operator()( + _Rng&& _Range, iterator_t<_Rng> _Mid, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _First = _RANGES begin(_Range); + auto _Last = _RANGES end(_Range); + + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + + _Inplace_merge_common(_STD move(_UFirst), _Get_unwrapped(_STD move(_Mid)), _STD move(_ULast), + _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + private: + template + static void _Inplace_merge_common(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + if (_First == _Mid || _Mid == _Last) { + return; + } +#if _ITERATOR_DEBUG_LEVEL == 2 + _STL_VERIFY(_RANGES _Is_sorted_until_unchecked(_First, _Mid, _Pred, _Proj) == _Mid, + "ranges::inplace_merge requires the range [first, middle) to be sorted"); + _STL_VERIFY(_RANGES _Is_sorted_until_unchecked(_Mid, _Last, _Pred, _Proj) == _Last, + "ranges::inplace_merge requires the range [middle, last) to be sorted"); +#endif //_ITERATOR_DEBUG_LEVEL == 2 + + // Find first element in [_First, _Mid) that is greater than *_Mid + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { + if (++_First == _Mid) { + return; + } + } + + // Fast early return if there is only one element to be moved + if (_Mid == --_Last) { + // rotate only element remaining in right partition to the beginning, without allocating + _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); + return; + } + + // Find last element in [_Mid, _Last) that is less than *--_Mid + const auto _Highest = _RANGES prev(_Mid); + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))) { + if (_Mid == --_Last) { + // rotate only element remaining in right partition to the beginning, without allocating + _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); + return; + } + } + ++_Last; + + const iter_difference_t<_It> _Count1 = _RANGES distance(_First, _Mid); + if (_Count1 == 1) { // rotate only element remaining in left partition to the end, without allocating + _RANGES _Rotate_one_left(_STD move(_First), _STD move(_Mid), _STD move(_Last)); + return; + } + + const iter_difference_t<_It> _Count2 = _RANGES distance(_Mid, _Last); + _Optimistic_temporary_buffer> _Temp_buf{(_STD min)(_Count1, _Count2)}; + if (_Count1 <= _Count2 && _Count1 <= _Temp_buf._Capacity) { + _RANGES _Inplace_merge_buffer_left(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); + } else if (_Count2 <= _Temp_buf._Capacity) { + _RANGES _Inplace_merge_buffer_right(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); + } else { + _RANGES _Buffered_inplace_merge_divide_and_conquer(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); + } + } + }; + + inline constexpr _Inplace_merge_fn inplace_merge{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + +// FUNCTION TEMPLATE sort +template +_CONSTEXPR20 _BidIt _Insertion_sort_unchecked(const _BidIt _First, const _BidIt _Last, _Pr _Pred) { + // insertion sort [_First, _Last) + if (_First != _Last) { + for (_BidIt _Mid = _First; ++_Mid != _Last;) { // order next element + _BidIt _Hole = _Mid; + _Iter_value_t<_BidIt> _Val = _STD move(*_Mid); + + if (_DEBUG_LT_PRED(_Pred, _Val, *_First)) { // found new earliest element, move to front + _Move_backward_unchecked(_First, _Mid, ++_Hole); + *_First = _STD move(_Val); + } else { // look for insertion point after first + for (_BidIt _Prev = _Hole; _DEBUG_LT_PRED(_Pred, _Val, *--_Prev); _Hole = _Prev) { + *_Hole = _STD move(*_Prev); // move hole down + } + + *_Hole = _STD move(_Val); // insert element in hole + } + } + } + + return _Last; +} + +template +_CONSTEXPR20 void _Med3_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { + // sort median of three elements to middle + if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { + _STD iter_swap(_Mid, _First); + } + + if (_DEBUG_LT_PRED(_Pred, *_Last, *_Mid)) { // swap middle and last, then test first again + _STD iter_swap(_Last, _Mid); + + if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { + _STD iter_swap(_Mid, _First); + } + } +} + +template +_CONSTEXPR20 void _Guess_median_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { // sort median element to middle using _Diff = _Iter_diff_t<_RanIt>; const _Diff _Count = _Last - _First; @@ -7137,12 +7682,12 @@ namespace ranges { // clang-format off template requires sortable<_It, _Pr, _Pj> - constexpr void _Insertion_sort_common(const _It _First, const _It _Last, _Pr _Pred, _Pj _Proj) { + constexpr _It _Insertion_sort_common(const _It _First, const _It _Last, _Pr _Pred, _Pj _Proj) { // clang-format on // insertion sort [_First, _Last) if (_First == _Last) { // empty range is sorted - return; + return _Last; } for (auto _Mid = _First; ++_Mid != _Last;) { // order next element @@ -7162,6 +7707,7 @@ namespace ranges { *_Hole = _STD move(_Val); // insert element in hole } + return _Last; } // clang-format off @@ -7410,16 +7956,16 @@ _OutIt _Merge_move(_InIt _First, const _InIt _Mid, const _InIt _Last, _OutIt _De } template -void _Uninitialized_chunked_merge_unchecked(_BidIt _First, const _BidIt _Last, _Ty* _Dest, - const _Iter_diff_t<_BidIt> _Chunk, _Iter_diff_t<_BidIt> _Count, _Pr _Pred) { - // move to uninitialized merging adjacent chunks of distance _Chunk +void _Uninitialized_chunked_merge_unchecked2( + _BidIt _First, const _BidIt _Last, _Ty* _Dest, _Iter_diff_t<_BidIt> _Count, _Pr _Pred) { + // move to uninitialized merging adjacent chunks of distance _Isort_max<_BidIt> // pre: _Count == distance(_First, _Last) // pre: _Chunk > 0 _Uninitialized_backout<_Ty*> _Backout{_Dest}; - while (_Chunk < _Count) { - _Count -= _Chunk; - const _BidIt _Mid1 = _STD next(_First, _Chunk); - const auto _Chunk2 = (_STD min)(_Chunk, _Count); + while (_Count > _Isort_max<_BidIt>) { + _Count -= _Isort_max<_BidIt>; + const _BidIt _Mid1 = _STD next(_First, _Isort_max<_BidIt>); + const auto _Chunk2 = (_STD min)(_Isort_max<_BidIt>, _Count); _Count -= _Chunk2; const _BidIt _Mid2 = _STD next(_Mid1, _Chunk2); _Backout._Last = _Uninitialized_merge_move(_First, _Mid1, _Mid2, _Backout._Last, _Pred); @@ -7451,10 +7997,10 @@ void _Chunked_merge_unchecked(_BidIt _First, const _BidIt _Last, _OutIt _Dest, c template void _Insertion_sort_isort_max_chunks(_BidIt _First, const _BidIt _Last, _Iter_diff_t<_BidIt> _Count, _Pr _Pred) { - // insertion sort every chunk of distance _ISORT_MAX in [_First, _Last) + // insertion sort every chunk of distance _Isort_max<_BidIt> in [_First, _Last) // pre: _Count == distance(_First, _Last) - for (; _ISORT_MAX < _Count; _Count -= _ISORT_MAX) { // sort chunks - _First = _Insertion_sort_unchecked(_First, _STD next(_First, _ISORT_MAX), _Pred); + for (; _Isort_max<_BidIt> < _Count; _Count -= _Isort_max<_BidIt>) { // sort chunks + _First = _Insertion_sort_unchecked(_First, _STD next(_First, _Isort_max<_BidIt>), _Pred); } _Insertion_sort_unchecked(_First, _Last, _Pred); // sort partial last chunk @@ -7468,14 +8014,14 @@ void _Buffered_merge_sort_unchecked(const _BidIt _First, const _BidIt _Last, con // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t _Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred); // merge adjacent pairs of chunks to and from temp buffer - auto _Chunk = static_cast<_Iter_diff_t<_BidIt>>(_ISORT_MAX); - if (_Count <= _Chunk) { + if (_Count <= _Isort_max<_BidIt>) { return; } // do the first merge, constructing elements in the temporary buffer - _Uninitialized_chunked_merge_unchecked(_First, _Last, _Temp_ptr, _Chunk, _Count, _Pred); + _Uninitialized_chunked_merge_unchecked2(_First, _Last, _Temp_ptr, _Count, _Pred); _Uninitialized_backout<_Iter_value_t<_BidIt>*> _Backout{_Temp_ptr, _Temp_ptr + _Count}; + auto _Chunk = _Isort_max<_BidIt>; for (;;) { // unconditionally merge elements back into the source buffer _Chunk <<= 1; @@ -7499,7 +8045,7 @@ void _Stable_sort_unchecked(const _BidIt _First, const _BidIt _Last, const _Iter if (_Count <= _ISORT_MAX) { _Insertion_sort_unchecked(_First, _Last, _Pred); // small } else { // sort halves and merge - const auto _Half_count = static_cast<_Diff>(_Count / 2); + const auto _Half_count = static_cast<_Diff>(_Count >> 1); // shift for codegen const auto _Half_count_ceil = static_cast<_Diff>(_Count - _Half_count); const _BidIt _Mid = _STD next(_First, _Half_count_ceil); if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer @@ -7549,6 +8095,265 @@ void stable_sort(_ExPo&& _Exec, _BidIt _First, _BidIt _Last) noexcept /* termina } #endif // _HAS_CXX17 + +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::stable_sort + class _Stable_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> + _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + + const auto _Count = _ULast - _UFirst; + _Stable_sort_common(_STD move(_UFirst), _STD move(_ULast), _Count, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _UFirst = _Ubegin(_Range); + auto _ULast = _Get_final_iterator_unwrapped(_Range); + + const auto _Count = _ULast - _UFirst; + _Stable_sort_common(_STD move(_UFirst), _ULast, _Count, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_ULast)); + } + + private: + template + static void _Stable_sort_common( + _It _First, _It _Last, const iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + // sort [_First, _Last) with respect to _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + + if (_Count <= _Isort_max<_It>) { + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + return; + } + + _Optimistic_temporary_buffer<_Iter_value_t<_It>> _Temp_buf{_Count - _Count / 2}; + _Stable_sort_common_buffered( + _STD move(_First), _STD move(_Last), _Count, _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); + } + + template + static void _Stable_sort_common_buffered(_It _First, _It _Last, const iter_difference_t<_It> _Count, + iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj) { + // sort [_First, _Last) with respect to _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + // Pre: _Temp_ptr points to empty storage for _Capacity objects + + if (_Count <= _Isort_max<_It>) { + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + } else { // sort halves and merge + const iter_difference_t<_It> _Half_count = _Count >> 1; // shift for codegen + const iter_difference_t<_It> _Half_count_ceil = _Count - _Half_count; + const _It _Mid = _First + _Half_count_ceil; + if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer + _Buffered_merge_sort_common(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Pred, _Proj); + _Buffered_merge_sort_common(_Mid, _Last, _Half_count, _Temp_ptr, _Pred, _Proj); + } else { // temp buffer not big enough, divide and conquer + _Stable_sort_common_buffered(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Capacity, _Pred, _Proj); + _Stable_sort_common_buffered(_Mid, _Last, _Half_count, _Temp_ptr, _Capacity, _Pred, _Proj); + } + // merge halves + _RANGES _Buffered_inplace_merge_common(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Half_count_ceil, _Half_count, _Temp_ptr, _Capacity, _Pred, _Proj); + } + } + + template + static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const iter_difference_t<_It> _Count, + iter_value_t<_It>* const _Temp_ptr, _Pr _Pred, _Pj _Proj) { + // sort using temp buffer for merges + // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_Last - _First == _Count); + + _Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred, _Proj); + // merge adjacent pairs of chunks to and from temp buffer + if (_Count <= _Isort_max<_It>) { + return; + } + + // do the first merge, constructing elements in the temporary buffer + _Uninitialized_chunked_merge_common(_First, _Last, _Temp_ptr, _Count, _Pred, _Proj); + _Uninitialized_backout*> _Backout{_Temp_ptr, _Temp_ptr + _Count}; + iter_difference_t<_It> _Chunk_size = _Isort_max<_It>; + for (;;) { + // unconditionally merge elements back into the source buffer + _Chunk_size <<= 1; + _Chunked_merge_common(_Temp_ptr, _Temp_ptr + _Count, _First, _Chunk_size, _Count, _Pred, _Proj); + _Chunk_size <<= 1; + if (_Count <= _Chunk_size) { // if the input would be a single chunk, it's already sorted and we're done + return; + } + + // more merges necessary; merge to temporary buffer + _Chunked_merge_common(_First, _Last, _Temp_ptr, _Chunk_size, _Count, _Pred, _Proj); + } + } + + template + static void _Insertion_sort_isort_max_chunks( + _It _First, _It _Last, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + // insertion sort every chunk of distance _Isort_max<_It> in [_First, _Last) + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + + for (; _Isort_max<_It> < _Count; _Count -= _Isort_max<_It>) { // sort chunks + _First = _RANGES _Insertion_sort_common(_First, _First + _Isort_max<_It>, _Pred, _Proj); + } + + // sort partial last chunk + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + } + + template + static void _Uninitialized_chunked_merge_common(_It _First, const _It _Last, iter_value_t<_It>* const _Dest, + iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + // move to uninitialized merging adjacent chunks of distance _Isort_max<_It> + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + + _Uninitialized_backout*> _Backout{_Dest}; + const auto _Backout_end = _Dest + _Count; + while (_Isort_max<_It> < _Count) { + _Count -= _Isort_max<_It>; + const auto _Chunk2 = (_STD min)(_Isort_max<_It>, _Count); + _Count -= _Chunk2; + + auto _Mid1 = _First + _Isort_max<_It>; + auto _Last1 = _Mid1 + _Chunk2; + auto _Last2 = _Backout._Last + _Isort_max<_It> + _Chunk2; + _Backout._Last = _Uninitialized_merge_move( + _STD move(_First), _STD move(_Mid1), _Last1, _Backout._Last, _Last2, _Pred, _Proj); + _First = _STD move(_Last1); + } + + // move partial last chunk + _RANGES _Uninitialized_move_unchecked(_STD move(_First), _STD move(_Last), _Backout._Last, _Backout_end); + _Backout._Release(); + } + + template + _NODISCARD static iter_value_t<_It>* _Uninitialized_merge_move(_It _First, _It _Mid, _It _Last, + iter_value_t<_It>* const _Dest, iter_value_t<_It>* const _Dest_last, _Pr _Pred, _Pj _Proj) { + // move merging ranges to uninitialized storage + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) <= _RANGES distance(_Dest, _Dest_last)); + + _Uninitialized_backout*> _Backout{_Dest}; + _It _Next = _Mid; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { + _Backout._Emplace_back(_RANGES iter_move(_Next)); + ++_Next; + + if (_Next == _Last) { + _Backout._Last = _RANGES _Uninitialized_move_unchecked( + _STD move(_First), _STD move(_Mid), _Backout._Last, _Dest_last) + .out; + return _Backout._Release(); + } + } else { + _Backout._Emplace_back(_RANGES iter_move(_First)); + ++_First; + + if (_First == _Mid) { + _Backout._Last = _RANGES _Uninitialized_move_unchecked( + _STD move(_Next), _STD move(_Last), _Backout._Last, _Dest_last) + .out; + return _Backout._Release(); + } + } + } + } + + template + _NODISCARD static _OutIt _Merge_move_common( + _InIt _First, _InIt _Mid, _InIt _Last, _OutIt _Dest, _Pr _Pred, _Pj _Proj) { + // move merging adjacent ranges [_First, _Mid) and [_Mid, _Last) to _Dest + _STL_INTERNAL_STATIC_ASSERT(sortable<_InIt, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_movable<_InIt, _OutIt>); + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); + + _InIt _Next = _Mid; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { + *_Dest = _RANGES iter_move(_Next); + ++_Dest; + ++_Next; + + if (_Next == _Last) { + return _RANGES _Move_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_Dest)).out; + } + } else { + *_Dest = _RANGES iter_move(_First); + ++_Dest; + ++_First; + + if (_First == _Mid) { + return _RANGES _Move_unchecked(_STD move(_Next), _STD move(_Last), _STD move(_Dest)).out; + } + } + } + } + + template + static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest, + const iter_difference_t<_It1> _Chunk_size, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj _Proj) { + // move merging adjacent chunks of distance _Chunk_size + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It1, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_movable<_It1, _It2>); + _STL_INTERNAL_CHECK(_Last - _First == _Count); + _STL_INTERNAL_CHECK(_Chunk_size > 0); + + while (_Chunk_size < _Count) { + _Count -= _Chunk_size; + const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); + _Count -= _Right_chunk_size; + + auto _Mid1 = _First + _Chunk_size; + auto _Last1 = _Mid1 + _Right_chunk_size; + _Dest = _Merge_move_common(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); + _First = _STD move(_Last1); + } + + // copy partial last chunk + _RANGES _Move_unchecked(_STD move(_First), _STD move(_Last), _STD move(_Dest)); + } + }; + + inline constexpr _Stable_sort_fn stable_sort{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE partial_sort template _CONSTEXPR20 void partial_sort(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { @@ -7946,7 +8751,7 @@ namespace ranges { } // sort any remainder - _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); } }; @@ -9562,26 +10367,6 @@ _NODISCARD bool is_sorted(_ExPo&& _Exec, _FwdIt _First, _FwdIt _Last) noexcept / #ifdef __cpp_lib_concepts namespace ranges { - // FUNCTION TEMPLATE _Is_sorted_until_unchecked - template - _NODISCARD constexpr _It _Is_sorted_until_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { - _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It, _Pj>>); - - if (_First == _Last) { - return _First; - } - - for (auto _Prev = _First; ++_First != _Last; ++_Prev) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) { - break; - } - } - - return _First; - } - // VARIABLE ranges::is_sorted class _Is_sorted_fn : private _Not_quite_object { public: diff --git a/stl/inc/array b/stl/inc/array index d51bc8f54b9..6f8e8b722cb 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -111,6 +111,11 @@ public: return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + return _Ptr <=> _Right._Ptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -130,6 +135,7 @@ public: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 using _Prevent_inheriting_unwrap = _Array_const_iterator; @@ -235,6 +241,12 @@ private: return _Idx == _Right._Idx; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Idx <=> _Right._Idx; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -255,6 +267,7 @@ private: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR17 void _Compat(const _Array_const_iterator& _Right) const noexcept { // test for compatible iterator pair _STL_VERIFY(_Ptr == _Right._Ptr, "array iterators incompatible"); @@ -775,17 +788,41 @@ _CONSTEXPR20 void swap(array<_Ty, _Size>& _Left, array<_Ty, _Size>& _Right) noex template _NODISCARD _CONSTEXPR20 bool operator==(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD equal(_Left.begin(), _Left.end(), _Right.begin()); +#else // ^^^ workaround / no workaround vvv + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); +#endif // ^^^ no workaround ^^^ } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20 bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +_NODISCARD bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr _Synth_three_way_result<_Ty> operator<=>( + const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 + return _STD lexicographical_compare_three_way( + _Left.begin(), _Left.end(), _Right.begin(), _Right.end(), _Synth_three_way{}); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +#endif // ^^^ no workaround ^^^ +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD _CONSTEXPR20 bool operator<(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); +#endif // ^^^ no workaround ^^^ } template @@ -802,6 +839,7 @@ template _NODISCARD _CONSTEXPR20 bool operator>=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 // FUNCTION TEMPLATE to_array diff --git a/stl/inc/atomic b/stl/inc/atomic index 5e229aefe28..3300bf34674 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -75,6 +75,84 @@ extern "C" _NODISCARD char __stdcall __std_atomic_has_cmpxchg16b() noexcept; #define _ATOMIC_HAS_DCAS 0 #endif // _STD_ATOMIC_ALWAYS_USE_CMPXCHG16B == 1 || !defined(_M_X64) || defined(_M_ARM64EC) +#if defined(_M_ARM64) && defined(__clang__) && __clang_major__ == 11 // TRANSITION, LLVM 12 +inline unsigned char _InterlockedCompareExchange128( + __int64 volatile* _Destination, __int64 _Val_high, __int64 _Val_low, __int64* _Comparand) { + auto _Dest = reinterpret_cast<__int128 volatile*>(_Destination); + auto _Cmp = reinterpret_cast<__int128*>(_Comparand); + __int128 _Val; + reinterpret_cast<__int64*>(&_Val)[0] = _Val_low; + reinterpret_cast<__int64*>(&_Val)[1] = _Val_high; + + __int128 _Stored; + do { + if (_Stored = __builtin_arm_ldaex(_Dest); _Stored != *_Cmp) { + *_Cmp = _Stored; + return 0; + } + } while (__builtin_arm_stlex(_Val, _Dest)); + + return 1; +} + +inline unsigned char _InterlockedCompareExchange128_acq( + __int64 volatile* _Destination, __int64 _Val_high, __int64 _Val_low, __int64* _Comparand) { + auto _Dest = reinterpret_cast<__int128 volatile*>(_Destination); + auto _Cmp = reinterpret_cast<__int128*>(_Comparand); + __int128 _Val; + reinterpret_cast<__int64*>(&_Val)[0] = _Val_low; + reinterpret_cast<__int64*>(&_Val)[1] = _Val_high; + + __int128 _Stored; + do { + if (_Stored = __builtin_arm_ldaex(_Dest); _Stored != *_Cmp) { + *_Cmp = _Stored; + return 0; + } + } while (__builtin_arm_strex(_Val, _Dest)); + + return 1; +} + +inline unsigned char _InterlockedCompareExchange128_nf( + __int64 volatile* _Destination, __int64 _Val_high, __int64 _Val_low, __int64* _Comparand) { + auto _Dest = reinterpret_cast<__int128 volatile*>(_Destination); + auto _Cmp = reinterpret_cast<__int128*>(_Comparand); + __int128 _Val; + reinterpret_cast<__int64*>(&_Val)[0] = _Val_low; + reinterpret_cast<__int64*>(&_Val)[1] = _Val_high; + + __int128 _Stored; + do { + if (_Stored = __builtin_arm_ldrex(_Dest); _Stored != *_Cmp) { + *_Cmp = _Stored; + return 0; + } + } while (__builtin_arm_strex(_Val, _Dest)); + + return 1; +} + +inline unsigned char _InterlockedCompareExchange128_rel( + __int64 volatile* _Destination, __int64 _Val_high, __int64 _Val_low, __int64* _Comparand) { + auto _Dest = reinterpret_cast<__int128 volatile*>(_Destination); + auto _Cmp = reinterpret_cast<__int128*>(_Comparand); + __int128 _Val; + reinterpret_cast<__int64*>(&_Val)[0] = _Val_low; + reinterpret_cast<__int64*>(&_Val)[1] = _Val_high; + + __int128 _Stored; + do { + if (_Stored = __builtin_arm_ldaex(_Dest); _Stored != *_Cmp) { + *_Cmp = _Stored; + return 0; + } + } while (__builtin_arm_stlex(_Val, _Dest)); + + return 1; +} +#endif // ^^^ ARM64 && LLVM 11 workaround ^^^ + // MACRO _ATOMIC_CHOOSE_INTRINSIC #if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) #define _ATOMIC_CHOOSE_INTRINSIC(_Order, _Result, _Intrinsic, ...) \ @@ -414,24 +492,45 @@ void _Atomic_wait_direct( } #endif // _HAS_CXX20 -#if 1 // TRANSITION, ABI +#if 1 // TRANSITION, ABI, GH-1151 inline void _Atomic_lock_acquire(long& _Spinlock) noexcept { - while (_InterlockedExchange(&_Spinlock, 1)) { - _YIELD_PROCESSOR(); +#if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) + // Algorithm from Intel(R) 64 and IA-32 Architectures Optimization Reference Manual, May 2020 + // Example 2-4. Contended Locks with Increasing Back-off Example - Improved Version, page 2-22 + // The code in mentioned manual is covered by the 0BSD license. + int _Current_backoff = 1; + const int _Max_backoff = 64; + while (_InterlockedExchange(&_Spinlock, 1) != 0) { + while (__iso_volatile_load32(&reinterpret_cast(_Spinlock)) != 0) { + for (int _Count_down = _Current_backoff; _Count_down != 0; --_Count_down) { + _mm_pause(); + } + _Current_backoff = _Current_backoff < _Max_backoff ? _Current_backoff << 1 : _Max_backoff; + } } +#elif defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) + while (_InterlockedExchange(&_Spinlock, 1) != 0) { // TRANSITION, GH-1133: _InterlockedExchange_acq + while (__iso_volatile_load32(&reinterpret_cast(_Spinlock)) != 0) { + __yield(); + } + } +#else // ^^^ defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) ^^^ +#error Unsupported hardware +#endif } inline void _Atomic_lock_release(long& _Spinlock) noexcept { -#if defined(_M_ARM) || defined(_M_ARM64) +#if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) + _InterlockedExchange(&_Spinlock, 0); // TRANSITION, GH-1133: same as ARM +#elif defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) _Memory_barrier(); __iso_volatile_store32(reinterpret_cast(&_Spinlock), 0); - _Memory_barrier(); -#else // ^^^ ARM32/ARM64 hardware / x86/x64 hardware vvv - _InterlockedExchange(&_Spinlock, 0); -#endif // hardware + _Memory_barrier(); // TRANSITION, GH-1133: remove +#else // ^^^ defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) ^^^ +#error Unsupported hardware +#endif } - inline void _Atomic_lock_acquire(_Smtx_t* _Spinlock) noexcept { _Smtx_lock_exclusive(_Spinlock); } @@ -471,18 +570,6 @@ bool __stdcall _Atomic_wait_compare_non_lock_free( #ifdef _WIN64 inline bool __stdcall _Atomic_wait_compare_16_bytes(const void* _Storage, void* _Comparand, size_t, void*) noexcept { -#if defined(__clang__) && defined(_M_ARM64) // TRANSITION, Clang 12 - const auto _Dest = static_cast<__int128*>(const_cast(_Storage)); - const auto _Cmp = static_cast(_Comparand); - - do { - if (__builtin_arm_ldrex(_Dest) != *_Cmp) { - return false; - } - } while (__builtin_arm_strex(*_Cmp, _Dest)); - - return true; -#else // ^^^ workaround / no workaround vvv const auto _Dest = static_cast(const_cast(_Storage)); const auto _Cmp = static_cast(_Comparand); alignas(16) long long _Tmp[2] = {_Cmp[0], _Cmp[1]}; @@ -491,7 +578,6 @@ inline bool __stdcall _Atomic_wait_compare_16_bytes(const void* _Storage, void* #else // ^^^ _M_X64 / ARM64 vvv return _InterlockedCompareExchange128_nf(_Dest, _Tmp[1], _Tmp[0], _Tmp) != 0; #endif // ^^^ ARM64 ^^^ -#endif // TRANSITION, Clang 12 } #endif // _WIN64 #endif // _HAS_CXX20 diff --git a/stl/inc/bit b/stl/inc/bit index d933364942c..56e6cdcb37f 100644 --- a/stl/inc/bit +++ b/stl/inc/bit @@ -211,6 +211,24 @@ _NODISCARD int _Checked_x86_x64_popcount(const _Ty _Val) noexcept { #if defined(_M_ARM) || defined(_M_ARM64) +#ifdef __clang__ // TRANSITION, GH-1586 +_NODISCARD constexpr int _Clang_arm_arm64_countl_zero(const unsigned short _Val) { + return __builtin_clzs(_Val); +} + +_NODISCARD constexpr int _Clang_arm_arm64_countl_zero(const unsigned int _Val) { + return __builtin_clz(_Val); +} + +_NODISCARD constexpr int _Clang_arm_arm64_countl_zero(const unsigned long _Val) { + return __builtin_clzl(_Val); +} + +_NODISCARD constexpr int _Clang_arm_arm64_countl_zero(const unsigned long long _Val) { + return __builtin_clzll(_Val); +} +#endif // TRANSITION, GH-1586 + template _NODISCARD int _Checked_arm_arm64_countl_zero(const _Ty _Val) noexcept { constexpr int _Digits = numeric_limits<_Ty>::digits; @@ -218,11 +236,20 @@ _NODISCARD int _Checked_arm_arm64_countl_zero(const _Ty _Val) noexcept { return _Digits; } +#ifdef __clang__ // TRANSITION, GH-1586 + if constexpr (is_same_v, unsigned char>) { + return _Clang_arm_arm64_countl_zero(static_cast(_Val)) + - (numeric_limits::digits - _Digits); + } else { + return _Clang_arm_arm64_countl_zero(_Val); + } +#else // ^^^ workaround / no workaround vvv if constexpr (_Digits <= 32) { - return _CountLeadingZeros(_Val); + return static_cast(_CountLeadingZeros(_Val)); } else { - return _CountLeadingZeros64(_Val); + return static_cast(_CountLeadingZeros64(_Val)); } +#endif // TRANSITION, GH-1586 } #endif // defined(_M_ARM) || defined(_M_ARM64) diff --git a/stl/inc/bitset b/stl/inc/bitset index 8d21a2676ce..ff75bdd526b 100644 --- a/stl/inc/bitset +++ b/stl/inc/bitset @@ -370,9 +370,11 @@ public: return _CSTD memcmp(&_Array[0], &_Right._Array[0], sizeof(_Array)) == 0; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const bitset& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool test(size_t _Pos) const { if (_Bits <= _Pos) { diff --git a/stl/inc/charconv b/stl/inc/charconv index 20d8342c41b..bdb31a1b857 100644 --- a/stl/inc/charconv +++ b/stl/inc/charconv @@ -127,6 +127,17 @@ _NODISCARD to_chars_result _Integer_to_chars( } while (_Value != 0); break; + case 3: + case 5: + case 6: + case 7: + case 9: + do { + *--_RNext = static_cast('0' + _Value % _Base); + _Value = static_cast<_Unsigned>(_Value / _Base); + } while (_Value != 0); + break; + default: do { *--_RNext = _Charconv_digits[_Value % _Base]; @@ -198,6 +209,9 @@ to_chars_result to_chars(char* _First, char* _Last, bool _Value, int _Base = 10) struct from_chars_result { const char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const from_chars_result&, const from_chars_result&) = default; +#endif // _HAS_CXX20 }; // FUNCTION from_chars (STRING TO INTEGER) diff --git a/stl/inc/chrono b/stl/inc/chrono index d53e2873cb6..69d0f848fcf 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -15,6 +15,13 @@ #include #include +#if _HAS_CXX20 +#include +#ifdef __cpp_lib_concepts +#include +#endif // defined(__cpp_lib_concepts) +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -50,6 +57,23 @@ namespace chrono { } }; +#if _HAS_CXX20 + template + concept _Is_clock = requires { + typename _Clock::rep; + typename _Clock::period; + typename _Clock::duration; + typename _Clock::time_point; + _Clock::is_steady; + _Clock::now(); + }; + + template + struct is_clock : bool_constant<_Is_clock<_Clock>> {}; + template + inline constexpr bool is_clock_v = _Is_clock<_Clock>; +#endif // _HAS_CXX20 + // CLASS TEMPLATE duration template > class duration; @@ -89,7 +113,7 @@ namespace chrono { int> = 0> constexpr duration(const duration<_Rep2, _Period2>& _Dur) noexcept( is_arithmetic_v<_Rep>&& is_arithmetic_v<_Rep2>) // strengthened - : _MyRep(chrono::duration_cast(_Dur).count()) {} + : _MyRep(_CHRONO duration_cast(_Dur).count()) {} _NODISCARD constexpr _Rep count() const noexcept(is_arithmetic_v<_Rep>) /* strengthened */ { return _MyRep; @@ -196,6 +220,23 @@ namespace chrono { return _MyDur; } +#if _HAS_CXX20 + constexpr time_point& operator++() noexcept(is_arithmetic_v) /* strengthened */ { + ++_MyDur; + return *this; + } + constexpr time_point operator++(int) noexcept(is_arithmetic_v) /* strengthened */ { + return time_point{_MyDur++}; + } + constexpr time_point& operator--() noexcept(is_arithmetic_v) /* strengthened */ { + --_MyDur; + return *this; + } + constexpr time_point operator--(int) noexcept(is_arithmetic_v) /* strengthened */ { + return time_point{_MyDur--}; + } +#endif // _HAS_CXX20 + _CONSTEXPR17 time_point& operator+=(const _Duration& _Dur) noexcept(is_arithmetic_v) /* strengthened */ { _MyDur += _Dur; return *this; @@ -225,16 +266,16 @@ struct _Lcm : integral_constant::value) * _Bx> { // STRUCT TEMPLATE common_type SPECIALIZATIONS template -struct common_type, - chrono::duration<_Rep2, _Period2>> { // common type of two durations - using type = chrono::duration, +struct common_type<_CHRONO duration<_Rep1, _Period1>, + _CHRONO duration<_Rep2, _Period2>> { // common type of two durations + using type = _CHRONO duration, ratio<_Gcd<_Period1::num, _Period2::num>::value, _Lcm<_Period1::den, _Period2::den>::value>>; }; template -struct common_type, - chrono::time_point<_Clock, _Duration2>> { // common type of two time points - using type = chrono::time_point<_Clock, common_type_t<_Duration1, _Duration2>>; +struct common_type<_CHRONO time_point<_Clock, _Duration1>, + _CHRONO time_point<_Clock, _Duration2>> { // common type of two time points + using type = _CHRONO time_point<_Clock, common_type_t<_Duration1, _Duration2>>; }; namespace chrono { @@ -332,12 +373,14 @@ namespace chrono { return _CT(_Left).count() == _CT(_Right).count(); } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept( is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool @@ -368,6 +411,19 @@ namespace chrono { return !(_Left < _Right); } +#ifdef __cpp_lib_concepts + // clang-format off + template + requires three_way_comparable, duration<_Rep2, _Period2>>::rep> + _NODISCARD constexpr auto + operator<=>(const duration<_Rep1, _Period1>& _Left, const duration<_Rep2, _Period2>& _Right) noexcept( + is_arithmetic_v<_Rep1>&& is_arithmetic_v<_Rep2>) /* strengthened */ { + // clang-format on + using _CT = common_type_t, duration<_Rep2, _Period2>>; + return _CT(_Left).count() <=> _CT(_Right).count(); + } +#endif // defined(__cpp_lib_concepts) + // FUNCTION TEMPLATE duration_cast template , int> _Enabled> _NODISCARD constexpr _To duration_cast(const duration<_Rep, _Period>& _Dur) noexcept( @@ -405,7 +461,7 @@ namespace chrono { is_arithmetic_v<_Rep>&& is_arithmetic_v) /* strengthened */ { // convert duration to another duration; round towards negative infinity // i.e. the greatest integral result such that the result <= _Dur - const _To _Casted{chrono::duration_cast<_To>(_Dur)}; + const _To _Casted{_CHRONO duration_cast<_To>(_Dur)}; if (_Casted > _Dur) { return _To{_Casted.count() - static_cast(1)}; } @@ -419,7 +475,7 @@ namespace chrono { is_arithmetic_v<_Rep>&& is_arithmetic_v) /* strengthened */ { // convert duration to another duration; round towards positive infinity // i.e. the least integral result such that _Dur <= the result - const _To _Casted{chrono::duration_cast<_To>(_Dur)}; + const _To _Casted{_CHRONO duration_cast<_To>(_Dur)}; if (_Casted < _Dur) { return _To{_Casted.count() + static_cast(1)}; } @@ -439,7 +495,7 @@ namespace chrono { _NODISCARD constexpr _To round(const duration<_Rep, _Period>& _Dur) noexcept( is_arithmetic_v<_Rep>&& is_arithmetic_v) /* strengthened */ { // convert duration to another duration, round to nearest, ties to even - const _To _Floored{chrono::floor<_To>(_Dur)}; + const _To _Floored{_CHRONO floor<_To>(_Dur)}; const _To _Ceiled{_Floored + _To{1}}; const auto _Floor_adjustment = _Dur - _Floored; const auto _Ceil_adjustment = _Ceiled - _Dur; @@ -466,6 +522,12 @@ namespace chrono { using seconds = duration; using minutes = duration>; using hours = duration>; +#if _HAS_CXX20 + using days = duration, hours::period>>; + using weeks = duration, days::period>>; + using years = duration, days::period>>; + using months = duration>>; +#endif // _HAS_CXX20 // time_point ARITHMETIC template @@ -506,12 +568,14 @@ namespace chrono { return _Left.time_since_epoch() == _Right.time_since_epoch(); } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const time_point<_Clock, _Duration1>& _Left, const time_point<_Clock, _Duration2>& _Right) noexcept( is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool @@ -541,12 +605,21 @@ namespace chrono { return !(_Left < _Right); } +#ifdef __cpp_lib_concepts + template _Duration2> + _NODISCARD constexpr auto + operator<=>(const time_point<_Clock, _Duration1>& _Left, const time_point<_Clock, _Duration2>& _Right) noexcept( + is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { + return _Left.time_since_epoch() <=> _Right.time_since_epoch(); + } +#endif // defined(__cpp_lib_concepts) + // FUNCTION TEMPLATE time_point_cast template , int> = 0> _NODISCARD constexpr time_point<_Clock, _To> time_point_cast(const time_point<_Clock, _Duration>& _Time) noexcept( is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { // change the duration type of a time_point; truncate - return time_point<_Clock, _To>(chrono::duration_cast<_To>(_Time.time_since_epoch())); + return time_point<_Clock, _To>(_CHRONO duration_cast<_To>(_Time.time_since_epoch())); } // FUNCTION TEMPLATE floor (for time_point instances) @@ -554,7 +627,7 @@ namespace chrono { _NODISCARD constexpr time_point<_Clock, _To> floor(const time_point<_Clock, _Duration>& _Time) noexcept( is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { // change the duration type of a time_point; round towards negative infinity - return time_point<_Clock, _To>(chrono::floor<_To>(_Time.time_since_epoch())); + return time_point<_Clock, _To>(_CHRONO floor<_To>(_Time.time_since_epoch())); } // FUNCTION TEMPLATE ceil (for time_point instances) @@ -562,7 +635,7 @@ namespace chrono { _NODISCARD constexpr time_point<_Clock, _To> ceil(const time_point<_Clock, _Duration>& _Time) noexcept( is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { // change the duration type of a time_point; round towards positive infinity - return time_point<_Clock, _To>(chrono::ceil<_To>(_Time.time_since_epoch())); + return time_point<_Clock, _To>(_CHRONO ceil<_To>(_Time.time_since_epoch())); } // FUNCTION TEMPLATE round (for time_point instances) @@ -571,15 +644,15 @@ namespace chrono { _NODISCARD constexpr time_point<_Clock, _To> round(const time_point<_Clock, _Duration>& _Time) noexcept( is_arithmetic_v&& is_arithmetic_v) /* strengthened */ { // change the duration type of a time_point; round to nearest, ties to even - return time_point<_Clock, _To>(chrono::round<_To>(_Time.time_since_epoch())); + return time_point<_Clock, _To>(_CHRONO round<_To>(_Time.time_since_epoch())); } // CLOCKS struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime using rep = long long; using period = ratio<1, 10'000'000>; // 100 nanoseconds - using duration = chrono::duration; - using time_point = chrono::time_point; + using duration = _CHRONO duration; + using time_point = _CHRONO time_point; static constexpr bool is_steady = false; _NODISCARD static time_point now() noexcept { // get current time @@ -595,11 +668,19 @@ namespace chrono { } }; +#if _HAS_CXX20 + // sys_time ALIASES + template + using sys_time = time_point; + using sys_seconds = sys_time; + using sys_days = sys_time; +#endif // _HAS_CXX20 + struct steady_clock { // wraps QueryPerformanceCounter using rep = long long; using period = nano; using duration = nanoseconds; - using time_point = chrono::time_point; + using time_point = _CHRONO time_point; static constexpr bool is_steady = true; _NODISCARD static time_point now() noexcept { // get current time @@ -699,28 +780,1457 @@ namespace chrono { return _Os << _Sstr.str(); } + + struct local_t {}; + + template + using local_time = time_point; + using local_seconds = local_time; + using local_days = local_time; + + struct last_spec { + explicit last_spec() = default; + }; + inline constexpr last_spec last{}; + + class day { + public: + day() = default; + constexpr explicit day(unsigned int _Val) noexcept : _Day{static_cast(_Val)} {} + + constexpr day& operator++() noexcept { + ++_Day; + return *this; + } + constexpr day operator++(int) noexcept { + return day{_Day++}; + } + constexpr day& operator--() noexcept { + --_Day; + return *this; + } + constexpr day operator--(int) noexcept { + return day{_Day--}; + } + + constexpr day& operator+=(const days& _Days) noexcept { + _Day += static_cast(_Days.count()); + return *this; + } + constexpr day& operator-=(const days& _Days) noexcept { + _Day -= static_cast(_Days.count()); + return *this; + } + + _NODISCARD constexpr explicit operator unsigned int() const noexcept { + return _Day; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Day >= 1 && _Day <= 31; + } + + private: + unsigned char _Day; + }; + + _NODISCARD constexpr bool operator==(const day& _Left, const day& _Right) noexcept { + return static_cast(_Left) == static_cast(_Right); + } + _NODISCARD constexpr strong_ordering operator<=>(const day& _Left, const day& _Right) noexcept { + return static_cast(_Left) <=> static_cast(_Right); + } + + _NODISCARD constexpr day operator+(const day& _Left, const days& _Right) noexcept { + return day{static_cast(_Left) + _Right.count()}; + } + _NODISCARD constexpr day operator+(const days& _Left, const day& _Right) noexcept { + return _Right + _Left; + } + _NODISCARD constexpr day operator-(const day& _Left, const days& _Right) noexcept { + return day{static_cast(_Left) - _Right.count()}; + } + _NODISCARD constexpr days operator-(const day& _Left, const day& _Right) noexcept { + return days{ + static_cast(static_cast(_Left)) - static_cast(static_cast(_Right))}; + } + + class month { + public: + month() = default; + constexpr explicit month(unsigned int _Val) noexcept : _Month{static_cast(_Val)} {} + + constexpr month& operator++() noexcept { + *this += months{1}; + return *this; + } + constexpr month operator++(int) noexcept { + month _Temp{*this}; + ++*this; + return _Temp; + } + constexpr month& operator--() noexcept { + *this -= months{1}; + return *this; + } + constexpr month operator--(int) noexcept { + month _Temp{*this}; + --*this; + return _Temp; + } + + constexpr month& operator+=(const months& _Months) noexcept; + constexpr month& operator-=(const months& _Months) noexcept; + + _NODISCARD constexpr explicit operator unsigned int() const noexcept { + return _Month; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Month >= 1 && _Month <= 12; + } + + private: + unsigned char _Month; + }; + + _NODISCARD constexpr bool operator==(const month& _Left, const month& _Right) noexcept { + return static_cast(_Left) == static_cast(_Right); + } + _NODISCARD constexpr strong_ordering operator<=>(const month& _Left, const month& _Right) noexcept { + return static_cast(_Left) <=> static_cast(_Right); + } + + _NODISCARD constexpr month operator+(const month& _Left, const months& _Right) noexcept { + const auto _Mo = static_cast(static_cast(_Left)) + (_Right.count() - 1); + const auto _Div = (_Mo >= 0 ? _Mo : _Mo - 11) / 12; + return month{static_cast(_Mo - _Div * 12 + 1)}; + } + _NODISCARD constexpr month operator+(const months& _Left, const month& _Right) noexcept { + return _Right + _Left; + } + _NODISCARD constexpr month operator-(const month& _Left, const months& _Right) noexcept { + return _Left + -_Right; + } + _NODISCARD constexpr months operator-(const month& _Left, const month& _Right) noexcept { + const auto _Mo = static_cast(_Left) - static_cast(_Right); + return months{_Mo <= 11 ? _Mo : _Mo + 12}; + } + + constexpr month& month::operator+=(const months& _Months) noexcept { + *this = *this + _Months; + return *this; + } + constexpr month& month::operator-=(const months& _Months) noexcept { + *this = *this - _Months; + return *this; + } + + class year { + public: + year() = default; + constexpr explicit year(int _Val) noexcept : _Year{static_cast(_Val)} {} + + constexpr year& operator++() noexcept { + ++_Year; + return *this; + } + constexpr year operator++(int) noexcept { + return year{_Year++}; + } + constexpr year& operator--() noexcept { + --_Year; + return *this; + } + constexpr year operator--(int) noexcept { + return year{_Year--}; + } + + constexpr year& operator+=(const years& _Years) noexcept { +#ifdef __EDG__ // TRANSITION, VSO-1271098 + _Year = static_cast(_Year + _Years.count()); +#else // ^^^ workaround / no workaround vvv + _Year += static_cast(_Years.count()); +#endif // ^^^ no workaround ^^^ + return *this; + } + constexpr year& operator-=(const years& _Years) noexcept { +#ifdef __EDG__ // TRANSITION, VSO-1271098 + _Year = static_cast(_Year - _Years.count()); +#else // ^^^ workaround / no workaround vvv + _Year -= static_cast(_Years.count()); +#endif // ^^^ no workaround ^^^ + return *this; + } + + _NODISCARD constexpr year operator+() const noexcept { + return *this; + } + _NODISCARD constexpr year operator-() const noexcept { + return year{-_Year}; + } + + _NODISCARD constexpr bool is_leap() const noexcept { + return _Year % 4 == 0 && (_Year % 100 != 0 || _Year % 400 == 0); + } + + _NODISCARD constexpr explicit operator int() const noexcept { + return _Year; + } + + _NODISCARD constexpr bool ok() const noexcept { + return _Year_min <= _Year && _Year <= _Year_max; + } + + _NODISCARD static constexpr year(min)() noexcept { + return year{_Year_min}; + } + _NODISCARD static constexpr year(max)() noexcept { + return year{_Year_max}; + } + + private: + short _Year; + static constexpr int _Year_min = -32767; + static constexpr int _Year_max = 32767; + }; + + _NODISCARD constexpr bool operator==(const year& _Left, const year& _Right) noexcept { + return static_cast(_Left) == static_cast(_Right); + } + _NODISCARD constexpr strong_ordering operator<=>(const year& _Left, const year& _Right) noexcept { + return static_cast(_Left) <=> static_cast(_Right); + } + + _NODISCARD constexpr year operator+(const year& _Left, const years& _Right) noexcept { + return year{static_cast(_Left) + _Right.count()}; + } + _NODISCARD constexpr year operator+(const years& _Left, const year& _Right) noexcept { + return _Right + _Left; + } + _NODISCARD constexpr year operator-(const year& _Left, const years& _Right) noexcept { + return _Left + -_Right; + } + _NODISCARD constexpr years operator-(const year& _Left, const year& _Right) noexcept { + return years{static_cast(_Left) - static_cast(_Right)}; + } + + class weekday_indexed; + class weekday_last; + + class weekday { + public: + weekday() = default; + constexpr explicit weekday(unsigned int _Val) noexcept + : _Weekday{static_cast(_Val == 7 ? 0 : _Val)} {} + constexpr weekday(const sys_days& _Sys_day) noexcept + : _Weekday{static_cast(_Weekday_from_days(_Sys_day.time_since_epoch().count()))} {} + constexpr explicit weekday(const local_days& _Local_day) noexcept + : _Weekday{static_cast(_Weekday_from_days(_Local_day.time_since_epoch().count()))} {} + + constexpr weekday& operator++() noexcept { + return *this += days{1}; + } + constexpr weekday operator++(int) noexcept { + weekday _Temp{*this}; + ++*this; + return _Temp; + } + constexpr weekday& operator--() noexcept { + return *this -= days{1}; + } + constexpr weekday operator--(int) noexcept { + weekday _Temp{*this}; + --*this; + return _Temp; + } + + constexpr weekday& operator+=(const days& _Days) noexcept; + constexpr weekday& operator-=(const days& _Days) noexcept; + + _NODISCARD constexpr unsigned int c_encoding() const noexcept { + return _Weekday; + } + _NODISCARD constexpr unsigned int iso_encoding() const noexcept { + return _Weekday == 0u ? 7u : _Weekday; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Weekday <= 6; + } + + _NODISCARD constexpr weekday_indexed operator[](unsigned int _Index) const noexcept; + _NODISCARD constexpr weekday_last operator[](last_spec) const noexcept; + + private: + unsigned char _Weekday; + + // courtesy of Howard Hinnant + // https://howardhinnant.github.io/date_algorithms.html#weekday_from_days + _NODISCARD static constexpr unsigned int _Weekday_from_days(int _Tp) noexcept { + return static_cast(_Tp >= -4 ? (_Tp + 4) % 7 : (_Tp + 5) % 7 + 6); + } + }; + + _NODISCARD constexpr bool operator==(const weekday& _Left, const weekday& _Right) noexcept { + return _Left.c_encoding() == _Right.c_encoding(); + } + + _NODISCARD constexpr weekday operator+(const weekday& _Left, const days& _Right) noexcept { + const auto _Wd = static_cast(_Left.c_encoding()) + _Right.count(); + const auto _Div = (_Wd >= 0 ? _Wd : _Wd - 6) / 7; + return weekday{static_cast(_Wd - _Div * 7)}; + } + _NODISCARD constexpr weekday operator+(const days& _Left, const weekday& _Right) noexcept { + return _Right + _Left; + } + _NODISCARD constexpr weekday operator-(const weekday& _Left, const days& _Right) noexcept { + return _Left + -_Right; + } + _NODISCARD constexpr days operator-(const weekday& _Left, const weekday& _Right) noexcept { + const auto _Wd = _Left.c_encoding() - _Right.c_encoding(); + const auto _Wk = _Wd <= 6 ? _Wd : _Wd + 7; + return days{_Wk}; + } + + constexpr weekday& weekday::operator+=(const days& _Days) noexcept { + *this = *this + _Days; + return *this; + } + constexpr weekday& weekday::operator-=(const days& _Days) noexcept { + *this = *this - _Days; + return *this; + } + + class weekday_indexed { + public: + weekday_indexed() = default; + constexpr weekday_indexed(const weekday& _Wd, unsigned int _Idx) noexcept + : _Weekday{_Wd}, _Index{static_cast(_Idx)} {} + + _NODISCARD constexpr weekday weekday() const noexcept { + return _Weekday; + } + _NODISCARD constexpr unsigned int index() const noexcept { + return _Index; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Weekday.ok() && _Index >= 1 && _Index <= 5; + } + + private: + _CHRONO weekday _Weekday; + unsigned char _Index; + }; + + _NODISCARD constexpr bool operator==(const weekday_indexed& _Left, const weekday_indexed& _Right) noexcept { + return _Left.weekday() == _Right.weekday() && _Left.index() == _Right.index(); + } + + class weekday_last { + public: + constexpr explicit weekday_last(const weekday& _Wd) noexcept : _Weekday{_Wd} {} + + _NODISCARD constexpr weekday weekday() const noexcept { + return _Weekday; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Weekday.ok(); + } + + private: + _CHRONO weekday _Weekday; + }; + + _NODISCARD constexpr bool operator==(const weekday_last& _Left, const weekday_last& _Right) noexcept { + return _Left.weekday() == _Right.weekday(); + } + + _NODISCARD constexpr weekday_indexed weekday::operator[](unsigned int _Index) const noexcept { + return {*this, _Index}; + } + _NODISCARD constexpr weekday_last weekday::operator[](last_spec) const noexcept { + return weekday_last{*this}; + } + + class month_day { + public: + month_day() = default; + constexpr month_day(const month& _Month_, const day& _Day_) noexcept : _Month{_Month_}, _Day{_Day_} {} + + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + _NODISCARD constexpr day day() const noexcept { + return _Day; + } + _NODISCARD constexpr bool ok() const noexcept { + if (!_Month.ok() || !_Day.ok()) { + return false; + } + + const auto _Da = static_cast(_Day); + const auto _Mo = static_cast(_Month); + if (_Mo == 2) { + return _Da <= 29; + } + + if (_Mo == 4 || _Mo == 6 || _Mo == 9 || _Mo == 11) { + return _Da <= 30; + } + return true; + } + + private: + _CHRONO month _Month; + _CHRONO day _Day; + }; + + _NODISCARD constexpr bool operator==(const month_day& _Left, const month_day& _Right) noexcept { + return _Left.month() == _Right.month() && _Left.day() == _Right.day(); + } + _NODISCARD constexpr strong_ordering operator<=>(const month_day& _Left, const month_day& _Right) noexcept { + const auto _Comp = _Left.month() <=> _Right.month(); + if (_Comp != 0) { + return _Comp; + } + + return _Left.day() <=> _Right.day(); + } + + class month_day_last { + public: + constexpr explicit month_day_last(const month& _Month_) noexcept : _Month{_Month_} {} + + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Month.ok(); + } + + private: + _CHRONO month _Month; + }; + + _NODISCARD constexpr bool operator==(const month_day_last& _Left, const month_day_last& _Right) noexcept { + return _Left.month() == _Right.month(); + } + _NODISCARD constexpr strong_ordering operator<=>( + const month_day_last& _Left, const month_day_last& _Right) noexcept { + return _Left.month() <=> _Right.month(); + } + + class month_weekday { + public: + constexpr month_weekday(const month& _Month_, const weekday_indexed& _Wdi) noexcept + : _Month{_Month_}, _Weekday_index{_Wdi} {} + + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + _NODISCARD constexpr weekday_indexed weekday_indexed() const noexcept { + return _Weekday_index; + } + + _NODISCARD constexpr bool ok() const noexcept { + return _Month.ok() && _Weekday_index.ok(); + } + + private: + _CHRONO month _Month; + _CHRONO weekday_indexed _Weekday_index; + }; + + _NODISCARD constexpr bool operator==(const month_weekday& _Left, const month_weekday& _Right) noexcept { + return _Left.month() == _Right.month() && _Left.weekday_indexed() == _Right.weekday_indexed(); + } + + class month_weekday_last { + public: + constexpr month_weekday_last(const month& _Month_, const weekday_last& _Wdl) noexcept + : _Month{_Month_}, _Weekday_last{_Wdl} {} + + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + _NODISCARD constexpr weekday_last weekday_last() const noexcept { + return _Weekday_last; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Month.ok() && _Weekday_last.ok(); + } + + private: + _CHRONO month _Month; + _CHRONO weekday_last _Weekday_last; + }; + + _NODISCARD constexpr bool operator==(const month_weekday_last& _Left, const month_weekday_last& _Right) noexcept { + return _Left.month() == _Right.month() && _Left.weekday_last() == _Right.weekday_last(); + } + + class year_month { + public: + year_month() = default; + constexpr year_month(const year& _Year_, const month& _Month_) noexcept : _Year{_Year_}, _Month{_Month_} {} + + _NODISCARD constexpr year year() const noexcept { + return _Year; + } + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + + template + constexpr year_month& operator+=(const months& _Months) noexcept; + template + constexpr year_month& operator-=(const months& _Months) noexcept; + constexpr year_month& operator+=(const years& _Years) noexcept; + constexpr year_month& operator-=(const years& _Years) noexcept; + + _NODISCARD constexpr bool ok() const noexcept { + return _Year.ok() && _Month.ok(); + } + + private: + _CHRONO year _Year; + _CHRONO month _Month; + }; + + _NODISCARD constexpr bool operator==(const year_month& _Left, const year_month& _Right) noexcept { + return _Left.year() == _Right.year() && _Left.month() == _Right.month(); + } + _NODISCARD constexpr strong_ordering operator<=>(const year_month& _Left, const year_month& _Right) noexcept { + const auto _Comp = _Left.year() <=> _Right.year(); + if (_Comp != 0) { + return _Comp; + } + + return _Left.month() <=> _Right.month(); + } + + template + _NODISCARD constexpr year_month operator+(const year_month& _Left, const months& _Right) noexcept { + const auto _Mo = static_cast(static_cast(_Left.month())) + (_Right.count() - 1); + const auto _Div = (_Mo >= 0 ? _Mo : _Mo - 11) / 12; + return year_month{_Left.year() + years{_Div}, month{static_cast(_Mo - _Div * 12 + 1)}}; + } + template + _NODISCARD constexpr year_month operator+(const months& _Left, const year_month& _Right) noexcept { + return _Right + _Left; + } + + template + _NODISCARD constexpr year_month operator-(const year_month& _Left, const months& _Right) noexcept { + return _Left + -_Right; + } + + _NODISCARD constexpr months operator-(const year_month& _Left, const year_month& _Right) noexcept { + return _Left.year() - _Right.year() + + months{static_cast(static_cast(_Left.month())) + - static_cast(static_cast(_Right.month()))}; + } + + _NODISCARD constexpr year_month operator+(const year_month& _Left, const years& _Right) noexcept { + return {year{_Left.year() + _Right}, _Left.month()}; + } + + _NODISCARD constexpr year_month operator+(const years& _Left, const year_month& _Right) noexcept { + return _Right + _Left; + } + + _NODISCARD constexpr year_month operator-(const year_month& _Left, const years& _Right) noexcept { + return _Left + -_Right; + } + + template + constexpr year_month& year_month::operator+=(const months& _Months) noexcept { + *this = *this + _Months; + return *this; + } + template + constexpr year_month& year_month::operator-=(const months& _Months) noexcept { + *this = *this - _Months; + return *this; + } + constexpr year_month& year_month::operator+=(const years& _Years) noexcept { + *this = *this + _Years; + return *this; + } + constexpr year_month& year_month::operator-=(const years& _Years) noexcept { + *this = *this - _Years; + return *this; + } + + // To prevent UB by going out of bounds, four extra days with an invalid day are added. + inline constexpr day _Last_day_table[] = {day{31}, day{28}, day{31}, day{30}, day{31}, day{30}, day{31}, day{31}, + day{30}, day{31}, day{30}, day{31}, day{255}, day{255}, day{255}, day{255}}; + + _NODISCARD constexpr day _Last_day(const year& _Year, const month& _Month) { + if (_Month == month{2} && _Year.is_leap()) { + return day{29}; + } + + return _Last_day_table[(static_cast(_Month) - 1) & 0xF]; + } + + class year_month_day_last; + + class year_month_day { + public: + year_month_day() = default; + constexpr year_month_day(const year& _Year_, const month& _Month_, const day& _Day_) noexcept + : _Year{_Year_}, _Month{_Month_}, _Day{_Day_} {} + constexpr year_month_day(const year_month_day_last& _Ymdl) noexcept; + constexpr year_month_day(const sys_days& _Sys_days) noexcept + : year_month_day{_Civil_from_days(_Sys_days.time_since_epoch().count())} {} + constexpr explicit year_month_day(const local_days& _Local_days) noexcept + : year_month_day{_Civil_from_days(_Local_days.time_since_epoch().count())} {} + + template + constexpr year_month_day& operator+=(const months& _Months) noexcept; + template + constexpr year_month_day& operator-=(const months& _Months) noexcept; + constexpr year_month_day& operator+=(const years& _Years) noexcept; + constexpr year_month_day& operator-=(const years& _Years) noexcept; + + _NODISCARD constexpr year year() const noexcept { + return _Year; + } + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + _NODISCARD constexpr day day() const noexcept { + return _Day; + } + + _NODISCARD constexpr operator sys_days() const noexcept { + return sys_days{_Days_from_civil()}; + } + _NODISCARD constexpr explicit operator local_days() const noexcept { + return local_days{static_cast(*this).time_since_epoch()}; + } + _NODISCARD constexpr bool ok() const noexcept { + if (!_Year.ok() || !_Month.ok()) { + return false; + } + + return _Day >= _CHRONO day{1} && _Day <= _Last_day(_Year, _Month); + } + + private: + _CHRONO year _Year; + _CHRONO month _Month; + _CHRONO day _Day; + + // _Civil_from_days and _Days_from_civil perform conversions between the dates in the (proleptic) Gregorian + // calendar and the continuous count of days since 1970-01-01. + + // To simplify the handling of leap days (February 29th), the algorithm below uses a modified calendar + // internally, in which each year begins on March 1st, while January and February belong to the previous year. + // We denote the modified year and month number as _Yp and _Mp. We also define modified centuries that begin on + // each modified year whose _Yp is a multiple of 100. + + // _Mp | Month | Day of Year + // --- | --------- | ----------- + // 0 | March | [ 0, 30] + // 1 | April | [ 31, 60] + // 2 | May | [ 61, 91] + // 3 | June | [ 92, 121] + // 4 | July | [122, 152] + // 5 | August | [153, 183] + // 6 | September | [184, 213] + // 7 | October | [214, 244] + // 8 | November | [245, 274] + // 9 | December | [275, 305] + // 10 | January | [306, 336] + // 11 | February | [337, 365] on leap years, [337, 364] on regular years + + // _Yp | First Day | Last Day (inclusive) | Leap Year? + // --- | ----------- | -------------------- | ---------- + // -4 | -0004-03-01 | -0003-02-28 | No + // -3 | -0003-03-01 | -0002-02-28 | No + // -2 | -0002-03-01 | -0001-02-28 | No + // -1 | -0001-03-01 | 0000-02-29 | Yes + // 0 | 0000-03-01 | 0001-02-28 | No + // 1 | 0001-03-01 | 0002-02-28 | No + // 2 | 0002-03-01 | 0003-02-28 | No + // 3 | 0003-03-01 | 0004-02-29 | Yes + + // _Century | First Day | Last Day (inclusive) | Long Century? + // -------- | ----------- | -------------------- | ------------- + // -4 | -0400-03-01 | -0300-02-28 | No + // -3 | -0300-03-01 | -0200-02-28 | No + // -2 | -0200-03-01 | -0100-02-28 | No + // -1 | -0100-03-01 | 0000-02-29 | Yes + // 0 | 0000-03-01 | 0100-02-28 | No + // 1 | 0100-03-01 | 0200-02-28 | No + // 2 | 0200-03-01 | 0300-02-28 | No + // 3 | 0300-03-01 | 0400-02-29 | Yes + + // The structure of the modified calendar: + // 1 ) It has a period of 4 centuries. + // 2 ) Each calendar period (146097 days) contains 3 regular centuries followed by a long century (36525 days). + // 3 ) Each regular century (36524 days) contains 24 regular 4-year spans followed by a short 4-year span. + // 3') Each long century (36525 days) contains 25 regular 4-year spans. + // 4 ) Each regular 4-year span (1461 days) contains 3 regular years followed by a leap year. + // 4') Each short 4-year span (1460 days) contains 4 regular years. + + // Formula 1: Compute _Day_of_year of the first day of month _Mp + // + // _Day_of_year = (979 * _Mp + 19) >> 5 + // + // A more well-known formula is 30 * _Mp + floor((3 * _Mp + 2) / 5) or floor((153 * _Mp + 2) / 5), which is used + // in Howard Hinnant's paper. + // + // The formula above returns the same result for all _Mp in [0, 11]. + // Note that 979 / 2^5 = 30.59375 ~= 30.6 = 153 / 5. + + // Formula 1': Compute _Mp from _Day_of_year + // + // _Mp = (535 * _Day_of_year + 333) >> 14 + // + // Howard Hinnant's paper uses floor((5 * _Day_of_year + 2) / 153), the inverse of floor((153 * _Mp + 2) / 5) or + // ceil((153 * _Mp - 2) / 5). + // + // The formula above returns the same result for all _Day_of_year in [0, 365]. + // Note that 2^14 / 535 = 30.624... ~= 30.6 = 153 / 5. + + // Formula 2: Compute _Zx of the first day of year _Yp, where _Zx is the continuous count of days since + // 0000-03-01. + // + // _Zx = ((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + // + // Start with multiplying by the number of days in regular years (365), add one day for the leap year in each + // 4-year span, subtract one day for the short 4-year span in each century, and finally add one day for the long + // century in each calendar period. This gives us 365 * _Yp + floor(_Yp / 4) - _Century + floor(_Century / 4). + + // Formula 2-1: Compute _Day_of_century of the first day of year _Year_of_century + // + // _Day_of_century = (1461 * _Year_of_century) >> 2 + // + // Start with multiplying by the number of days in regular years (365), add one day for the leap year in each + // 4-year span. This gives us 365 * _Year_of_century + floor(_Year_of_century / 4) + // == floor(1461 * _Year_of_century / 4). + + // Formula 2-1': Compute _Year_of_century from _Day_of_century + // + // _Year_of_century = (91867 * (_Day_of_century + 1)) >> 25 + // + // The inverse of floor(1461 * _Year_of_century / 4) or ceil((1461 * _Year_of_century - 3) / 4) is + // floor((4 * _Day_of_century + 3) / 1461). + // + // The formula above returns the same result for all _Day_of_century in [0, 36524]. + // Note that 2^25 / 91867 = 365.2501... ~= 365.25 = 1461 / 4. + + // Formula 2-2: Compute _Zx of the first day of century _Century, where _Zx is the continuous count of days + // since 0000-03-01. + // + // _Zx = (146097 * _Century) >> 2 + // + // Start with multiplying by the number of days in regular centuries (36524), add one day for the long century + // in each calendar period. This gives us 36524 * _Century + floor(_Century / 4) = floor(146097 * _Century / 4). + + // Formula 2-2': Compute _Century from _Zx, where _Zx is the continuous count of days since 0000-03-01. + // + // _Century = floor((4 * _Zx + 3) / 146097) + // + // This is the inverse of floor(146097 * _Year_of_century / 4) or ceil((146097 * _Year_of_century - 3) / 4) + + // courtesy of Howard Hinnant + // https://howardhinnant.github.io/date_algorithms.html#civil_from_days + _NODISCARD static constexpr year_month_day _Civil_from_days(int _Tp) noexcept { + static_assert(numeric_limits::digits >= 32); + static_assert(numeric_limits::digits >= 26); + const int _Zx = _Tp + 719468; // Shift epoch to 0000-03-01 + // Formula 2-2' + const int _Century = (_Zx >= 0 ? 4 * _Zx + 3 : 4 * _Zx - 146093) / 146097; + // Formula 2-2 + const unsigned int _Day_of_century = + static_cast(_Zx - ((146097 * _Century) >> 2)); // [0, 36524] + // Formula 2-1' + const unsigned int _Year_of_century = (91867 * (_Day_of_century + 1)) >> 25; // [0, 99] + const int _Yp = static_cast(_Year_of_century) + _Century * 100; // Where March is the first month + // Formula 2-1 + const unsigned int _Day_of_year = _Day_of_century - ((1461 * _Year_of_century) >> 2); // [0, 365] + // Formula 1' + const unsigned int _Mp = (535 * _Day_of_year + 333) >> 14; // [0, 11] + // Formula 1 + const unsigned int _Day = _Day_of_year - ((979 * _Mp + 19) >> 5) + 1; // [1, 31] + const unsigned int _Month = _Mp + (_Mp < 10 ? 3 : static_cast(-9)); // [1, 12] + return year_month_day{_CHRONO year{_Yp + (_Month <= 2)}, _CHRONO month{_Month}, _CHRONO day{_Day}}; + } + // courtesy of Howard Hinnant + // https://howardhinnant.github.io/date_algorithms.html#days_from_civil + _NODISCARD constexpr days _Days_from_civil() const noexcept { + static_assert(numeric_limits::digits >= 18); + static_assert(numeric_limits::digits >= 26); + const unsigned int _Mo = static_cast(_Month); // [1, 12] + const int _Yp = static_cast(_Year) - (_Mo <= 2); + const int _Century = (_Yp >= 0 ? _Yp : _Yp - 99) / 100; + const unsigned int _Mp = _Mo + (_Mo > 2 ? static_cast(-3) : 9); // [0, 11] + // Formula 1 + const int _Day_of_year = static_cast(((979 * _Mp + 19) >> 5) + static_cast(_Day)) - 1; + // Formula 2 + return days{((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468}; + } + }; + + _NODISCARD constexpr bool operator==(const year_month_day& _Left, const year_month_day& _Right) noexcept { + return _Left.year() == _Right.year() && _Left.month() == _Right.month() && _Left.day() == _Right.day(); + } + _NODISCARD constexpr strong_ordering operator<=>( + const year_month_day& _Left, const year_month_day& _Right) noexcept { + auto _Comp = _Left.year() <=> _Right.year(); + if (_Comp != 0) { + return _Comp; + } + + _Comp = _Left.month() <=> _Right.month(); + if (_Comp != 0) { + return _Comp; + } + + return _Left.day() <=> _Right.day(); + } + + template + _NODISCARD constexpr year_month_day operator+(const year_month_day& _Left, const months& _Right) noexcept { + const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right; + return {_Ym.year(), _Ym.month(), _Left.day()}; + } + + template + _NODISCARD constexpr year_month_day operator+(const months& _Left, const year_month_day& _Right) noexcept { + return _Right + _Left; + } + + template + _NODISCARD constexpr year_month_day operator-(const year_month_day& _Left, const months& _Right) noexcept { + return _Left + -_Right; + } + + _NODISCARD constexpr year_month_day operator+(const year_month_day& _Left, const years& _Right) noexcept { + return {_Left.year() + _Right, _Left.month(), _Left.day()}; + } + + _NODISCARD constexpr year_month_day operator+(const years& _Left, const year_month_day& _Right) noexcept { + return _Right + _Left; + } + + _NODISCARD constexpr year_month_day operator-(const year_month_day& _Left, const years& _Right) noexcept { + return _Left + -_Right; + } + + template + constexpr year_month_day& year_month_day::operator+=(const months& _Months) noexcept { + *this = *this + _Months; + return *this; + } + template + constexpr year_month_day& year_month_day::operator-=(const months& _Months) noexcept { + *this = *this - _Months; + return *this; + } + constexpr year_month_day& year_month_day::operator+=(const years& _Years) noexcept { + *this = *this + _Years; + return *this; + } + constexpr year_month_day& year_month_day::operator-=(const years& _Years) noexcept { + *this = *this - _Years; + return *this; + } + + class year_month_day_last { + public: + constexpr year_month_day_last(const year& _Year_, const month_day_last& _Mdl) noexcept + : _Year{_Year_}, _Month_day_last{_Mdl} {} + + template + constexpr year_month_day_last& operator+=(const months& _Months) noexcept; + template + constexpr year_month_day_last& operator-=(const months& _Months) noexcept; + constexpr year_month_day_last& operator+=(const years& _Years) noexcept; + constexpr year_month_day_last& operator-=(const years& _Years) noexcept; + + _NODISCARD constexpr year year() const noexcept { + return _Year; + } + _NODISCARD constexpr month month() const noexcept { + return _Month_day_last.month(); + } + _NODISCARD constexpr month_day_last month_day_last() const noexcept { + return _Month_day_last; + } + _NODISCARD constexpr day day() const noexcept { + return _Last_day(year(), month()); + } + + _NODISCARD constexpr operator sys_days() const noexcept { + return sys_days{year_month_day{year(), month(), day()}}; + } + _NODISCARD constexpr explicit operator local_days() const noexcept { + return local_days{static_cast(*this).time_since_epoch()}; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Year.ok() && _Month_day_last.ok(); + } + + private: + _CHRONO year _Year; + _CHRONO month_day_last _Month_day_last; + }; + + _NODISCARD constexpr bool operator==(const year_month_day_last& _Left, const year_month_day_last& _Right) noexcept { + return _Left.year() == _Right.year() && _Left.month_day_last() == _Right.month_day_last(); + } + _NODISCARD constexpr strong_ordering operator<=>( + const year_month_day_last& _Left, const year_month_day_last& _Right) noexcept { + const auto _Comp = _Left.year() <=> _Right.year(); + if (_Comp != 0) { + return _Comp; + } + + return _Left.month_day_last() <=> _Right.month_day_last(); + } + + template + _NODISCARD constexpr year_month_day_last operator+( + const year_month_day_last& _Left, const months& _Right) noexcept { + const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right; + return {_Ym.year(), month_day_last{_Ym.month()}}; + } + template + _NODISCARD constexpr year_month_day_last operator+( + const months& _Left, const year_month_day_last& _Right) noexcept { + return _Right + _Left; + } + + template + _NODISCARD constexpr year_month_day_last operator-( + const year_month_day_last& _Left, const months& _Right) noexcept { + return _Left + -_Right; + } + + _NODISCARD constexpr year_month_day_last operator+(const year_month_day_last& _Left, const years& _Right) noexcept { + return {_Left.year() + _Right, _Left.month_day_last()}; + } + _NODISCARD constexpr year_month_day_last operator+(const years& _Left, const year_month_day_last& _Right) noexcept { + return _Right + _Left; + } + _NODISCARD constexpr year_month_day_last operator-(const year_month_day_last& _Left, const years& _Right) noexcept { + return _Left + -_Right; + } + + template + constexpr year_month_day_last& year_month_day_last::operator+=(const months& _Months) noexcept { + *this = *this + _Months; + return *this; + } + template + constexpr year_month_day_last& year_month_day_last::operator-=(const months& _Months) noexcept { + *this = *this - _Months; + return *this; + } + constexpr year_month_day_last& year_month_day_last::operator+=(const years& _Years) noexcept { + *this = *this + _Years; + return *this; + } + constexpr year_month_day_last& year_month_day_last::operator-=(const years& _Years) noexcept { + *this = *this - _Years; + return *this; + } + + constexpr year_month_day::year_month_day(const year_month_day_last& _Ymdl) noexcept + : _Year{_Ymdl.year()}, _Month{_Ymdl.month()}, _Day{_Ymdl.day()} {} + + class year_month_weekday { + public: + year_month_weekday() = default; + constexpr year_month_weekday(const year& _Year_, const month& _Month_, const weekday_indexed& _Wdi) noexcept + : _Year{_Year_}, _Month{_Month_}, _Weekday_index{_Wdi} {} + constexpr year_month_weekday(const sys_days& _Sys_days) noexcept + : year_month_weekday{_Ymwd_from_days(_Sys_days.time_since_epoch())} {} + constexpr explicit year_month_weekday(const local_days& _Local_days) noexcept + : year_month_weekday{_Ymwd_from_days(_Local_days.time_since_epoch())} {} + + template + constexpr year_month_weekday& operator+=(const months& _Months) noexcept; + template + constexpr year_month_weekday& operator-=(const months& _Months) noexcept; + constexpr year_month_weekday& operator+=(const years& _Years) noexcept; + constexpr year_month_weekday& operator-=(const years& _Years) noexcept; + + _NODISCARD constexpr year year() const noexcept { + return _Year; + } + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + _NODISCARD constexpr weekday weekday() const noexcept { + return _Weekday_index.weekday(); + } + _NODISCARD constexpr unsigned int index() const noexcept { + return _Weekday_index.index(); + } + _NODISCARD constexpr weekday_indexed weekday_indexed() const noexcept { + return _Weekday_index; + } + + _NODISCARD constexpr operator sys_days() const noexcept { + const sys_days _First = year_month_day{_Year, _Month, day{1}}; + const days _Diff = weekday() - _CHRONO weekday{_First}; + const days _Days = _Diff + days{(static_cast(index()) - 1) * 7}; + return _First + _Days; + } + _NODISCARD constexpr explicit operator local_days() const noexcept { + return local_days{static_cast(*this).time_since_epoch()}; + } + _NODISCARD constexpr bool ok() const noexcept { + if (!_Year.ok() || !_Month.ok() || !_Weekday_index.ok()) { + return false; + } + + if (_Weekday_index.index() <= 4) { + return true; + } + + // As index() == 5 is not always valid + // Determine the date of the first weekday and check if + days{28} is <= last day of the month + const sys_days _First_of_month = year_month_day{_Year, _Month, day{1}}; + const days _First_weekday = weekday() - _CHRONO weekday{_First_of_month} + days{1}; + const days _Last = _First_weekday + days{28}; + return static_cast(_Last.count()) <= static_cast(_Last_day(_Year, _Month)); + } + + private: + _CHRONO year _Year; + _CHRONO month _Month; + _CHRONO weekday_indexed _Weekday_index; + + _NODISCARD static constexpr year_month_weekday _Ymwd_from_days(days _Dp) noexcept { + const _CHRONO year_month_day _Ymd = sys_days{_Dp}; + const _CHRONO weekday _Wd = sys_days{_Dp}; + const auto _Idx = ((static_cast(_Ymd.day()) - 1) / 7) + 1; + return {_Ymd.year(), _Ymd.month(), _Wd[_Idx]}; + } + }; + + _NODISCARD constexpr bool operator==(const year_month_weekday& _Left, const year_month_weekday& _Right) noexcept { + return _Left.year() == _Right.year() && _Left.month() == _Right.month() + && _Left.weekday_indexed() == _Right.weekday_indexed(); + } + + template + _NODISCARD constexpr year_month_weekday operator+(const year_month_weekday& _Left, const months& _Right) noexcept { + const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right; + return {_Ym.year(), _Ym.month(), _Left.weekday_indexed()}; + } + template + _NODISCARD constexpr year_month_weekday operator+(const months& _Left, const year_month_weekday& _Right) noexcept { + return _Right + _Left; + } + + template + _NODISCARD constexpr year_month_weekday operator-(const year_month_weekday& _Left, const months& _Right) noexcept { + return _Left + -_Right; + } + + _NODISCARD constexpr year_month_weekday operator+(const year_month_weekday& _Left, const years& _Right) noexcept { + return year_month_weekday{_Left.year() + _Right, _Left.month(), _Left.weekday_indexed()}; + } + _NODISCARD constexpr year_month_weekday operator+(const years& _Left, const year_month_weekday& _Right) noexcept { + return _Right + _Left; + } + + _NODISCARD constexpr year_month_weekday operator-(const year_month_weekday& _Left, const years& _Right) noexcept { + return _Left + -_Right; + } + + template + constexpr year_month_weekday& year_month_weekday::operator+=(const months& _Months) noexcept { + *this = *this + _Months; + return *this; + } + template + constexpr year_month_weekday& year_month_weekday::operator-=(const months& _Months) noexcept { + *this = *this - _Months; + return *this; + } + constexpr year_month_weekday& year_month_weekday::operator+=(const years& _Years) noexcept { + *this = *this + _Years; + return *this; + } + constexpr year_month_weekday& year_month_weekday::operator-=(const years& _Years) noexcept { + *this = *this - _Years; + return *this; + } + + class year_month_weekday_last { + public: + constexpr year_month_weekday_last(const year& _Year_, const month& _Month_, const weekday_last& _Wdl) noexcept + : _Year{_Year_}, _Month{_Month_}, _Weekday_last{_Wdl} {} + + template + constexpr year_month_weekday_last& operator+=(const months& _Months) noexcept; + template + constexpr year_month_weekday_last& operator-=(const months& _Months) noexcept; + constexpr year_month_weekday_last& operator+=(const years& _Years) noexcept; + constexpr year_month_weekday_last& operator-=(const years& _Years) noexcept; + + _NODISCARD constexpr year year() const noexcept { + return _Year; + } + _NODISCARD constexpr month month() const noexcept { + return _Month; + } + _NODISCARD constexpr weekday weekday() const noexcept { + return _Weekday_last.weekday(); + } + + _NODISCARD constexpr weekday_last weekday_last() const noexcept { + return _Weekday_last; + } + + _NODISCARD constexpr operator sys_days() const noexcept { + const sys_days _Last = year_month_day_last{_Year, month_day_last{_Month}}; + const auto _Diff = _CHRONO weekday{_Last} - weekday(); + return _Last - _Diff; + } + _NODISCARD constexpr explicit operator local_days() const noexcept { + return local_days{static_cast(*this).time_since_epoch()}; + } + _NODISCARD constexpr bool ok() const noexcept { + return _Year.ok() && _Month.ok() && _Weekday_last.ok(); + } + + private: + _CHRONO year _Year; + _CHRONO month _Month; + _CHRONO weekday_last _Weekday_last; + }; + + _NODISCARD constexpr bool operator==( + const year_month_weekday_last& _Left, const year_month_weekday_last& _Right) noexcept { + return _Left.year() == _Right.year() && _Left.month() == _Right.month() + && _Left.weekday_last() == _Right.weekday_last(); + } + + template + _NODISCARD constexpr year_month_weekday_last operator+( + const year_month_weekday_last& _Left, const months& _Right) noexcept { + const auto _Ym = year_month{_Left.year(), _Left.month()} + _Right; + return {_Ym.year(), _Ym.month(), _Left.weekday_last()}; + } + template + _NODISCARD constexpr year_month_weekday_last operator+( + const months& _Left, const year_month_weekday_last& _Right) noexcept { + return _Right + _Left; + } + + template + _NODISCARD constexpr year_month_weekday_last operator-( + const year_month_weekday_last& _Left, const months& _Right) noexcept { + return _Left + -_Right; + } + + _NODISCARD constexpr year_month_weekday_last operator+( + const year_month_weekday_last& _Left, const years& _Right) noexcept { + return {_Left.year() + _Right, _Left.month(), _Left.weekday_last()}; + } + _NODISCARD constexpr year_month_weekday_last operator+( + const years& _Left, const year_month_weekday_last& _Right) noexcept { + return _Right + _Left; + } + + _NODISCARD constexpr year_month_weekday_last operator-( + const year_month_weekday_last& _Left, const years& _Right) noexcept { + return _Left + -_Right; + } + + template + constexpr year_month_weekday_last& year_month_weekday_last::operator+=(const months& _Months) noexcept { + *this = *this + _Months; + return *this; + } + template + constexpr year_month_weekday_last& year_month_weekday_last::operator-=(const months& _Months) noexcept { + *this = *this - _Months; + return *this; + } + constexpr year_month_weekday_last& year_month_weekday_last::operator+=(const years& _Years) noexcept { + *this = *this + _Years; + return *this; + } + constexpr year_month_weekday_last& year_month_weekday_last::operator-=(const years& _Years) noexcept { + *this = *this - _Years; + return *this; + } + + // Civil calendar conventional syntax operators + _NODISCARD constexpr year_month operator/(const year& _Year, const month& _Month) noexcept { + return {_Year, _Month}; + } + _NODISCARD constexpr year_month operator/(const year& _Year, int _Month) noexcept { + return _Year / month{static_cast(_Month)}; + } + _NODISCARD constexpr month_day operator/(const month& _Month, const day& _Day) noexcept { + return {_Month, _Day}; + } + _NODISCARD constexpr month_day operator/(const month& _Month, int _Day) noexcept { + return _Month / day{static_cast(_Day)}; + } + _NODISCARD constexpr month_day operator/(int _Month, const day& _Day) noexcept { + return month{static_cast(_Month)} / _Day; + } + _NODISCARD constexpr month_day operator/(const day& _Day, const month& _Month) noexcept { + return _Month / _Day; + } + _NODISCARD constexpr month_day operator/(const day& _Day, int _Month) noexcept { + return month{static_cast(_Month)} / _Day; + } + _NODISCARD constexpr month_day_last operator/(const month& _Month, last_spec) noexcept { + return month_day_last{_Month}; + } + _NODISCARD constexpr month_day_last operator/(int _Month, last_spec) noexcept { + return month{static_cast(_Month)} / last; + } + _NODISCARD constexpr month_day_last operator/(last_spec, const month& _Month) noexcept { + return _Month / last; + } + _NODISCARD constexpr month_day_last operator/(last_spec, int _Month) noexcept { + return month{static_cast(_Month)} / last; + } + _NODISCARD constexpr month_weekday operator/(const month& _Month, const weekday_indexed& _Wdi) noexcept { + return {_Month, _Wdi}; + } + _NODISCARD constexpr month_weekday operator/(int _Month, const weekday_indexed& _Wdi) noexcept { + return month{static_cast(_Month)} / _Wdi; + } + _NODISCARD constexpr month_weekday operator/(const weekday_indexed& _Wdi, const month& _Month) noexcept { + return _Month / _Wdi; + } + _NODISCARD constexpr month_weekday operator/(const weekday_indexed& _Wdi, int _Month) noexcept { + return month{static_cast(_Month)} / _Wdi; + } + _NODISCARD constexpr month_weekday_last operator/(const month& _Month, const weekday_last& _Wdl) noexcept { + return {_Month, _Wdl}; + } + _NODISCARD constexpr month_weekday_last operator/(int _Month, const weekday_last& _Wdl) noexcept { + return month{static_cast(_Month)} / _Wdl; + } + _NODISCARD constexpr month_weekday_last operator/(const weekday_last& _Wdl, const month& _Month) noexcept { + return _Month / _Wdl; + } + _NODISCARD constexpr month_weekday_last operator/(const weekday_last& _Wdl, int _Month) noexcept { + return month{static_cast(_Month)} / _Wdl; + } + _NODISCARD constexpr year_month_day operator/(const year_month& _Ym, const day& _Day) noexcept { + return {_Ym.year(), _Ym.month(), _Day}; + } + _NODISCARD constexpr year_month_day operator/(const year_month& _Ym, int _Day) noexcept { + return _Ym / day{static_cast(_Day)}; + } + _NODISCARD constexpr year_month_day operator/(const year& _Year, const month_day& _Md) noexcept { + return _Year / _Md.month() / _Md.day(); + } + _NODISCARD constexpr year_month_day operator/(int _Year, const month_day& _Md) noexcept { + return year{_Year} / _Md.month() / _Md.day(); + } + _NODISCARD constexpr year_month_day operator/(const month_day& _Md, const year& _Year) noexcept { + return _Year / _Md.month() / _Md.day(); + } + _NODISCARD constexpr year_month_day operator/(const month_day& _Md, int _Year) noexcept { + return year{_Year} / _Md.month() / _Md.day(); + } + _NODISCARD constexpr year_month_day_last operator/(const year_month& _Ym, last_spec) noexcept { + return {_Ym.year(), month_day_last{_Ym.month()}}; + } + _NODISCARD constexpr year_month_day_last operator/(const year& _Year, const month_day_last& _Mdl) noexcept { + return {_Year, _Mdl}; + } + _NODISCARD constexpr year_month_day_last operator/(int _Year, const month_day_last& _Mdl) noexcept { + return year{_Year} / _Mdl; + } + _NODISCARD constexpr year_month_day_last operator/(const month_day_last& _Mdl, const year& _Year) noexcept { + return _Year / _Mdl; + } + _NODISCARD constexpr year_month_day_last operator/(const month_day_last& _Mdl, int _Year) noexcept { + return year{_Year} / _Mdl; + } + _NODISCARD constexpr year_month_weekday operator/(const year_month& _Ym, const weekday_indexed& _Wdi) noexcept { + return year_month_weekday{_Ym.year(), _Ym.month(), _Wdi}; + } + _NODISCARD constexpr year_month_weekday operator/(const year& _Year, const month_weekday& _Mwd) noexcept { + return year_month_weekday{_Year, _Mwd.month(), _Mwd.weekday_indexed()}; + } + _NODISCARD constexpr year_month_weekday operator/(int _Year, const month_weekday& _Mwd) noexcept { + return year{_Year} / _Mwd; + } + _NODISCARD constexpr year_month_weekday operator/(const month_weekday& _Mwd, const year& _Year) noexcept { + return _Year / _Mwd; + } + _NODISCARD constexpr year_month_weekday operator/(const month_weekday& _Mwd, int _Year) noexcept { + return year{_Year} / _Mwd; + } + _NODISCARD constexpr year_month_weekday_last operator/(const year_month& _Ym, const weekday_last& _Wdl) noexcept { + return {_Ym.year(), _Ym.month(), _Wdl}; + } + _NODISCARD constexpr year_month_weekday_last operator/( + const year& _Year, const month_weekday_last& _Mwdl) noexcept { + return {_Year, _Mwdl.month(), _Mwdl.weekday_last()}; + } + _NODISCARD constexpr year_month_weekday_last operator/(int _Year, const month_weekday_last& _Mwdl) noexcept { + return year{_Year} / _Mwdl; + } + _NODISCARD constexpr year_month_weekday_last operator/( + const month_weekday_last& _Mwdl, const year& _Year) noexcept { + return _Year / _Mwdl; + } + _NODISCARD constexpr year_month_weekday_last operator/(const month_weekday_last& _Mwdl, int _Year) noexcept { + return year{_Year} / _Mwdl; + } + + // Calendrical constants + inline constexpr weekday Sunday{0}; + inline constexpr weekday Monday{1}; + inline constexpr weekday Tuesday{2}; + inline constexpr weekday Wednesday{3}; + inline constexpr weekday Thursday{4}; + inline constexpr weekday Friday{5}; + inline constexpr weekday Saturday{6}; + + inline constexpr month January{1}; + inline constexpr month February{2}; + inline constexpr month March{3}; + inline constexpr month April{4}; + inline constexpr month May{5}; + inline constexpr month June{6}; + inline constexpr month July{7}; + inline constexpr month August{8}; + inline constexpr month September{9}; + inline constexpr month October{10}; + inline constexpr month November{11}; + inline constexpr month December{12}; + + _NODISCARD constexpr intmax_t _Pow10(const unsigned int _Exp) { + intmax_t _Result = 1; + for (unsigned int _Ix = 0; _Ix < _Exp; ++_Ix) { + _Result *= 10; + } + return _Result; + } + + template + requires _Is_duration_v<_Duration> class hh_mm_ss { + public: + static constexpr unsigned int fractional_width = [] { + auto _Num = _Duration::period::num; + constexpr auto _Den = _Duration::period::den; + // Returns the number of fractional digits of _Num / _Den in the range [0, 18]. + // If it can't be represented, 6 is returned. + // Example: _Fractional_width(1, 8) would return 3 for 0.125. + _STL_ASSERT(_Num > 0 && _Den > 0, "Numerator and denominator can't be less than 1."); + unsigned int _Result = 0; + for (; _Num % _Den != 0 && _Result < 19; _Num = _Num % _Den * 10, ++_Result) { + } + return _Result == 19 ? 6 : _Result; + }(); + using precision = + duration, ratio<1, _Pow10(fractional_width)>>; + + constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {} + // clang-format off + constexpr explicit hh_mm_ss(_Duration _Dur) + : _Is_neg{_Dur < _Duration::zero()}, + _Hours{_CHRONO duration_cast<_CHRONO hours>(_CHRONO abs(_Dur))}, + _Mins{_CHRONO duration_cast<_CHRONO minutes>(_CHRONO abs(_Dur) - hours())}, + _Secs{_CHRONO duration_cast<_CHRONO seconds>(_CHRONO abs(_Dur) - hours() - minutes())} { + // clang-format on + if constexpr (treat_as_floating_point_v) { + _Sub_secs = _CHRONO abs(_Dur) - hours() - minutes() - seconds(); + } else { + _Sub_secs = _CHRONO duration_cast(_CHRONO abs(_Dur) - hours() - minutes() - seconds()); + } + } + + _NODISCARD constexpr bool is_negative() const noexcept { + return _Is_neg; + } + _NODISCARD constexpr hours hours() const noexcept { + return _Hours; + } + _NODISCARD constexpr minutes minutes() const noexcept { + return _Mins; + } + _NODISCARD constexpr seconds seconds() const noexcept { + return _Secs; + } + _NODISCARD constexpr precision subseconds() const noexcept { + return _Sub_secs; + } + + _NODISCARD constexpr explicit operator precision() const noexcept { + return to_duration(); + } + _NODISCARD constexpr precision to_duration() const noexcept { + const auto _Dur = _Hours + _Mins + _Secs + _Sub_secs; + return _Is_neg ? -_Dur : _Dur; + } + + private: + bool _Is_neg; + _CHRONO hours _Hours; + _CHRONO minutes _Mins; + _CHRONO seconds _Secs; + precision _Sub_secs; + }; + + _NODISCARD constexpr bool is_am(const hours& _Hours) noexcept { + return _Hours >= hours{0} && _Hours <= hours{11}; + } + _NODISCARD constexpr bool is_pm(const hours& _Hours) noexcept { + return _Hours >= hours{12} && _Hours <= hours{23}; + } + + _NODISCARD constexpr hours make12(const hours& _Hours) noexcept { + const auto _H_count{_Hours.count()}; + auto _Ret{_H_count == 0 ? 12 : _H_count}; + if (_Ret > 12) { + _Ret -= 12; + } + + return hours{_Ret}; + } + _NODISCARD constexpr hours make24(const hours& _Hours, bool _Is_pm) noexcept { + const auto _H_count{_Hours.count()}; + auto _Ret{_H_count == 12 ? 0 : _H_count}; + if (_Is_pm) { + _Ret += 12; + } + + return hours{_Ret}; + } #endif // _HAS_CXX20 } // namespace chrono // HELPERS template -_NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const chrono::duration<_Rep, _Period>& _Rel_time) noexcept( +_NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const _CHRONO duration<_Rep, _Period>& _Rel_time) noexcept( is_arithmetic_v<_Rep>) { // Convert duration to xtime, maximum 10 days from now, returns whether clamping occurred. // If clamped, timeouts will be transformed into spurious non-timeout wakes, due to ABI restrictions where // the other side of the DLL boundary overflows int32_t milliseconds. // Every function calling this one is TRANSITION, ABI - constexpr chrono::nanoseconds _Ten_days{chrono::hours{24} * 10}; - constexpr chrono::duration _Ten_days_d{_Ten_days}; - chrono::nanoseconds _Tx0 = chrono::system_clock::now().time_since_epoch(); + constexpr _CHRONO nanoseconds _Ten_days{_CHRONO hours{24} * 10}; + constexpr _CHRONO duration _Ten_days_d{_Ten_days}; + _CHRONO nanoseconds _Tx0 = _CHRONO system_clock::now().time_since_epoch(); const bool _Clamped = _Ten_days_d < _Rel_time; if (_Clamped) { _Tx0 += _Ten_days; } else { - _Tx0 += chrono::duration_cast(_Rel_time); + _Tx0 += _CHRONO duration_cast<_CHRONO nanoseconds>(_Rel_time); } - const auto _Whole_seconds = chrono::duration_cast(_Tx0); + const auto _Whole_seconds = _CHRONO duration_cast<_CHRONO seconds>(_Tx0); _Xt.sec = _Whole_seconds.count(); _Tx0 -= _Whole_seconds; _Xt.nsec = static_cast(_Tx0.count()); @@ -730,58 +2240,66 @@ _NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const chrono::duratio // duration LITERALS inline namespace literals { inline namespace chrono_literals { - _NODISCARD constexpr chrono::hours operator"" h(unsigned long long _Val) noexcept /* strengthened */ { - return chrono::hours(_Val); + _NODISCARD constexpr _CHRONO hours operator"" h(unsigned long long _Val) noexcept /* strengthened */ { + return _CHRONO hours(_Val); } - _NODISCARD constexpr chrono::duration> operator"" h(long double _Val) noexcept + _NODISCARD constexpr _CHRONO duration> operator"" h(long double _Val) noexcept /* strengthened */ { - return chrono::duration>(_Val); + return _CHRONO duration>(_Val); } - _NODISCARD constexpr chrono::minutes(operator"" min)(unsigned long long _Val) noexcept /* strengthened */ { - return chrono::minutes(_Val); + _NODISCARD constexpr _CHRONO minutes(operator"" min)(unsigned long long _Val) noexcept /* strengthened */ { + return _CHRONO minutes(_Val); } - _NODISCARD constexpr chrono::duration>(operator"" min)(long double _Val) noexcept + _NODISCARD constexpr _CHRONO duration>(operator"" min)(long double _Val) noexcept /* strengthened */ { - return chrono::duration>(_Val); + return _CHRONO duration>(_Val); } - _NODISCARD constexpr chrono::seconds operator"" s(unsigned long long _Val) noexcept /* strengthened */ { - return chrono::seconds(_Val); + _NODISCARD constexpr _CHRONO seconds operator"" s(unsigned long long _Val) noexcept /* strengthened */ { + return _CHRONO seconds(_Val); } - _NODISCARD constexpr chrono::duration operator"" s(long double _Val) noexcept /* strengthened */ { - return chrono::duration(_Val); + _NODISCARD constexpr _CHRONO duration operator"" s(long double _Val) noexcept /* strengthened */ { + return _CHRONO duration(_Val); } - _NODISCARD constexpr chrono::milliseconds operator"" ms(unsigned long long _Val) noexcept /* strengthened */ { - return chrono::milliseconds(_Val); + _NODISCARD constexpr _CHRONO milliseconds operator"" ms(unsigned long long _Val) noexcept /* strengthened */ { + return _CHRONO milliseconds(_Val); } - _NODISCARD constexpr chrono::duration operator"" ms(long double _Val) noexcept + _NODISCARD constexpr _CHRONO duration operator"" ms(long double _Val) noexcept /* strengthened */ { - return chrono::duration(_Val); + return _CHRONO duration(_Val); } - _NODISCARD constexpr chrono::microseconds operator"" us(unsigned long long _Val) noexcept /* strengthened */ { - return chrono::microseconds(_Val); + _NODISCARD constexpr _CHRONO microseconds operator"" us(unsigned long long _Val) noexcept /* strengthened */ { + return _CHRONO microseconds(_Val); } - _NODISCARD constexpr chrono::duration operator"" us(long double _Val) noexcept + _NODISCARD constexpr _CHRONO duration operator"" us(long double _Val) noexcept /* strengthened */ { - return chrono::duration(_Val); + return _CHRONO duration(_Val); } - _NODISCARD constexpr chrono::nanoseconds operator"" ns(unsigned long long _Val) noexcept /* strengthened */ { - return chrono::nanoseconds(_Val); + _NODISCARD constexpr _CHRONO nanoseconds operator"" ns(unsigned long long _Val) noexcept /* strengthened */ { + return _CHRONO nanoseconds(_Val); } - _NODISCARD constexpr chrono::duration operator"" ns(long double _Val) noexcept + _NODISCARD constexpr _CHRONO duration operator"" ns(long double _Val) noexcept /* strengthened */ { - return chrono::duration(_Val); + return _CHRONO duration(_Val); } +#if _HAS_CXX20 + _NODISCARD constexpr _CHRONO day operator"" d(unsigned long long _Day) noexcept { + return _CHRONO day{static_cast(_Day)}; + } + _NODISCARD constexpr _CHRONO year operator"" y(unsigned long long _Year) noexcept { + return _CHRONO year{static_cast(_Year)}; + } +#endif // _HAS_CXX20 } // namespace chrono_literals } // namespace literals diff --git a/stl/inc/cmath b/stl/inc/cmath index 46eecf2faff..358fc459b04 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -902,50 +902,50 @@ _STD_END #if _HAS_CXX17 _EXTERN_C -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_laguerre(unsigned int, unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_laguerref(unsigned int, unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_legendre(unsigned int, unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_legendref(unsigned int, unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_beta(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_betaf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_1(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_1f(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_2(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_2f(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_3(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_3f(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_i(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_if(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_j(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_jf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_k(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_kf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_neumann(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_neumannf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_1(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_1f(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_2(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_2f(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_3(double, double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_3f(float, float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_expint(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_expintf(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hermite(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hermitef(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_laguerre(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_laguerref(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_legendre(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_legendref(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_riemann_zeta(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_riemann_zetaf(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_bessel(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_besself(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_legendre(unsigned int, unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_legendref(unsigned int, unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_neumann(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_neumannf(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hypot3(double, double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hypot3f(float, float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_laguerre(unsigned int, unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_laguerref(unsigned int, unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_legendre(unsigned int, unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_legendref(unsigned int, unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_beta(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_betaf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_1(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_1f(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_2(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_2f(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_3(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_3f(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_i(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_if(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_j(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_jf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_k(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_kf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_neumann(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_neumannf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_1(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_1f(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_2(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_2f(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_3(double, double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_3f(float, float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_expint(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_expintf(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hermite(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hermitef(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_laguerre(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_laguerref(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_legendre(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_legendref(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_riemann_zeta(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_riemann_zetaf(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_bessel(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_besself(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_legendre(unsigned int, unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_legendref(unsigned int, unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_neumann(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_neumannf(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hypot3(double, double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hypot3f(float, float, float) noexcept; _END_EXTERN_C _STD_BEGIN diff --git a/stl/inc/compare b/stl/inc/compare index 46bdfd0371f..ab47a879606 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -342,6 +342,35 @@ struct compare_three_way { }; // clang-format on +// STRUCT _Synth_three_way +struct _Synth_three_way { + // clang-format off + template + _NODISCARD constexpr auto operator()(const _Ty1& _Left, const _Ty2& _Right) const + requires requires { + { _Left < _Right } -> _Boolean_testable; + { _Right < _Left } -> _Boolean_testable; + } + // clang-format on + { + if constexpr (three_way_comparable_with<_Ty1, _Ty2>) { + return _Left <=> _Right; + } else { + if (_Left < _Right) { + return weak_ordering::less; + } else if (_Right < _Left) { + return weak_ordering::greater; + } else { + return weak_ordering::equivalent; + } + } + } +}; + +// ALIAS TEMPLATE _Synth_three_way_result +template +using _Synth_three_way_result = decltype(_Synth_three_way{}(_STD declval<_Ty1&>(), _STD declval<_Ty2&>())); + // Note: The following CPOs are passing arguments as lvalues; see GH-1374. // CUSTOMIZATION POINT OBJECT strong_order diff --git a/stl/inc/complex b/stl/inc/complex index e0aff1ed4ee..5e935cf52d0 100644 --- a/stl/inc/complex +++ b/stl/inc/complex @@ -1514,12 +1514,15 @@ _NODISCARD constexpr bool operator==(const complex<_Ty>& _Left, const _Ty& _Righ return _Left.real() == _Right && _Left.imag() == 0; } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator==(const _Ty& _Left, const complex<_Ty>& _Right) { return _Left == _Right.real() && 0 == _Right.imag(); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE operator!= +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const complex<_Ty>& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); @@ -1534,6 +1537,7 @@ template _NODISCARD constexpr bool operator!=(const _Ty& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE imag template diff --git a/stl/inc/condition_variable b/stl/inc/condition_variable index 42d02aa8db7..3c17c8227a3 100644 --- a/stl/inc/condition_variable +++ b/stl/inc/condition_variable @@ -92,12 +92,18 @@ public: template cv_status wait_until(_Lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time) { // wait until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return wait_for(_Lck, _Abs_time - _Clock::now()); } template bool wait_until(_Lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time, _Predicate _Pred) { // wait for signal with timeout and check predicate +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 while (!_Pred()) { if (wait_until(_Lck, _Abs_time) == cv_status::timeout) { return _Pred(); @@ -193,6 +199,7 @@ public: template bool wait_until( _Lock& _Lck, stop_token _Stoken, const chrono::time_point<_Clock, _Duration>& _Abs_time, _Predicate _Pred) { + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); stop_callback<_Cv_any_notify_all> _Cb{_Stoken, this}; for (;;) { if (_Pred()) { diff --git a/stl/inc/coroutine b/stl/inc/coroutine index ad2c2f30dd8..a9f8f729737 100644 --- a/stl/inc/coroutine +++ b/stl/inc/coroutine @@ -11,16 +11,19 @@ #ifdef _RESUMABLE_FUNCTIONS_SUPPORTED #pragma message("The contents of are not available with /await.") -#pragma message("Remove /await for standard coroutines or use for legacy /await support.") +#pragma message("Remove /await or use /await:strict for standard coroutines.") +#pragma message("Use for legacy /await support.") #else // ^^^ /await ^^^ / vvv no /await vvv #ifndef __cpp_lib_coroutine -#pragma message("The contents of are available only with C++20 or later.") -#else // ^^^ __cpp_lib_coroutine not defined / __cpp_lib_coroutine defined vvv +#pragma message("The contents of are available only with C++20 or later or /await:strict.") +#else // ^^^ is not available / is available vvv #ifndef _ALLOW_COROUTINE_ABI_MISMATCH #pragma detect_mismatch("_COROUTINE_ABI", "2") #endif // _ALLOW_COROUTINE_ABI_MISMATCH +#if _HAS_CXX20 #include +#endif // _HAS_CXX20 #include #pragma pack(push, _CRT_PACKING) @@ -156,6 +159,7 @@ _NODISCARD constexpr bool operator==(const coroutine_handle<> _Left, const corou return _Left.address() == _Right.address(); } +#if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>( const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { #ifdef __cpp_lib_concepts @@ -164,6 +168,27 @@ _NODISCARD constexpr strong_ordering operator<=>( return _Left.address() <=> _Right.address(); #endif // __cpp_lib_concepts } +#else // ^^^ <=> exists / <=> does not exist vvv +_NODISCARD constexpr bool operator!=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left == _Right); +} + +_NODISCARD constexpr bool operator<(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return less{}(_Left.address(), _Right.address()); +} + +_NODISCARD constexpr bool operator>(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return _Right < _Left; +} + +_NODISCARD constexpr bool operator<=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left > _Right); +} + +_NODISCARD constexpr bool operator>=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left < _Right); +} +#endif // _HAS_CXX20 template struct hash> { @@ -246,7 +271,7 @@ _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // __cpp_lib_coroutine +#endif // is available #endif // _RESUMABLE_FUNCTIONS_SUPPORTED #endif // _STL_COMPILER_PREPROCESSOR #endif // _COROUTINE_ diff --git a/stl/inc/deque b/stl/inc/deque index cb0f3258fd3..9018290d9c6 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -107,6 +107,11 @@ public: return _Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_unchecked_const_iterator& _Right) const noexcept { + return _Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -126,6 +131,7 @@ public: _NODISCARD bool operator>=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 const _Container_base12* _Getcont() const noexcept { // get container pointer return _Mycont; @@ -345,6 +351,12 @@ public: return this->_Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_const_iterator& _Right) const noexcept { + _Compat(_Right); + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -365,6 +377,7 @@ public: _NODISCARD bool operator>=(const _Deque_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 void _Compat(const _Deque_const_iterator& _Right) const noexcept { // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL == 0 @@ -1582,11 +1595,20 @@ _NODISCARD bool operator==(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Al && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -1607,6 +1629,7 @@ template _NODISCARD bool operator>=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/execution b/stl/inc/execution index b1d73575657..7953f238a2a 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -2665,24 +2665,23 @@ bool _Process_sort_work_item(const _RanIt _Basis, _Pr _Pred, _Sort_work_item<_Ra // the return value is false // _Wi's range is completely sorted // _Right_fork_wi is unmodified - using _Diff = _Iter_diff_t<_RanIt>; - constexpr auto _Diffsort_max = static_cast<_Diff>(_ISORT_MAX); - const auto _Size = _Wi._Size; - const auto _First = _Basis + _Wi._Offset; - const auto _Last = _First + _Size; - const auto _Ideal = _Wi._Ideal; - if (_Size <= _Diffsort_max) { + const auto _Size = _Wi._Size; + const auto _First = _Basis + _Wi._Offset; + const auto _Last = _First + _Size; + const auto _Ideal = _Wi._Ideal; + if (_Size <= _Isort_max<_RanIt>) { _Insertion_sort_unchecked(_First, _Last, _Pred); _Work_complete += _Size; return false; } if (0 < _Ideal) { // divide and conquer by partitioning (quicksort) - const auto _Mid = _Partition_by_median_guess_unchecked(_First, _Last, _Pred); - const auto _New_ideal = static_cast<_Diff>(_Ideal / 2 + _Ideal / 4); // allow 1.5 log2(N) divisions - _Wi._Size = _Mid.first - _First; - _Wi._Ideal = _New_ideal; - _Right_fork_wi = {_Mid.second - _Basis, _Last - _Mid.second, _New_ideal}; + const auto _Mid = _Partition_by_median_guess_unchecked(_First, _Last, _Pred); + const auto _New_ideal = + static_cast<_Iter_diff_t<_RanIt>>(_Ideal / 2 + _Ideal / 4); // allow 1.5 log2(N) divisions + _Wi._Size = _Mid.first - _First; + _Wi._Ideal = _New_ideal; + _Right_fork_wi = {_Mid.second - _Basis, _Last - _Mid.second, _New_ideal}; _Work_complete += _Mid.second - _Mid.first; return true; } diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 0f9bec6e02c..05341d65c4c 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -26,6 +26,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1399,6 +1403,11 @@ namespace filesystem { return _Left.compare(_Right) == 0; } +#if _HAS_CXX20 + _NODISCARD friend strong_ordering operator<=>(const path& _Left, const path& _Right) noexcept { + return _Left.compare(_Right) <=> 0; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD friend bool operator!=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) != 0; } @@ -1418,6 +1427,7 @@ namespace filesystem { _NODISCARD friend bool operator>=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) >= 0; } +#endif // !_HAS_CXX20 _NODISCARD friend path operator/(const path& _Left, const path& _Right) { // append a pair of paths together return path(_Left) /= _Right; @@ -1593,9 +1603,11 @@ namespace filesystem { return _Lhs._Position == _Rhs._Position; } +#if !_HAS_CXX20 _NODISCARD friend bool operator!=(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { return _Lhs._Position != _Rhs._Position; } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend void _Verify_range(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { @@ -1967,6 +1979,12 @@ namespace filesystem { return _Myperms; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const file_status& _Lhs, const file_status& _Rhs) noexcept { + return _Lhs._Myftype == _Rhs._Myftype && _Lhs._Myperms == _Rhs._Myperms; + } +#endif // _HAS_CXX20 + void _Refresh(const __std_win_error _Error, const __std_fs_stats& _Stats) noexcept { if (_Error == __std_win_error::_Success) { const auto _Attrs = _Stats._Attributes; @@ -2445,18 +2463,23 @@ namespace filesystem { return _Result._Status; } - _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { - return _Path < _Rhs._Path; - } - _NODISCARD bool operator==(const directory_entry& _Rhs) const noexcept { return _Path == _Rhs._Path; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const directory_entry& _Rhs) const noexcept { + return _Path <=> _Rhs._Path; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const directory_entry& _Rhs) const noexcept { return _Path != _Rhs._Path; } + _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { + return _Path < _Rhs._Path; + } + _NODISCARD bool operator<=(const directory_entry& _Rhs) const noexcept { return _Path <= _Rhs._Path; } @@ -2468,6 +2491,15 @@ namespace filesystem { _NODISCARD bool operator>=(const directory_entry& _Rhs) const noexcept { return _Path >= _Rhs._Path; } +#endif // !_HAS_CXX20 + + // [fs.dir.entry.io], inserter + template + friend _STD basic_ostream<_Elem, _Traits>& operator<<( // TRANSITION, VSO-570323 + _STD basic_ostream<_Elem, _Traits>& _Ostr, const directory_entry& _Entry) { // TRANSITION, VSO-570323 + // insert a directory_entry into a stream + return _Ostr << _Entry.path(); + } private: void _Refresh(const __std_fs_find_data& _Data) noexcept { @@ -2698,9 +2730,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const directory_iterator& _Rhs) const noexcept /* strengthened */ { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); @@ -2947,9 +2981,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const recursive_directory_iterator& _Rhs) const noexcept { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); @@ -3641,6 +3677,10 @@ namespace filesystem { uintmax_t capacity; uintmax_t free; uintmax_t available; + +#if _HAS_CXX20 + _NODISCARD friend constexpr bool operator==(const space_info&, const space_info&) noexcept = default; +#endif // _HAS_CXX20 }; _NODISCARD inline space_info space(const path& _Target) { diff --git a/stl/inc/forward_list b/stl/inc/forward_list index 986f9e3a984..6505741e507 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -63,17 +63,21 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return _Ptr == nullptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return _Ptr != nullptr; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -161,9 +165,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Flist_const_iterator& _First, const _Flist_const_iterator& _Last) noexcept { @@ -814,6 +820,10 @@ public: return {}; } + _Unchecked_const_iterator _Unchecked_end_iter() const noexcept { + return _Unchecked_const_iterator(nullptr, nullptr); + } + iterator _Make_iter(_Nodeptr _Where) const noexcept { return iterator(_Where, _STD addressof(_Mypair._Myval2)); } @@ -1518,17 +1528,29 @@ void swap(forward_list<_Ty, _Alloc>& _Left, forward_list<_Ty, _Alloc>& _Right) n template _NODISCARD bool operator==(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD equal(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD equal( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>( + const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } template @@ -1545,6 +1567,7 @@ template _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/functional b/stl/inc/functional index d202403421d..649ab6d2a97 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1281,6 +1281,7 @@ _NODISCARD bool operator==(const function<_Fty>& _Other, nullptr_t) noexcept { return !_Other; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const function<_Fty>& _Other) noexcept { return !_Other; @@ -1295,6 +1296,7 @@ template _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { return static_cast(_Other); } +#endif // !_HAS_CXX20 // PLACEHOLDERS template diff --git a/stl/inc/future b/stl/inc/future index 2600a3cac8f..3036d53dd0b 100644 --- a/stl/inc/future +++ b/stl/inc/future @@ -770,6 +770,9 @@ public: template future_status wait_until(const chrono::time_point<_Clock, _Dur>& _Abs_time) const { // wait until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 if (!valid()) { _Throw_future_error(make_error_code(future_errc::no_state)); } @@ -874,6 +877,9 @@ class future : public _State_manager<_Ty> { using _Mybase = _State_manager<_Ty>; public: + static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>, + "T in future must meet the Cpp17Destructible requirements (N4878 [futures.unique.future]/4)."); + future() noexcept {} future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {} @@ -972,6 +978,9 @@ class shared_future : public _State_manager<_Ty> { using _Mybase = _State_manager<_Ty>; public: + static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>, + "T in shared_future must meet the Cpp17Destructible requirements (N4878 [futures.shared.future]/4)."); + shared_future() noexcept {} shared_future(const shared_future& _Other) noexcept : _Mybase(_Other) {} @@ -1139,6 +1148,9 @@ private: template class promise { // class that defines an asynchronous provider that holds a value public: + static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>, + "T in promise must meet the Cpp17Destructible requirements (N4878 [futures.promise]/1)."); + promise() : _MyPromise(new _Associated_state<_Ty>) {} template diff --git a/stl/inc/hash_map b/stl/inc/hash_map index ffb0da9f74f..d2fc7b3f49d 100644 --- a/stl/inc/hash_map +++ b/stl/inc/hash_map @@ -84,20 +84,21 @@ namespace stdext { }; template - static const _Kty& _Kfn(const pair<_Ty1, _Ty2>& _Val) noexcept { // extract key from element value + _NODISCARD static const _Kty& _Kfn(const pair<_Ty1, _Ty2>& _Val) noexcept { // extract key from element value return _Val.first; } template - static const _Ty2& _Nonkfn(const pair<_Ty1, _Ty2>& _Val) noexcept { // extract non-key from element value + _NODISCARD static const _Ty2& _Nonkfn(const pair<_Ty1, _Ty2>& _Val) noexcept { + // extract non-key from element value return _Val.second; } - float& _Get_max_bucket_size() noexcept { + _NODISCARD float& _Get_max_bucket_size() noexcept { return _Max_buckets; } - const float& _Get_max_bucket_size() const noexcept { + _NODISCARD const float& _Get_max_bucket_size() const noexcept { return _Max_buckets; } @@ -224,7 +225,7 @@ namespace stdext { return this->_Try_emplace(_Keyval).first->_Myval.second; } - mapped_type& at(const key_type& _Keyval) { + _NODISCARD mapped_type& at(const key_type& _Keyval) { const auto _Target = this->_Find_last(_Keyval, this->_Traitsobj(_Keyval)); if (_Target._Duplicate) { return _Target._Duplicate->_Myval.second; @@ -233,7 +234,7 @@ namespace stdext { _Xout_of_range("invalid hash_map key"); } - const mapped_type& at(const key_type& _Keyval) const { + _NODISCARD const mapped_type& at(const key_type& _Keyval) const { const auto _Target = this->_Find_last(_Keyval, this->_Traitsobj(_Keyval)); if (_Target._Duplicate) { return _Target._Duplicate->_Myval.second; @@ -245,27 +246,27 @@ namespace stdext { using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; - reverse_iterator rbegin() noexcept { + _NODISCARD reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); } - const_reverse_iterator rbegin() const noexcept { + _NODISCARD const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(this->end()); } - reverse_iterator rend() noexcept { + _NODISCARD reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); } - const_reverse_iterator rend() const noexcept { + _NODISCARD const_reverse_iterator rend() const noexcept { return const_reverse_iterator(this->begin()); } - const_reverse_iterator crbegin() const noexcept { + _NODISCARD const_reverse_iterator crbegin() const noexcept { return rbegin(); } - const_reverse_iterator crend() const noexcept { + _NODISCARD const_reverse_iterator crend() const noexcept { return rend(); } @@ -406,27 +407,27 @@ namespace stdext { using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; - reverse_iterator rbegin() noexcept { + _NODISCARD reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); } - const_reverse_iterator rbegin() const noexcept { + _NODISCARD const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(this->end()); } - reverse_iterator rend() noexcept { + _NODISCARD reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); } - const_reverse_iterator rend() const noexcept { + _NODISCARD const_reverse_iterator rend() const noexcept { return const_reverse_iterator(this->begin()); } - const_reverse_iterator crbegin() const noexcept { + _NODISCARD const_reverse_iterator crbegin() const noexcept { return rbegin(); } - const_reverse_iterator crend() const noexcept { + _NODISCARD const_reverse_iterator crend() const noexcept { return rend(); } diff --git a/stl/inc/hash_set b/stl/inc/hash_set index 79f04e8a922..7a3a4214318 100644 --- a/stl/inc/hash_set +++ b/stl/inc/hash_set @@ -62,20 +62,20 @@ namespace stdext { using value_compare = key_compare; - static const _Kty& _Kfn(const value_type& _Val) noexcept { + _NODISCARD static const _Kty& _Kfn(const value_type& _Val) noexcept { return _Val; } - static int _Nonkfn(const value_type&) noexcept { + _NODISCARD static int _Nonkfn(const value_type&) noexcept { // extract "non-key" from element value (for container equality) return 0; } - float& _Get_max_bucket_size() noexcept { + _NODISCARD float& _Get_max_bucket_size() noexcept { return _Max_buckets; } - const float& _Get_max_bucket_size() const noexcept { + _NODISCARD const float& _Get_max_bucket_size() const noexcept { return _Max_buckets; } @@ -181,27 +181,27 @@ namespace stdext { using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; - reverse_iterator rbegin() noexcept { + _NODISCARD reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); } - const_reverse_iterator rbegin() const noexcept { + _NODISCARD const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(this->end()); } - reverse_iterator rend() noexcept { + _NODISCARD reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); } - const_reverse_iterator rend() const noexcept { + _NODISCARD const_reverse_iterator rend() const noexcept { return const_reverse_iterator(this->begin()); } - const_reverse_iterator crbegin() const noexcept { + _NODISCARD const_reverse_iterator crbegin() const noexcept { return rbegin(); } - const_reverse_iterator crend() const noexcept { + _NODISCARD const_reverse_iterator crend() const noexcept { return rend(); } @@ -325,27 +325,27 @@ namespace stdext { using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; - reverse_iterator rbegin() noexcept { + _NODISCARD reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); } - const_reverse_iterator rbegin() const noexcept { + _NODISCARD const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(this->end()); } - reverse_iterator rend() noexcept { + _NODISCARD reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); } - const_reverse_iterator rend() const noexcept { + _NODISCARD const_reverse_iterator rend() const noexcept { return const_reverse_iterator(this->begin()); } - const_reverse_iterator crbegin() const noexcept { + _NODISCARD const_reverse_iterator crbegin() const noexcept { return rbegin(); } - const_reverse_iterator crend() const noexcept { + _NODISCARD const_reverse_iterator crend() const noexcept { return rend(); } diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index cb1ef9efeb5..d33b9abc629 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -87,6 +87,7 @@ "semaphore", "set", "shared_mutex", + "source_location", "span", "sstream", "stack", @@ -96,6 +97,7 @@ "string", "string_view", "strstream", + "syncstream", "system_error", "thread", "tuple", diff --git a/stl/inc/iosfwd b/stl/inc/iosfwd index 2eea4b96749..0aec6258a95 100644 --- a/stl/inc/iosfwd +++ b/stl/inc/iosfwd @@ -200,6 +200,14 @@ template > class basic_ofstream; template > class basic_fstream; +#if _HAS_CXX20 +template > +class _Basic_syncbuf_impl; +template , class _Alloc = allocator<_Elem>> +class basic_syncbuf; +template , class _Alloc = allocator<_Elem>> +class basic_osyncstream; +#endif // _HAS_CXX20 #if defined(_DLL_CPPLIB) template @@ -224,6 +232,10 @@ using filebuf = basic_filebuf>; using ifstream = basic_ifstream>; using ofstream = basic_ofstream>; using fstream = basic_fstream>; +#if _HAS_CXX20 +using syncbuf = basic_syncbuf; +using osyncstream = basic_osyncstream; +#endif // _HAS_CXX20 // wchar_t TYPEDEFS using wios = basic_ios>; @@ -239,6 +251,10 @@ using wfilebuf = basic_filebuf>; using wifstream = basic_ifstream>; using wofstream = basic_ofstream>; using wfstream = basic_fstream>; +#if _HAS_CXX20 +using wsyncbuf = basic_syncbuf; +using wosyncstream = basic_osyncstream; +#endif // _HAS_CXX20 #if defined(_CRTBLD) // unsigned short TYPEDEFS diff --git a/stl/inc/iterator b/stl/inc/iterator index ca74d58917c..6fd2233e372 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -302,11 +302,13 @@ _NODISCARD bool operator==(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _ return _Left._Equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Left, const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) noexcept /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostream_iterator template > @@ -488,11 +490,13 @@ _NODISCARD bool operator==( return _Left.equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const istreambuf_iterator<_Elem, _Traits>& _Left, const istreambuf_iterator<_Elem, _Traits>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostreambuf_iterator template @@ -1492,6 +1496,13 @@ public: return _Myindex == _Right._Myindex; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const checked_array_iterator& _Right) const noexcept { + _STL_VERIFY(_Myarray == _Right._Myarray && _Mysize == _Right._Mysize, + "cannot compare incompatible checked_array_iterators"); + return _Myindex <=> _Right._Myindex; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const checked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1513,6 +1524,7 @@ public: _NODISCARD constexpr bool operator>=(const checked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 friend constexpr void _Verify_range( const checked_array_iterator& _First, const checked_array_iterator& _Last) noexcept { @@ -1646,6 +1658,11 @@ public: return _Myptr == _Right._Myptr; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const unchecked_array_iterator& _Right) const noexcept { + return _Myptr <=> _Right._Myptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const unchecked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1665,6 +1682,7 @@ public: _NODISCARD constexpr bool operator>=(const unchecked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend constexpr void _Verify_range( diff --git a/stl/inc/list b/stl/inc/list index dca51000ff7..58f93ff9bb6 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -74,9 +74,11 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -199,9 +201,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _List_const_iterator& _First, const _List_const_iterator& _Last) noexcept { @@ -1803,17 +1807,28 @@ void swap(list<_Ty, _Alloc>& _Left, list<_Ty, _Alloc>& _Right) noexcept /* stren template _NODISCARD bool operator==(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() && _STD equal(_Left.begin(), _Left.end(), _Right.begin()); + return _Left.size() == _Right.size() + && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); } template @@ -1830,6 +1845,7 @@ template _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/map b/stl/inc/map index 078249a414a..1c66bbcd478 100644 --- a/stl/inc/map +++ b/stl/inc/map @@ -369,11 +369,21 @@ _NODISCARD bool operator==(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_ && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -394,6 +404,7 @@ template _NODISCARD bool operator>=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(map<_Kty, _Ty, _Pr, _Alloc>& _Left, map<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( @@ -557,12 +568,22 @@ _NODISCARD bool operator==( && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { @@ -587,6 +608,7 @@ _NODISCARD bool operator>=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/memory b/stl/inc/memory index c8021980f36..4fe7830d5df 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -28,35 +28,6 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #ifdef __cpp_lib_concepts namespace ranges { - // clang-format off - // CONCEPT _No_throw_input_iterator - template - concept _No_throw_input_iterator = input_iterator<_It> - && is_lvalue_reference_v> - && same_as>, iter_value_t<_It>>; - - // CONCEPT _No_throw_sentinel_for - template - concept _No_throw_sentinel_for = sentinel_for<_Se, _It>; - - // CONCEPT _No_throw_forward_iterator - template - concept _No_throw_forward_iterator = _No_throw_input_iterator<_It> - && forward_iterator<_It> - && _No_throw_sentinel_for<_It, _It>; - - // CONCEPT _No_throw_input_range - template - concept _No_throw_input_range = range<_Rng> - && _No_throw_input_iterator> - && _No_throw_sentinel_for, iterator_t<_Rng>>; - - // CONCEPT _No_throw_forward_range - template - concept _No_throw_forward_range = _No_throw_input_range<_Rng> - && _No_throw_forward_iterator>; - // clang-format on - // ALIAS TEMPLATE uninitialized_copy_result template using uninitialized_copy_result = in_out_result<_In, _Out>; @@ -107,7 +78,7 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_reference_t<_It>>); - if constexpr (is_same_v<_Se, _It> && _Ptr_copy_cat<_It, _Out>::_Really_trivial) { + if constexpr (is_same_v<_Se, _It> && is_same_v<_OSe, _Out> && _Ptr_copy_cat<_It, _Out>::_Really_trivial) { return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -178,7 +149,9 @@ namespace ranges { auto _OFirst = _Get_unwrapped(_STD move(_First2)); const auto _OLast = _Get_unwrapped(_STD move(_Last2)); if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial) { - _OFirst = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + _IFirst = _UResult.in; + _OFirst = _UResult.out; } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -214,10 +187,6 @@ _NoThrowFwdIt uninitialized_move(const _InIt _First, const _InIt _Last, _NoThrow #ifdef __cpp_lib_concepts namespace ranges { - // ALIAS TEMPLATE uninitialized_move_result - template - using uninitialized_move_result = in_out_result<_In, _Out>; - // VARIABLE ranges::uninitialized_move class _Uninitialized_move_fn : private _Not_quite_object { public: @@ -231,9 +200,9 @@ namespace ranges { // clang-format on _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); - auto _UResult = - _Uninitialized_move_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), - _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2))); + auto _UResult = _RANGES _Uninitialized_move_unchecked(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2))); _Seek_wrapped(_First1, _STD move(_UResult.in)); _Seek_wrapped(_First2, _STD move(_UResult.out)); @@ -247,35 +216,12 @@ namespace ranges { _Rng1&& _Range1, _Rng2&& _Range2) const { // clang-format on auto _First1 = _RANGES begin(_Range1); - auto _UResult = _Uninitialized_move_unchecked( + auto _UResult = _RANGES _Uninitialized_move_unchecked( _Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2)); _Seek_wrapped(_First1, _STD move(_UResult.in)); return {_STD move(_First1), _Rewrap_iterator(_Range2, _STD move(_UResult.out))}; } - - private: - template - _NODISCARD static uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( - _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_Out>); - _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); - _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); - - if constexpr (is_same_v<_Se, _It> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { - return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); - } else { - _Uninitialized_backout _Backout{_STD move(_OFirst)}; - - for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) { - _Backout._Emplace_back(_RANGES iter_move(_IFirst)); - } - - return {_STD move(_IFirst), _Backout._Release()}; - } - } }; inline constexpr _Uninitialized_move_fn uninitialized_move{_Not_quite_object::_Construct_tag{}}; @@ -339,7 +285,9 @@ namespace ranges { auto _OFirst = _Get_unwrapped(_STD move(_First2)); const auto _OLast = _Get_unwrapped(_STD move(_Last2)); if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial) { - _OFirst = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + _IFirst = _UResult.in; + _OFirst = _UResult.out; } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -386,7 +334,7 @@ namespace ranges { private: template - _NODISCARD static _It _Uninitialized_fill_unchecked(_It _OFirst, const _Se _OLast, const _Ty& _Val) { + _NODISCARD static _It _Uninitialized_fill_unchecked(_It _OFirst, _Se _OLast, const _Ty& _Val) { _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, const _Ty&>); @@ -844,16 +792,13 @@ namespace ranges { private: template - _NODISCARD static _It _Uninitialized_value_construct_unchecked(_It _OFirst, const _Se _OLast) { + _NODISCARD static _It _Uninitialized_value_construct_unchecked(_It _OFirst, _Se _OLast) { _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(default_initializable>); if constexpr (_Use_memset_value_construct_v<_It>) { - const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); - const auto _Count = static_cast(_OFinal - _OFirst); - _CSTD memset(_STD to_address(_OFirst), 0, _Count); - return _OFinal; + return _Zero_range(_OFirst, _RANGES next(_OFirst, _STD move(_OLast))); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -902,8 +847,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); if constexpr (_Use_memset_value_construct_v<_It>) { - _CSTD memset(_STD to_address(_UFirst), 0, static_cast(_Count)); - _Seek_wrapped(_First, _UFirst + _Count); + _Seek_wrapped(_First, _Zero_range(_UFirst, _UFirst + _Count)); } else { _Uninitialized_backout _Backout{_STD move(_UFirst)}; @@ -1847,6 +1791,12 @@ _NODISCARD bool operator==(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2> return _Left.get() == _Right.get(); } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { + return _Left.get() <=> _Right.get(); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() != _Right.get(); @@ -1871,12 +1821,19 @@ template _NODISCARD bool operator<=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template _NODISCARD bool operator==(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { return _Left.get() == nullptr; } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { + return _Left.get() <=> static_cast::element_type*>(nullptr); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return nullptr == _Right.get(); @@ -1931,6 +1888,7 @@ template _NODISCARD bool operator<=(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return static_cast::element_type*>(nullptr) <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Out, const shared_ptr<_Ty>& _Px) { @@ -2082,10 +2040,10 @@ struct _Alignas_storage_unit { alignas(_Align) char _Space[_Align]; }; -enum class _Check_overflow : bool { _No, _Yes }; +enum class _Check_overflow : bool { _Nope, _Yes }; template -_NODISCARD size_t _Calculate_bytes_for_flexible_array(const size_t _Count) noexcept(_Check == _Check_overflow::_No) { +_NODISCARD size_t _Calculate_bytes_for_flexible_array(const size_t _Count) noexcept(_Check == _Check_overflow::_Nope) { constexpr size_t _Align = alignof(_Refc); size_t _Bytes = sizeof(_Refc); // contains storage for one element @@ -2122,20 +2080,26 @@ template _NODISCARD _Refc* _Allocate_flexible_array(const size_t _Count) { const size_t _Bytes = _Calculate_bytes_for_flexible_array<_Refc, _Check_overflow::_Yes>(_Count); constexpr size_t _Align = alignof(_Refc); - if constexpr (_Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - return static_cast<_Refc*>(::operator new(_Bytes)); - } else { +#ifdef __cpp_aligned_new + if constexpr (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { return static_cast<_Refc*>(::operator new (_Bytes, align_val_t{_Align})); + } else +#endif // __cpp_aligned_new + { + return static_cast<_Refc*>(::operator new(_Bytes)); } } template void _Deallocate_flexible_array(_Refc* const _Ptr) noexcept { constexpr size_t _Align = alignof(_Refc); - if constexpr (_Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - ::operator delete(static_cast(_Ptr)); - } else { +#ifdef __cpp_aligned_new + if constexpr (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { ::operator delete (static_cast(_Ptr), align_val_t{_Align}); + } else +#endif // __cpp_aligned_new + { + ::operator delete(static_cast(_Ptr)); } } @@ -2691,7 +2655,7 @@ private: _Rebind_alloc_t<_Alloc, _Storage> _Al(this->_Get_val()); const size_t _Bytes = - _Calculate_bytes_for_flexible_array<_Ref_count_unbounded_array_alloc, _Check_overflow::_No>(_Size); + _Calculate_bytes_for_flexible_array<_Ref_count_unbounded_array_alloc, _Check_overflow::_Nope>(_Size); const size_t _Storage_units = _Bytes / sizeof(_Storage); this->~_Ref_count_unbounded_array_alloc(); @@ -3471,10 +3435,12 @@ _NODISCARD bool operator==(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return _Left.get() == _Right.get(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { @@ -3499,11 +3465,25 @@ _NODISCARD bool operator<=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable_with::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> +_NODISCARD compare_three_way_result_t::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> + operator<=>(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { + // clang-format on + return _Left.get() <=> _Right.get(); +} +#endif // __cpp_lib_concepts + template _NODISCARD bool operator==(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) noexcept { return !_Left; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !_Right; @@ -3518,6 +3498,7 @@ template _NODISCARD bool operator!=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t _Right) { @@ -3561,6 +3542,17 @@ _NODISCARD bool operator<=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable::pointer> +_NODISCARD compare_three_way_result_t::pointer> operator<=>( + const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) { + // clang-format on + return _Left.get() <=> static_cast::pointer>(nullptr); +} +#endif // __cpp_lib_concepts + template struct _Can_stream_unique_ptr : false_type {}; template diff --git a/stl/inc/memory_resource b/stl/inc/memory_resource index a97b4c99049..618c74aae25 100644 --- a/stl/inc/memory_resource +++ b/stl/inc/memory_resource @@ -44,7 +44,7 @@ namespace pmr { #endif // __cpp_aligned_new } - extern "C" _CRT_SATELLITE_1 _NODISCARD memory_resource* __cdecl null_memory_resource() noexcept; + extern "C" _NODISCARD _CRT_SATELLITE_1 memory_resource* __cdecl null_memory_resource() noexcept; // FUNCTION new_delete_resource class _Identity_equal_resource : public memory_resource { diff --git a/stl/inc/mutex b/stl/inc/mutex index cf58b283725..aafefe48bcd 100644 --- a/stl/inc/mutex +++ b/stl/inc/mutex @@ -153,7 +153,12 @@ public: template _NODISCARD_CTOR unique_lock(_Mutex& _Mtx, const chrono::time_point<_Clock, _Duration>& _Abs_time) - : _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock_until(_Abs_time)) {} // construct and lock with timeout + : _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock_until(_Abs_time)) { + // construct and lock with timeout +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 + } _NODISCARD_CTOR unique_lock(_Mutex& _Mtx, const xtime* _Abs_time) : _Pmtx(_STD addressof(_Mtx)), _Owns(false) { // try to lock until _Abs_time @@ -209,6 +214,9 @@ public: template _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 _Validate(); _Owns = _Pmtx->try_lock_until(_Abs_time); return _Owns; @@ -635,6 +643,9 @@ public: template cv_status wait_until(unique_lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time) { // wait until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 for (;;) { const auto _Now = _Clock::now(); if (_Abs_time <= _Now) { @@ -654,6 +665,9 @@ public: bool wait_until( unique_lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time, _Predicate _Pred) { // wait for signal with timeout and check predicate +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Wait_until1(_Lck, _Abs_time, _Pred); } @@ -792,8 +806,11 @@ public: } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock the mutex with timeout + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock the mutex with timeout +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Try_lock_until(_Abs_time); } @@ -903,8 +920,11 @@ public: } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock the mutex with timeout + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock the mutex with timeout +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Try_lock_until(_Abs_time); } diff --git a/stl/inc/optional b/stl/inc/optional index 04e36c0c10b..81d2639a164 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -11,6 +11,9 @@ #if !_HAS_CXX17 #pragma message("The contents of are available only with C++17 or later.") #else // ^^^ !_HAS_CXX17 / _HAS_CXX17 vvv +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #include #include #include @@ -480,11 +483,30 @@ _NODISCARD constexpr bool operator>=(const optional<_Ty1>& _Left, const optional return !_Right.has_value() || (_Left.has_value() && *_Left >= *_Right); } +#ifdef __cpp_lib_concepts +template _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> operator<=>( + const optional<_Ty1>& _Left, const optional<_Ty2>& _Right) { + if (_Left && _Right) { + return *_Left <=> *_Right; + } + + return _Left.has_value() <=> _Right.has_value(); +} +#endif // __cpp_lib_concepts + // COMPARISONS WITH nullopt [optional.nullops] template _NODISCARD constexpr bool operator==(const optional<_Ty>& _Left, nullopt_t) noexcept { return !_Left.has_value(); } + +#if _HAS_CXX20 +template +_NODISCARD constexpr strong_ordering operator<=>(const optional<_Ty>& _Left, nullopt_t) noexcept { + return _Left.has_value() <=> false; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD constexpr bool operator==(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); @@ -534,6 +556,7 @@ template _NODISCARD constexpr bool operator>=(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); } +#endif // !_HAS_CXX20 // COMPARISONS WITH T [optional.comp_with_t] template @@ -567,6 +590,7 @@ template = _NODISCARD constexpr bool operator==(const optional<_Ty1>& _Left, const _Ty2& _Right) { return _Left ? *_Left == _Right : false; } + template = 0> _NODISCARD constexpr bool operator==(const _Ty1& _Left, const optional<_Ty2>& _Right) { return _Right ? _Left == *_Right : false; @@ -617,6 +641,22 @@ _NODISCARD constexpr bool operator>=(const _Ty1& _Left, const optional<_Ty2>& _R return _Right ? _Left >= *_Right : true; } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (!_Is_specialization_v<_Ty2, optional>) // TRANSITION, GH-1674 + && three_way_comparable_with<_Ty1, _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> + operator<=>(const optional<_Ty1>& _Left, const _Ty2& _Right) { + // clang-format on + if (_Left) { + return *_Left <=> _Right; + } + + return strong_ordering::less; +} +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE swap [optional.specalg] template && is_swappable_v<_Ty>, int> = 0> void swap(optional<_Ty>& _Left, optional<_Ty>& _Right) noexcept(noexcept(_Left.swap(_Right))) { diff --git a/stl/inc/ostream b/stl/inc/ostream index 671f8d0e0bd..61766bf311e 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -993,6 +993,45 @@ basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL flush(basic_ostream<_Elem, _Tr return _Ostr; } +#if _HAS_CXX20 +#ifdef _CPPRTTI +template +basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->set_emit_on_sync(true); + } + return _Ostr; +} + +template +basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->set_emit_on_sync(false); + } + return _Ostr; +} + +template +basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>& _Ostr) { + _Ostr.flush(); + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->_Do_emit(); + } + return _Ostr; +} +#else // _CPPRTTI +template +basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +template +basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +template +basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +#endif // _CPPRTTI +#endif // _HAS_CXX20 + // INSERTER FOR error_category template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, diff --git a/stl/inc/queue b/stl/inc/queue index 8954046397d..bcd510658e4 100644 --- a/stl/inc/queue +++ b/stl/inc/queue @@ -54,6 +54,14 @@ _NODISCARD bool operator>=(const queue<_Ty, _Container>& _Left, const queue<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class queue { public: @@ -150,6 +158,12 @@ public: friend bool operator>= <>(const queue&, const queue&); // clang-format on +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const queue<_Ty2, _Container2>&, const queue<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts + protected: _Container c{}; }; diff --git a/stl/inc/random b/stl/inc/random index 405f47898a4..4bef1ed35fa 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -2428,7 +2428,7 @@ public: _Ty1 _Logp; _Ty1 _Logp1; - _Small_poisson_distribution<_Ty> _Small; + _Small_poisson_distribution<_Ty> _Small; // TRANSITION, ABI: unused }; binomial_distribution() : _Par(1, _Ty1(0.5)) {} @@ -2505,8 +2505,26 @@ private: return _Res; } else if (_Par0._Mean < 1.0) { - // events are rare, use Poisson distribution - _Res = _Par0._Small(_Eng); + // Events are rare, use waiting time method (Luc Devroye, Non-Uniform Random Variate Generation, p. 525). + const _Ty1 _Rand = _NRAND(_Eng, _Ty1); + + // The exit condition is log(1 - _Rand)/t < log(1-p), which is equivalent to _Rand > 1 - (1-p)^t. If + // we have a cheap upper bound for 1-(1-p)^t, we can exit early without having to call log. We use two + // such bounds, one that is tight for mean ~0 and another for mean ~1. In the first case, Bernoulli's + // inequality gives -1+p*t >= -(1-p)^t, so 1 - (1-p)^t <= p*t = mean. For the other bound, 1-(1-p)^t = + // 1-(1-p)(1-mean/t)^(t-1) <= 1-(1-p)(1-1/t)^(t-1) <= 1-(1-p)/e. + const _Ty1 _Ub = + (_STD min)(_Par0._Mean, _Ty1{3.678794411714423216e-1} * _Par0._Pp + _Ty1{6.32120558828557678e-1}); + if (_Rand > _Ub) { + _Res = _Ty{0}; + } else { + _Ty _Denom = _Par0._Tx; + _Ty1 _Sum = _CSTD log(_Ty1{1.0} - _Rand) / _Denom; + while (_Sum >= _Par0._Logp1 && --_Denom != 0) { + _Sum += _CSTD log(_Ty1{1.0} - _NRAND(_Eng, _Ty1)) / _Denom; + } + _Res = static_cast<_Ty>(_Par0._Tx - _Denom); + } } else { // no shortcuts using _Uty = make_unsigned_t<_Ty>; const auto _Ty1_Tx{_Float_upper_bound<_Ty1>(static_cast<_Uty>(_Par0._Tx))}; @@ -2780,7 +2798,7 @@ public: } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -2917,11 +2935,11 @@ public: } _NODISCARD result_type(min)() const { // get smallest possible result - return numeric_limits::denorm_min(); + return -numeric_limits::infinity(); } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() { // clear internal state @@ -3057,7 +3075,7 @@ public: } _NODISCARD bool operator==(const param_type& _Right) const { - return _Px == _Right._Px; + return _Alpha == _Right._Alpha && _Beta == _Right._Beta; } _NODISCARD bool operator!=(const param_type& _Right) const { @@ -3111,11 +3129,11 @@ public: } _NODISCARD result_type(min)() const { // get smallest possible result - return numeric_limits::denorm_min(); + return result_type{0.0}; } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -3306,7 +3324,7 @@ public: } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -3438,11 +3456,11 @@ public: } _NODISCARD result_type(min)() const { // get smallest possible result - return (numeric_limits::min)(); + return -numeric_limits::infinity(); } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -3576,11 +3594,11 @@ public: } _NODISCARD result_type(min)() const { // get smallest possible result - return -(numeric_limits::max)(); + return result_type{0.0}; } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -3702,11 +3720,11 @@ public: } _NODISCARD result_type(min)() const { // get smallest possible result - return numeric_limits::denorm_min(); + return result_type{0.0}; } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -3834,11 +3852,11 @@ public: } _NODISCARD result_type(min)() const { // get smallest possible result - return -(numeric_limits::max)(); + return -numeric_limits::infinity(); } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -4035,7 +4053,7 @@ public: } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state @@ -4168,11 +4186,11 @@ public: } _NODISCARD result_type(min)() const { // get smallest possible result - return -(numeric_limits::max)(); + return -numeric_limits::infinity(); } _NODISCARD result_type(max)() const { // get largest possible result - return (numeric_limits::max)(); + return numeric_limits::infinity(); } void reset() {} // clear internal state diff --git a/stl/inc/ranges b/stl/inc/ranges index 1720c95babd..367648ffe80 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -13,6 +13,7 @@ #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv #include #include +#include #include #include @@ -534,6 +535,331 @@ namespace ranges { inline constexpr _Single_fn single; } // namespace views + // CLASS TEMPLATE ranges::iota_view + template + using _Iota_diff_t = conditional_t, conditional_t<(sizeof(_Ty) < sizeof(int)), int, long long>, + iter_difference_t<_Ty>>; + + // clang-format off + template + concept _Decrementable = incrementable<_Ty> && requires(_Ty __t) { + { --__t } -> same_as<_Ty&>; + { __t-- } -> same_as<_Ty>; + }; + + template + concept _Advanceable = _Decrementable<_Ty> && totally_ordered<_Ty> + && requires(_Ty __i, const _Ty __j, const _Iota_diff_t<_Ty> __n) { + { __i += __n } -> same_as<_Ty&>; + { __i -= __n } -> same_as<_Ty&>; + _Ty(__j + __n); + _Ty(__n + __j); + _Ty(__j - __n); + { __j - __j } -> convertible_to<_Iota_diff_t<_Ty>>; + }; + + template + requires semiregular<_Wi> + struct _Ioterator { + // clang-format on + /* [[no_unique_address]] */ _Wi _Current{}; + + using iterator_concept = conditional_t<_Advanceable<_Wi>, random_access_iterator_tag, + conditional_t<_Decrementable<_Wi>, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>; + using iterator_category = input_iterator_tag; + using value_type = _Wi; + using difference_type = _Iota_diff_t<_Wi>; + + _NODISCARD constexpr _Wi operator*() const noexcept(is_nothrow_copy_constructible_v<_Wi>) { + return _Current; + } + + constexpr _Ioterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ { + ++_Current; + return *this; + } + + constexpr auto operator++(int) noexcept( + noexcept(++_Current) && (!incrementable<_Wi> || is_nothrow_copy_constructible_v<_Wi>) ) /* strengthened */ { + if constexpr (incrementable<_Wi>) { + auto _Tmp = *this; + ++_Current; + return _Tmp; + } else { + ++_Current; + } + } + + constexpr _Ioterator& operator--() noexcept( + noexcept(--_Current)) /* strengthened */ requires _Decrementable<_Wi> { + --_Current; + return *this; + } + + constexpr _Ioterator operator--(int) noexcept(is_nothrow_copy_constructible_v<_Wi>&& noexcept( + --_Current)) /* strengthened */ requires _Decrementable<_Wi> { + auto _Tmp = *this; + --_Current; + return _Tmp; + } + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1347136 + private: + template + static constexpr bool _Nothrow_plus_equal = noexcept(_STD declval<_Left&>() += _STD declval()); + template <_Integer_like _Left, class _Right> + static constexpr bool _Nothrow_plus_equal<_Left, _Right> = true; + + template + static constexpr bool _Nothrow_minus_equal = noexcept(_STD declval<_Left&>() -= _STD declval()); + template <_Integer_like _Left, class _Right> + static constexpr bool _Nothrow_minus_equal<_Left, _Right> = true; + + public: +#endif // TRANSITION, DevCom-1347136 + + constexpr _Ioterator& operator+=(const difference_type _Off) +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-1347136 + noexcept(noexcept(_Current += _Off)) /* strengthened */ +#else // ^^^ no workaround / workaround vvv + noexcept(_Nothrow_plus_equal<_Wi, difference_type>) /* strengthened */ +#endif // TRANSITION, DevCom-1347136 + requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + if constexpr (_Signed_integer_like<_Wi>) { + _Current = static_cast<_Wi>(_Current + _Off); + } else { + if (_Off >= difference_type{0}) { + _Current += static_cast<_Wi>(_Off); + } else { + _Current -= static_cast<_Wi>(-_Off); + } + } + } else { + _Current += _Off; + } + return *this; + } + + constexpr _Ioterator& operator-=(const difference_type _Off) +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-1347136 + noexcept(noexcept(_Current -= _Off)) /* strengthened */ +#else // ^^^ no workaround / workaround vvv + noexcept(_Nothrow_minus_equal<_Wi, difference_type>) /* strengthened */ +#endif // TRANSITION, DevCom-1347136 + requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + if constexpr (_Signed_integer_like<_Wi>) { + _Current = static_cast<_Wi>(_Current - _Off); + } else { + if (_Off >= difference_type{0}) { + _Current -= static_cast<_Wi>(_Off); + } else { + _Current += static_cast<_Wi>(-_Off); + } + } + } else { + _Current -= _Off; + } + return *this; + } + + _NODISCARD constexpr _Wi operator[](const difference_type _Idx) const + noexcept(noexcept(static_cast<_Wi>(_Current + _Idx))) /* strengthened */ requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + return static_cast<_Wi>(_Current + static_cast<_Wi>(_Idx)); + } else { + return static_cast<_Wi>(_Current + _Idx); + } + } + + _NODISCARD friend constexpr bool operator==( + const _Ioterator&, const _Ioterator&) requires equality_comparable<_Wi> = default; + _NODISCARD friend constexpr bool operator<(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires totally_ordered<_Wi> { + return _Left._Current < _Right._Current; + } + _NODISCARD friend constexpr bool operator>(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Right._Current < _Left._Current)) /* strengthened */ requires totally_ordered<_Wi> { + return _Right._Current < _Left._Current; + } + _NODISCARD friend constexpr bool operator<=(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(!(_Right._Current < _Left._Current))) /* strengthened */ requires totally_ordered<_Wi> { + return !(_Right._Current < _Left._Current); + } + _NODISCARD friend constexpr bool operator>=(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(!(_Left._Current < _Right._Current))) /* strengthened */ requires totally_ordered<_Wi> { + return !(_Left._Current < _Right._Current); + } + // clang-format off + _NODISCARD friend constexpr auto operator<=>(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current <=> _Right._Current)) /* strengthened */ + requires totally_ordered<_Wi> && three_way_comparable<_Wi> { + // clang-format on + return _Left._Current <=> _Right._Current; + } + + _NODISCARD friend constexpr _Ioterator operator+(_Ioterator _It, const difference_type _Off) noexcept( + noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + } + _NODISCARD friend constexpr _Ioterator operator+(const difference_type _Off, _Ioterator _It) noexcept( + noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + } + _NODISCARD friend constexpr _Ioterator operator-(_Ioterator _It, const difference_type _Off) noexcept( + noexcept(static_cast<_Wi>(_It._Current - _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current - _Off)}; + } + _NODISCARD friend constexpr difference_type + operator-(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current - _Right._Current)) /* strengthened */ requires _Advanceable<_Wi> { + return static_cast(_Left._Current - _Right._Current); + } + }; + + // clang-format off + template + requires _Weakly_equality_comparable_with<_Wi, _Bo> && semiregular<_Wi> + struct _Iotinel { + // clang-format on + private: + using _It = _Ioterator<_Wi>; + + _NODISCARD constexpr bool _Equal(const _It& _That) const noexcept(noexcept(_That._Current == _Last)) { + return _That._Current == _Last; + } + + _NODISCARD constexpr iter_difference_t<_Wi> _Delta(const _It& _That) const + noexcept(noexcept(_Last - _That._Current)) { + _STL_INTERNAL_STATIC_ASSERT(sized_sentinel_for<_Bo, _Wi>); + return _Last - _That._Current; + } + + public: + /* [[no_unique_address]] */ _Bo _Last{}; + + _NODISCARD friend constexpr bool operator==(const _It& _Left, const _Iotinel& _Right) noexcept( + noexcept(_Right._Equal(_Left))) /* strengthened */ { + return _Right._Equal(_Left); + } + + _NODISCARD friend constexpr iter_difference_t<_Wi> operator-(const _It& _Left, const _Iotinel& _Right) noexcept( + noexcept(_Right._Delta(_Left))) /* strengthened */ requires sized_sentinel_for<_Bo, _Wi> { + return -_Right._Delta(_Left); + } + + _NODISCARD friend constexpr iter_difference_t<_Wi> operator-(const _Iotinel& _Left, const _It& _Right) noexcept( + noexcept(_Left._Delta(_Right))) /* strengthened */ requires sized_sentinel_for<_Bo, _Wi> { + return _Left._Delta(_Right); + } + }; + + // clang-format off + template + requires _Weakly_equality_comparable_with<_Wi, _Bo> && semiregular<_Wi> + class iota_view : public view_interface> { + // clang-format on + private: + /* [[no_unique_address]] */ _Wi _Value{}; + /* [[no_unique_address]] */ _Bo _Bound{}; + + using _It = _Ioterator<_Wi>; + using _Se = conditional_t, _It, + conditional_t, _Bo, _Iotinel<_Wi, _Bo>>>; + + _NODISCARD static constexpr _Bo& _Bound_from(_Se& _Last) noexcept { + if constexpr (same_as<_Wi, _Bo>) { + return _Last._Current; + } else if constexpr (same_as<_Bo, unreachable_sentinel_t>) { + return _Last; + } else { + return _Last._Last; + } + } + + public: + iota_view() = default; + + constexpr explicit iota_view(_Wi _Value_) noexcept( + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_default_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_Value_)) {} + + constexpr iota_view(type_identity_t<_Wi> _Value_, type_identity_t<_Bo> _Bound_) noexcept( + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_Value_)), _Bound(_STD move(_Bound_)) { + if constexpr (totally_ordered_with<_Wi, _Bo>) { + _STL_ASSERT(_Value_ <= _Bound_, "Per N4878 [range.iota.view]/8, the first argument must precede the " + "second when their types are totally ordered."); + } + } + + constexpr iota_view(_It _First, _Se _Last) noexcept( // Per LWG-3523 + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_First._Current)), _Bound(_STD move(_Bound_from(_Last))) {} + + _NODISCARD constexpr _It begin() const noexcept(is_nothrow_copy_constructible_v<_Wi>) /* strengthened */ { + return _It{_Value}; + } + + _NODISCARD constexpr _Se end() const noexcept(is_nothrow_copy_constructible_v<_Bo>) /* strengthened */ { + if constexpr (same_as<_Bo, unreachable_sentinel_t>) { + return unreachable_sentinel; + } else { + return _Se{_Bound}; + } + } + + // clang-format off + _NODISCARD constexpr auto size() const noexcept(noexcept(_Bound - _Value)) /* strengthened */ + requires (same_as<_Wi, _Bo> && _Advanceable<_Wi>) + || (integral<_Wi> && integral<_Bo>) + || sized_sentinel_for<_Bo, _Wi> { + // clang-format on + if constexpr (_Integer_like<_Wi> && _Integer_like<_Bo>) { +#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned + return (_Value < 0) ? ((_Bound < 0) ? (_To_unsigned_like(-_Value) - _To_unsigned_like(-_Bound)) +#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned + : (_To_unsigned_like(_Bound) + _To_unsigned_like(-_Value))) + : (_To_unsigned_like(_Bound) - _To_unsigned_like(_Value)); + } else { + return _To_unsigned_like(_Bound - _Value); + } + } + }; + + // clang-format off + template + requires (!_Integer_like<_Wi> || !_Integer_like<_Bo> + || (_Signed_integer_like<_Wi> == _Signed_integer_like<_Bo>)) + iota_view(_Wi, _Bo) -> iota_view<_Wi, _Bo>; + // clang-format on + + template + inline constexpr bool enable_borrowed_range> = true; + + namespace views { + // VARIABLE views::iota + struct _Iota_fn { + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) const + noexcept(noexcept(iota_view{static_cast<_Ty&&>(_Val)})) requires requires { + iota_view{static_cast<_Ty&&>(_Val)}; + } + { return iota_view{static_cast<_Ty&&>(_Val)}; } + + template + _NODISCARD constexpr auto operator()(_Ty1&& _Val1, _Ty2&& _Val2) const noexcept( + noexcept(iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)})) requires requires { + iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)}; + } + { return iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)}; } + }; + + inline constexpr _Iota_fn iota; + } // namespace views + // CLASS TEMPLATE ranges::istream_view template concept _Stream_extractable = requires(basic_istream<_Elem, _Traits>& __is, _Ty& __t) { @@ -1548,7 +1874,7 @@ namespace ranges { } // clang-format off - template + template requires sentinel_for<_Base_sentinel, _Base_iterator<_OtherConst>> _NODISCARD friend constexpr bool operator==( const _Counted_iter<_OtherConst>& _Left, const _Sentinel& _Right) { @@ -1666,25 +1992,25 @@ namespace ranges { 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; + inline constexpr bool _Is_subrange = false; template - static constexpr bool _Is_subrange> = true; + inline constexpr bool _Is_subrange> = true; + // clang-format off 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>); + concept _Random_sized_range = random_access_range<_Rng> && sized_range<_Rng>; + // clang-format on class _Take_fn { private: - enum class _St { _Empty, _Preserve, _Take_view }; + enum class _St { + _Empty, + _Reconstruct_span, + _Reconstruct_string_view, + _Reconstruct_iota_view, + _Reconstruct_subrange, + _Take_view + }; template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { @@ -1692,9 +2018,16 @@ namespace ranges { 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&>()), + } else if constexpr (_Is_span_v<_Ty>) { + return {_St::_Reconstruct_span, true}; + } else if constexpr (_Is_specialization_v<_Ty, basic_string_view>) { + return {_St::_Reconstruct_string_view, true}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_specialization_v<_Ty, iota_view>) { + return {_St::_Reconstruct_iota_view, + noexcept(_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()))}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + return {_St::_Reconstruct_subrange, + noexcept(subrange{_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}})}; @@ -1730,13 +2063,26 @@ namespace ranges { if constexpr (_Strat == _St::_Empty) { // it's an empty_view: return another empty view return remove_cvref_t<_Rng>{}; - } else if constexpr (_Strat == _St::_Preserve) { + } else if constexpr (_Strat == _St::_Take_view) { + return take_view{_STD forward<_Rng>(_Range), _Count}; + } else { // 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 (_Strat == _St::_Take_view) { - return take_view{_STD forward<_Rng>(_Range), _Count}; + + // The following are all per the proposed resolution of LWG-3407 + if constexpr (_Strat == _St::_Reconstruct_span) { + return span{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_string_view) { + return remove_cvref_t<_Rng>{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_iota_view) { + using _Vt = range_value_t<_Rng>; + return iota_view<_Vt, _Vt>{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_subrange) { + return subrange{_First, _First + _Count}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } } } @@ -2053,7 +2399,7 @@ namespace ranges { // VARIABLE views::drop class _Drop_fn { private: - enum class _St { _Empty, _Preserve, _Drop_view }; + enum class _St { _Empty, _Reconstruct_span, _Reconstruct_subrange, _Reconstruct_other, _Drop_view }; template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { @@ -2061,8 +2407,22 @@ namespace ranges { if constexpr (_Is_specialization_v<_Ty, empty_view>) { return {_St::_Empty, true}; - } else if constexpr (_Reconstructible_range<_Rng>) { - return {_St::_Preserve, + } else if constexpr (_Is_span_v<_Ty>) { + return {_St::_Reconstruct_span, true}; + } else if constexpr (_Is_specialization_v<_Ty, basic_string_view>) { + return {_St::_Reconstruct_other, true}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + if constexpr (sized_sentinel_for, iterator_t<_Ty>>) { + return {_St::_Reconstruct_subrange, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>())})}; + } else { + return {_St::_Reconstruct_subrange, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>()), range_difference_t<_Rng>{0}})}; + } + } else if constexpr (_Random_sized_range<_Ty> && _Is_specialization_v<_Ty, iota_view>) { + return {_St::_Reconstruct_other, noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), _RANGES end(_STD declval<_Rng&>())})}; } else { @@ -2099,12 +2459,27 @@ namespace ranges { if constexpr (_Strat == _St::_Empty) { // it's an empty_view: return another empty view return remove_cvref_t<_Rng>{}; - } else if constexpr (_Strat == _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 (_Strat == _St::_Drop_view) { return drop_view{_STD forward<_Rng>(_Range), _Count}; + } else { + // it's a "reconstructible range"; return the same kind of range with a restricted extent + _Count = (_STD min)(_RANGES distance(_Range), _Count); + + // The following are all per the proposed resolution of LWG-3407 + if constexpr (_Strat == _St::_Reconstruct_span) { + return span{_Ubegin(_Range) + _Count, _Uend(_Range)}; + } else if constexpr (_Strat == _St::_Reconstruct_subrange) { + if constexpr (sized_sentinel_for, iterator_t<_Rng>>) { + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else { + return remove_cvref_t<_Rng>{ + _RANGES begin(_Range) + _Count, _RANGES end(_Range), _RANGES size(_Range) - _Count}; + } + } else if constexpr (_Strat == _St::_Reconstruct_other) { + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } } } diff --git a/stl/inc/regex b/stl/inc/regex index 0cd17c2b3a0..2f2949e2683 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -593,6 +593,18 @@ bool _Is_word(_Elem _Ch) { return _UCh <= static_cast<_UElem>('z') && _Is_word(static_cast(_UCh)); } +#if _HAS_CXX20 +template +struct _Get_member_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_member_comparison_category<_Ty, void_t> { + using type = typename _Ty::comparison_category; +}; +#endif // _HAS_CXX20 + // CLASS TEMPLATE sub_match template class sub_match : public pair<_BidIt, _BidIt> { // class to hold contents of a capture group @@ -607,6 +619,10 @@ public: // Note that _Size_type should always be std::size_t using _Size_type = typename string_type::size_type; +#if _HAS_CXX20 + using _Comparison_category = typename _Get_member_comparison_category<_Traits>::type; +#endif // _HAS_CXX20 + constexpr sub_match() : _Mybase(), matched(false) {} bool matched; @@ -709,6 +725,12 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const sub_match<_BidI return _Left._Match_equal(_Right); } +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left == _Right); @@ -733,9 +755,21 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND NTBS template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return _Left._Match_equal(_Right); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_Left); } @@ -765,11 +799,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return _Left._Match_equal(_Right); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left == _Right); @@ -794,9 +823,22 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND ELEMENT template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return _Left._Match_equal(_STD addressof(_Right), 1); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return static_cast::_Comparison_category>( + _Left._Compare(_STD addressof(_Right), 1) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_STD addressof(_Left), 1); } @@ -826,11 +868,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return _Left._Match_equal(_STD addressof(_Right), 1); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left == _Right); @@ -855,6 +892,7 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND string template @@ -863,71 +901,79 @@ _NODISCARD bool operator==( return _Left._Match_equal(_Right.data(), _Right.size()); } +#if _HAS_CXX20 template -_NODISCARD bool operator!=( +_NODISCARD auto operator<=>( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD bool operator==( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Match_equal(_Left.data(), _Left.size()); +} + +template +_NODISCARD bool operator!=( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left == _Right); } template _NODISCARD bool operator<( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return _Left._Less(_Right.data(), _Right.size()); + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Greater(_Left.data(), _Left.size()); } template _NODISCARD bool operator>( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return _Right < _Left; } template _NODISCARD bool operator<=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return !(_Right < _Left); } template _NODISCARD bool operator>=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return !(_Left < _Right); -} - -template -_NODISCARD bool operator==( const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return _Right._Match_equal(_Left.data(), _Left.size()); + return !(_Left < _Right); } template _NODISCARD bool operator!=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Left == _Right); } template _NODISCARD bool operator<( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return _Right._Greater(_Left.data(), _Left.size()); + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return _Left._Less(_Right.data(), _Right.size()); } template _NODISCARD bool operator>( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return _Right < _Left; } template _NODISCARD bool operator<=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Right < _Left); } template _NODISCARD bool operator>=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // INSERT sub_match IN STREAM template @@ -1135,10 +1181,12 @@ _NODISCARD bool operator==(const match_results<_BidIt, _Alloc>& _Left, const mat } } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const match_results<_BidIt, _Alloc>& _Left, const match_results<_BidIt, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // NFA PROPERTIES const unsigned int _BRE_MAX_GRP = 9U; @@ -2429,9 +2477,11 @@ public: && _MyVal._At(0) == _Right._MyVal._At(0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 @@ -2466,7 +2516,7 @@ public: _MyRe = nullptr; #if _ITERATOR_DEBUG_LEVEL == 2 - this->_Orphan_me(); + this->_Orphan_me_v2(); #endif // _ITERATOR_DEBUG_LEVEL return *this; @@ -2612,9 +2662,11 @@ public: return *_Res == *_Right._Res && _Pos == _Right._Pos && _Subs == _Right._Subs; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_token_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 diff --git a/stl/inc/scoped_allocator b/stl/inc/scoped_allocator index 76f95f24f36..e35dfaf2db3 100644 --- a/stl/inc/scoped_allocator +++ b/stl/inc/scoped_allocator @@ -230,7 +230,16 @@ public: template void construct(_Ty* _Ptr, _Types&&... _Args) { // construct with varying allocator styles +#if _HAS_CXX20 + _STD apply( + [_Ptr, this](auto&&... _New_args) { + _Scoped_outermost_traits::construct( + _Scoped_outermost(*this), _Ptr, _STD forward(_New_args)...); + }, + _STD uses_allocator_construction_args<_Ty>(inner_allocator(), _STD forward<_Types>(_Args)...)); +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _Uses_allocator_construct(_Ptr, _Scoped_outermost(*this), inner_allocator(), _STD forward<_Types>(_Args)...); +#endif // ^^^ !_HAS_CXX20 ^^^ } template @@ -248,23 +257,23 @@ scoped_allocator_adaptor(_Outer, _Inner...) -> scoped_allocator_adaptor<_Outer, template _NODISCARD bool operator==(const scoped_allocator_adaptor<_Outer1, _Inner1, _Inner...>& _Left, - const scoped_allocator_adaptor<_Outer2, _Inner1, _Inner...>& - _Right) noexcept { // compare scoped_allocator_adaptors for equality + const scoped_allocator_adaptor<_Outer2, _Inner1, _Inner...>& _Right) noexcept { return _Left.outer_allocator() == _Right.outer_allocator() && _Left.inner_allocator() == _Right.inner_allocator(); } template -_NODISCARD bool operator==(const scoped_allocator_adaptor<_Outer1>& _Left, - const scoped_allocator_adaptor<_Outer2>& _Right) noexcept { // compare scoped_allocator_adaptors for equality +_NODISCARD bool operator==( + const scoped_allocator_adaptor<_Outer1>& _Left, const scoped_allocator_adaptor<_Outer2>& _Right) noexcept { return _Left.outer_allocator() == _Right.outer_allocator(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const scoped_allocator_adaptor<_Outer1, _Inner...>& _Left, - const scoped_allocator_adaptor<_Outer2, _Inner...>& - _Right) noexcept { // compare scoped_allocator_adaptors for equality + const scoped_allocator_adaptor<_Outer2, _Inner...>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 _STD_END #pragma pop_macro("new") diff --git a/stl/inc/semaphore b/stl/inc/semaphore index 79d2760603b..903c4800bdd 100644 --- a/stl/inc/semaphore +++ b/stl/inc/semaphore @@ -177,6 +177,7 @@ public: template _NODISCARD bool try_acquire_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); ptrdiff_t _Current = _Counter.load(memory_order_relaxed); for (;;) { while (_Current == 0) { @@ -274,6 +275,7 @@ public: template _NODISCARD bool try_acquire_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); 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 diff --git a/stl/inc/set b/stl/inc/set index 409f0b7b22a..f45bd81991d 100644 --- a/stl/inc/set +++ b/stl/inc/set @@ -180,11 +180,21 @@ _NODISCARD bool operator==(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -205,6 +215,7 @@ template _NODISCARD bool operator>=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(set<_Kty, _Pr, _Alloc>& _Left, set<_Kty, _Pr, _Alloc>& _Right) noexcept(noexcept(_Left.swap(_Right))) { @@ -352,11 +363,21 @@ _NODISCARD bool operator==(const multiset<_Kty, _Pr, _Alloc>& _Left, const multi && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -377,6 +398,7 @@ template _NODISCARD bool operator>=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(multiset<_Kty, _Pr, _Alloc>& _Left, multiset<_Kty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/shared_mutex b/stl/inc/shared_mutex index 4509b0cb62b..a735ca0938c 100644 --- a/stl/inc/shared_mutex +++ b/stl/inc/shared_mutex @@ -114,8 +114,11 @@ public: } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock until time point + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 auto _Not_writing = [this] { return !_Writing; }; auto _Zero_readers = [this] { return _Readers == 0; }; unique_lock _Lock(_Mymtx); @@ -166,8 +169,8 @@ public: } template - _NODISCARD bool try_lock_shared_for( - const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock non-exclusive for relative time + _NODISCARD bool try_lock_shared_for(const chrono::duration<_Rep, _Period>& _Rel_time) { + // try to lock non-exclusive for relative time return try_lock_shared_until(_To_absolute_time(_Rel_time)); } @@ -186,8 +189,11 @@ public: } template - _NODISCARD bool try_lock_shared_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock non-exclusive until absolute time + _NODISCARD bool try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock non-exclusive until absolute time +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Try_lock_shared_until(_Abs_time); } @@ -257,6 +263,9 @@ public: _NODISCARD_CTOR shared_lock(mutex_type& _Mtx, const chrono::time_point<_Clock, _Duration>& _Abs_time) : _Pmtx(_STD addressof(_Mtx)), _Owns(_Mtx.try_lock_shared_until(_Abs_time)) { // construct with mutex and try to lock until absolute time +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 } ~shared_lock() noexcept { @@ -298,16 +307,19 @@ public: } template - _NODISCARD bool try_lock_for( - const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock the mutex for _Rel_time + _NODISCARD bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time) { + // try to lock the mutex for _Rel_time _Validate(); _Owns = _Pmtx->try_lock_shared_for(_Rel_time); return _Owns; } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock the mutex until _Abs_time + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock the mutex until _Abs_time +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 _Validate(); _Owns = _Pmtx->try_lock_shared_until(_Abs_time); return _Owns; diff --git a/stl/inc/source_location b/stl/inc/source_location new file mode 100644 index 00000000000..a9e6154c44f --- /dev/null +++ b/stl/inc/source_location @@ -0,0 +1,66 @@ +// source_location standard header (core) + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _SOURCE_LOCATION_ +#define _SOURCE_LOCATION_ +#include +#if _STL_COMPILER_PREPROCESSOR +#ifndef __cpp_consteval +#pragma message("The contents of are available only with C++20 consteval support.") +#else // ^^^ !defined(__cpp_consteval) / defined(__cpp_consteval) vvv + +#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 source_location { + _NODISCARD static consteval source_location current(const uint_least32_t _Line_ = __builtin_LINE(), + const uint_least32_t _Column_ = __builtin_COLUMN(), const char* const _File_ = __builtin_FILE(), + const char* const _Function_ = __builtin_FUNCTION()) noexcept { + source_location _Result; + _Result._Line = _Line_; + _Result._Column = _Column_; + _Result._File = _File_; + _Result._Function = _Function_; + return _Result; + } + + _NODISCARD_CTOR constexpr source_location() noexcept = default; + + _NODISCARD constexpr uint_least32_t line() const noexcept { + return _Line; + } + _NODISCARD constexpr uint_least32_t column() const noexcept { + return _Column; + } + _NODISCARD constexpr const char* file_name() const noexcept { + return _File; + } + _NODISCARD constexpr const char* function_name() const noexcept { + return _Function; + } + +private: + uint_least32_t _Line{}; + uint_least32_t _Column{}; + const char* _File = ""; + const char* _Function = ""; +}; +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // !defined(__cpp_consteval) +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _SOURCE_LOCATION_ diff --git a/stl/inc/stack b/stl/inc/stack index 55889064e87..2cc4d964f56 100644 --- a/stl/inc/stack +++ b/stl/inc/stack @@ -52,6 +52,14 @@ _NODISCARD bool operator>=(const stack<_Ty, _Container>& _Left, const stack<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class stack { public: @@ -140,6 +148,12 @@ public: friend bool operator>= <>(const stack&, const stack&); // clang-format on +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const stack<_Ty2, _Container2>&, const stack<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts + protected: _Container c{}; }; diff --git a/stl/inc/syncstream b/stl/inc/syncstream new file mode 100644 index 00000000000..434a1cd7a7f --- /dev/null +++ b/stl/inc/syncstream @@ -0,0 +1,381 @@ +// syncstream standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _SYNCSTREAM_ +#define _SYNCSTREAM_ +#include +#if _STL_COMPILER_PREPROCESSOR +#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") +#pragma push_macro("emit") +#undef new +#undef emit + +_EXTERN_C +_NODISCARD _STD shared_mutex* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept; +void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept; +_END_EXTERN_C + +_STD_BEGIN + +// CLASS TEMPLATE _Basic_syncbuf_impl +template +class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> { +public: + void set_emit_on_sync(const bool _Val) noexcept { + _Emit_on_sync = _Val; + } + + virtual bool _Do_emit() = 0; + +#ifdef _ENABLE_STL_INTERNAL_CHECK + _NODISCARD bool _Stl_internal_check_get_emit_on_sync() const noexcept { + return _Emit_on_sync; + } +#endif // _ENABLE_STL_INTERNAL_CHECK + +protected: + using _Mysb = basic_streambuf<_Elem, _Traits>; + + _Basic_syncbuf_impl() = default; + + _Basic_syncbuf_impl(_Basic_syncbuf_impl&& _Right) { + _Swap(_Right); + } + + void _Swap(_Basic_syncbuf_impl& _Right) { // see LWG-3498 regarding noexcept + _Mysb::swap(_Right); + _STD swap(_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Sync_recorded, _Right._Sync_recorded); + } + + bool _Emit_on_sync{false}; + bool _Sync_recorded{false}; +}; + +// CLASS TEMPLATE basic_syncbuf +template +class basic_syncbuf : public _Basic_syncbuf_impl<_Elem, _Traits> { +public: + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using allocator_type = _Alloc; + using streambuf_type = basic_streambuf<_Elem, _Traits>; + + using _Mybase = _Basic_syncbuf_impl<_Elem, _Traits>; + using _Pointer = typename allocator_traits<_Alloc>::pointer; + using _Size_type = typename allocator_traits<_Alloc>::size_type; + + using _Mybase::set_emit_on_sync; + + basic_syncbuf() = default; + + explicit basic_syncbuf(streambuf_type* _Strbuf) : basic_syncbuf(_Strbuf, _Alloc{}) {} + + basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) + : _Wrapped(_Strbuf), _Mypair{_One_then_variadic_args_t{}, _Al_, nullptr} { + if (_Wrapped) { + auto& _Mutex = _Get_mutex(); + _Mutex = __std_acquire_shared_mutex_for_instance(_Wrapped); + if (!_Mutex) { + _Xbad_alloc(); + } + } + _Init(); + } + + basic_syncbuf(basic_syncbuf&& _Right) : _Mypair{_One_then_variadic_args_t{}, _STD move(_Right._Getal()), nullptr} { + _Swap_except_al(_Right); + } + + ~basic_syncbuf() { + _Emit(); + _Tidy(); + } + + basic_syncbuf& operator=(basic_syncbuf&& _Right) { // see LWG-3498 regarding noexcept + emit(); + if (this != _STD addressof(_Right)) { + _Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{}); + } + return *this; + } + + void swap(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept + if (this != _STD addressof(_Right)) { + _Pocs(_Getal(), _Right._Getal()); + _Swap_except_al(_Right); + } + } + + bool emit() { + if (!_Wrapped) { + return false; + } + + bool _Result = true; + const _Size_type _Data_size = _Get_data_size(); + _Elem* const _Begin_seq_ptr = streambuf_type::pbase(); + if (_Data_size > 0 || _Mybase::_Sync_recorded) { + scoped_lock _Guard(*_Get_mutex()); + + if (_Data_size > 0 + && _Data_size + != static_cast<_Size_type>( + _Wrapped->sputn(_Begin_seq_ptr, static_cast(_Data_size)))) { + _Result = false; + } + + if (_Mybase::_Sync_recorded) { + if (_Wrapped->pubsync() == -1) { + _Result = false; + } + } + } + _Mybase::_Sync_recorded = false; + streambuf_type::setp(_Begin_seq_ptr, streambuf_type::epptr()); // reset written data + return _Result; + } + + _NODISCARD streambuf_type* get_wrapped() const noexcept { + return _Wrapped; + } + + _NODISCARD allocator_type get_allocator() const noexcept { + return _Mypair._Get_first(); + } + +protected: + virtual int sync() override { + _Mybase::_Sync_recorded = true; + + if (_Mybase::_Emit_on_sync) { + if (!emit()) { + return -1; + } + } + return 0; + } + + virtual int_type overflow(int_type _Current_elem) override { + if (!_Wrapped) { + return _Traits::eof(); + } + const bool _Chk_eof = _Traits::eq_int_type(_Current_elem, _Traits::eof()); + if (_Chk_eof) { + return _Traits::not_eof(_Current_elem); + } + + auto& _Al = _Getal(); + const _Size_type _Buf_size = _Get_buffer_size(); + const _Size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al); + if (_Buf_size == _Max_allocation) { + return _Traits::eof(); + } + + const _Size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); + const _Elem* const _Old_ptr = streambuf_type::pbase(); + const _Size_type _Old_data_size = _Get_data_size(); + + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_New_capacity)); + _Traits::copy(_New_ptr, _Old_ptr, _Old_data_size); + + streambuf_type::setp(_New_ptr, _New_ptr + _Old_data_size, _New_ptr + _New_capacity); + streambuf_type::sputc(_Traits::to_char_type(_Current_elem)); + + return _Current_elem; + } + +private: + static constexpr _Size_type _Min_size = 32; // constant for minimum buffer size + + void _Init() { + _Elem* const _New_ptr = _Unfancy(_Getal().allocate(_Min_size)); + streambuf_type::setp(_New_ptr, _New_ptr + _Min_size); + } + + void _Tidy() noexcept { + const _Size_type _Buf_size = _Get_buffer_size(); + if (0 < _Buf_size) { + _Getal().deallocate(_Refancy<_Pointer>(streambuf_type::pbase()), _Buf_size); + } + + streambuf_type::setp(nullptr, nullptr, nullptr); + if (_Wrapped) { + __std_release_shared_mutex_for_instance(_Wrapped); + _Wrapped = nullptr; + _Get_mutex() = nullptr; + } + } + + void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) { // see LWG-3498 regarding noexcept + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + _Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) { // see LWG-3498 regarding noexcept + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + _Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { // see LWG-3498 regarding noexcept + auto& _Al = _Getal(); + if (_Al == _Right._Getal()) { + _Move_assign(_STD move(_Right), _Equal_allocators{}); + } else { + _Tidy(); + + const _Size_type _Right_buf_size = _Right._Get_buffer_size(); + const _Size_type _Right_data_size = _Right._Get_data_size(); + + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Right_buf_size)); + _Traits::copy(_New_ptr, _Right.pbase(), _Right_data_size); + + streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size); + _STD swap(streambuf_type::_Plocale, _Right._Plocale); + + _STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Mybase::_Sync_recorded, _Right._Sync_recorded); + _STD swap(_Wrapped, _Right._Wrapped); + _STD swap(_Get_mutex(), _Right._Get_mutex()); + + _Right._Tidy(); + } + } + + void _Swap_except_al(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept + _Mybase::_Swap(_Right); + _STD swap(_Wrapped, _Right._Wrapped); + _STD swap(_Get_mutex(), _Right._Get_mutex()); + } + + virtual bool _Do_emit() override { + return emit(); + } + + bool _Emit() noexcept { + _TRY_BEGIN + return emit(); + _CATCH_ALL + return false; + _CATCH_END + } + + _NODISCARD static constexpr _Size_type _Calculate_growth( + const _Size_type _Oldsize, const _Size_type _Newsize, const _Size_type _Maxsize) { + if (_Oldsize > _Maxsize - _Oldsize / 2) { + return _Maxsize; // geometric growth would overflow + } + + const _Size_type _Geometric = _Oldsize + _Oldsize / 2; + + if (_Geometric < _Newsize) { + return _Newsize; // geometric growth would be insufficient + } + + return _Geometric; // geometric growth is sufficient + } + + _NODISCARD _Size_type _Get_data_size() const noexcept { + return static_cast<_Size_type>(streambuf_type::pptr() - streambuf_type::pbase()); + } + + _NODISCARD _Size_type _Get_buffer_size() const noexcept { + return static_cast<_Size_type>(streambuf_type::epptr() - streambuf_type::pbase()); + } + + _NODISCARD _Alloc& _Getal() noexcept { + return _Mypair._Get_first(); + } + + _NODISCARD shared_mutex*& _Get_mutex() noexcept { + return _Mypair._Myval2; + } + + streambuf_type* _Wrapped{nullptr}; + _Compressed_pair<_Alloc, shared_mutex*> _Mypair{_Zero_then_variadic_args_t{}, nullptr}; +}; + +template +void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, + basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) { // see LWG-3498 regarding noexcept + _Left.swap(_Right); +} + +// CLASS TEMPLATE basic_osyncstream +template +class basic_osyncstream : public basic_ostream<_Elem, _Traits> { +public: + using char_type = _Elem; + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using traits_type = _Traits; + using allocator_type = _Alloc; + using streambuf_type = basic_streambuf<_Elem, _Traits>; + using syncbuf_type = basic_syncbuf<_Elem, _Traits, _Alloc>; + + using _Mybase = basic_ostream<_Elem, _Traits>; + + basic_osyncstream(streambuf_type* _Strbuf, const _Alloc& _Al) + : _Mybase(_STD addressof(_Sync_buf)), _Sync_buf(_Strbuf, _Al) {} + + explicit basic_osyncstream(streambuf_type* _Strbuf) : basic_osyncstream(_Strbuf, _Alloc{}) {} + + basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr, const _Alloc& _Al) + : basic_osyncstream(_Ostr.rdbuf(), _Al) {} + + explicit basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr) : basic_osyncstream(_Ostr, _Alloc{}) {} + + basic_osyncstream(basic_osyncstream&& _Right) noexcept + : _Mybase(_STD move(_Right)), _Sync_buf(_STD move(_Right._Sync_buf)) { + _Mybase::set_rdbuf(_STD addressof(_Sync_buf)); + } + + ~basic_osyncstream() = default; + + basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default; + + void emit() { + if (!_Sync_buf.emit()) { + _Mybase::setstate(ios::badbit); + } + } + _NODISCARD streambuf_type* get_wrapped() const noexcept { + return _Sync_buf.get_wrapped(); + } + _NODISCARD syncbuf_type* rdbuf() const noexcept { + return const_cast(_STD addressof(_Sync_buf)); + } + +private: + syncbuf_type _Sync_buf; +}; + +_STD_END + +#pragma pop_macro("emit") +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _HAS_CXX20 +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _SYNCSTREAM_ diff --git a/stl/inc/system_error b/stl/inc/system_error index 42b03990542..a9b527db251 100644 --- a/stl/inc/system_error +++ b/stl/inc/system_error @@ -16,7 +16,11 @@ #include #ifndef _M_CEE_PURE #include -#endif +#endif // _M_CEE_PURE + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -90,13 +94,22 @@ public: return _Addr == _Right._Addr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const error_category& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD strong_ordering operator<=>(const error_category& _Right) const noexcept { + return compare_three_way{}(_Addr, _Right._Addr); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD bool operator<(const error_category& _Right) const noexcept { return _Addr < _Right._Addr; } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ error_category(const error_category&) = delete; error_category& operator=(const error_category&) = delete; @@ -173,6 +186,21 @@ public: return _System_error_equal(_Left, _Right); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); + } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +#if !_HAS_CXX20 _NODISCARD friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { return _System_error_equal(_Right, _Left); } @@ -188,11 +216,7 @@ public: _NODISCARD friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept { return !_System_error_equal(_Right, _Left); } - - _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); - } +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -249,22 +273,36 @@ public: return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } - _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { - return !(_Left == _Right); +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>( + const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); } - +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD friend bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() < _Right.category() || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +#if !_HAS_CXX20 + _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { + return !(_Left == _Right); + } +#endif // !_HAS_CXX20 // We grant friendship to the operators from error_code here to allow is_error_code_enum_v but not // is_error_condition_enum_v enums to be compared directly with error_condition; for example: // io_errc::stream == make_error_condition(errc::out_of_memory) friend bool operator==(const error_code& _Left, const error_condition& _Right) noexcept; +#if !_HAS_CXX20 friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept; friend bool operator!=(const error_code& _Left, const error_condition& _Right) noexcept; friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept; +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -285,14 +323,42 @@ _NODISCARD inline bool operator==(const error_code& _Left, const error_condition return _Left.category().equivalent(_Left.value(), _Right) || _Right.category().equivalent(_Left, _Right.value()); } -_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { - return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); -} - _NODISCARD inline bool operator==(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts +_NODISCARD inline strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} + +_NODISCARD inline strong_ordering operator<=>(const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} + +_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + +#if !_HAS_CXX20 +_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { + return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); +} + _NODISCARD inline bool operator!=(const error_code& _Left, const error_code& _Right) noexcept { return !(_Left == _Right); } @@ -308,16 +374,7 @@ _NODISCARD inline bool operator!=(const error_condition& _Left, const error_code _NODISCARD inline bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { return !(_Left == _Right); } - -_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} - -_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS // VIRTUALS FOR error_category diff --git a/stl/inc/thread b/stl/inc/thread index 2d46b78936d..0d7be907f61 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -14,6 +14,7 @@ #include #include #if _HAS_CXX20 +#include #include #endif // _HAS_CXX20 @@ -185,6 +186,9 @@ namespace this_thread { template void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 for (;;) { const auto _Now = _Clock::now(); if (_Abs_time <= _Now) { @@ -215,7 +219,11 @@ private: friend thread::id thread::get_id() const noexcept; friend thread::id this_thread::get_id() noexcept; friend bool operator==(thread::id _Left, thread::id _Right) noexcept; +#if _HAS_CXX20 + friend strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv friend bool operator<(thread::id _Left, thread::id _Right) noexcept; +#endif // !_HAS_CXX20 template friend basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id); friend hash; @@ -237,6 +245,11 @@ _NODISCARD inline bool operator==(thread::id _Left, thread::id _Right) noexcept return _Left._Id == _Right._Id; } +#if _HAS_CXX20 +_NODISCARD inline strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept { + return _Left._Id <=> _Right._Id; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD inline bool operator!=(thread::id _Left, thread::id _Right) noexcept { return !(_Left == _Right); } @@ -256,6 +269,7 @@ _NODISCARD inline bool operator>(thread::id _Left, thread::id _Right) noexcept { _NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept { return !(_Left < _Right); } +#endif // !_HAS_CXX20 template basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) { diff --git a/stl/inc/tuple b/stl/inc/tuple index d0734801e84..388a8b237e9 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -8,6 +8,9 @@ #define _TUPLE_ #include #if _STL_COMPILER_PREPROCESSOR +#ifdef __cpp_lib_concepts +#include +#endif // __cpp_lib_concepts #include #include @@ -233,9 +236,15 @@ public: return true; } - constexpr bool _Less(const tuple&) const noexcept { +#ifdef __cpp_lib_concepts + _NODISCARD constexpr strong_ordering _Three_way_compare(const tuple&) const noexcept { + return strong_ordering::equal; + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD constexpr bool _Less(const tuple&) const noexcept { return false; } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ }; template @@ -673,11 +682,23 @@ public: return _Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest()); } +#ifdef __cpp_lib_concepts + template , // TRANSITION, DevCom-1344701 + _Synth_three_way_result<_Rest, _Other>...>> // (should be a normal or trailing return type) + _NODISCARD constexpr _Ret _Three_way_compare(const tuple<_First, _Other...>& _Right) const { + if (auto _Result = _Synth_three_way{}(_Myfirst._Val, _Right._Myfirst._Val); _Result != 0) { + return _Result; + } + return _Mybase::_Three_way_compare(_Right._Get_rest()); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template - constexpr bool _Less(const tuple<_Other...>& _Right) const { + _NODISCARD constexpr bool _Less(const tuple<_Other...>& _Right) const { return _Myfirst._Val < _Right._Myfirst._Val || (!(_Right._Myfirst._Val < _Myfirst._Val) && _Mybase::_Less(_Right._Get_rest())); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template friend constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept; @@ -733,10 +754,20 @@ _NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple return _Left._Equals(_Right); } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Types1, _Types2>...> operator<=>( + const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { + static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes"); + return _Left._Three_way_compare(_Right); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { @@ -758,6 +789,7 @@ template _NODISCARD constexpr bool operator<=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Right < _Left); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template ...>, int> = 0> _CONSTEXPR20 void swap(tuple<_Types...>& _Left, tuple<_Types...>& _Right) noexcept(noexcept(_Left.swap(_Right))) { diff --git a/stl/inc/type_traits b/stl/inc/type_traits index c1a030758bb..e1ef10aafc1 100644 --- a/stl/inc/type_traits +++ b/stl/inc/type_traits @@ -1804,6 +1804,38 @@ inline constexpr bool is_nothrow_invocable_r_v = _Select_invoke_traits<_Callable, _Args...>::template _Is_nothrow_invocable_r<_Rx>::value; #endif // _HAS_CXX17 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, VSO-1268984 +#ifndef __clang__ // TRANSITION, LLVM-48860 +// STRUCT TEMPLATE is_layout_compatible +template +struct is_layout_compatible : bool_constant<__is_layout_compatible(_Ty1, _Ty2)> {}; + +template +inline constexpr bool is_layout_compatible_v = __is_layout_compatible(_Ty1, _Ty2); + +// STRUCT TEMPLATE is_pointer_interconvertible_base_of +template +struct is_pointer_interconvertible_base_of : bool_constant<__is_pointer_interconvertible_base_of(_Base, _Derived)> {}; + +template +inline constexpr bool is_pointer_interconvertible_base_of_v = __is_pointer_interconvertible_base_of(_Base, _Derived); + +// FUNCTION TEMPLATE is_pointer_interconvertible_with_class +template +_NODISCARD constexpr bool is_pointer_interconvertible_with_class(_MemberTy _ClassTy::*_Pm) noexcept { + return __is_pointer_interconvertible_with_class(_ClassTy, _Pm); +} + +// FUNCTION TEMPLATE is_corresponding_member +template +_NODISCARD constexpr bool is_corresponding_member(_MemberTy1 _ClassTy1::*_Pm1, _MemberTy2 _ClassTy2::*_Pm2) noexcept { + return __is_corresponding_member(_ClassTy1, _ClassTy2, _Pm1, _Pm2); +} +#endif // __clang__ +#endif // __EDG__ +#endif // _HAS_CXX20 + // ALIAS TEMPLATE _Weak_types template struct _Function_args {}; // determine whether _Ty is a function diff --git a/stl/inc/typeindex b/stl/inc/typeindex index 0aa47537679..68d02e809f7 100644 --- a/stl/inc/typeindex +++ b/stl/inc/typeindex @@ -38,9 +38,17 @@ public: return *_Tptr == *_Right._Tptr; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const type_index& _Right) const noexcept { + return *_Tptr == *_Right._Tptr ? strong_ordering::equal + : _Tptr->before(*_Right._Tptr) ? strong_ordering::less + : strong_ordering::greater; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const type_index& _Right) const noexcept { return !(*this == _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD bool operator<(const type_index& _Right) const noexcept { return _Tptr->before(*_Right._Tptr); diff --git a/stl/inc/unordered_map b/stl/inc/unordered_map index 0a564aaba2e..c5633ff84a4 100644 --- a/stl/inc/unordered_map +++ b/stl/inc/unordered_map @@ -468,11 +468,13 @@ _NODISCARD bool operator==(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multimap template , class _Keyeq = equal_to<_Kty>, @@ -758,11 +760,13 @@ _NODISCARD bool operator==(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/unordered_set b/stl/inc/unordered_set index 1f32080e230..2a7125567b6 100644 --- a/stl/inc/unordered_set +++ b/stl/inc/unordered_set @@ -322,11 +322,13 @@ _NODISCARD bool operator==(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _ return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multiset template , class _Keyeq = equal_to<_Kty>, class _Alloc = allocator<_Kty>> @@ -584,11 +586,13 @@ _NODISCARD bool operator==(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/utility b/stl/inc/utility index cc191b85356..a909684b495 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -347,10 +347,22 @@ _NODISCARD constexpr bool operator==(const pair<_Ty1, _Ty2>& _Left, const pair<_ return _Left.first == _Right.first && _Left.second == _Right.second; } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Ty1>, _Synth_three_way_result<_Ty2>> + operator<=>(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { + if (auto _Result = _Synth_three_way{}(_Left.first, _Right.first); _Result != 0) { + return _Result; + } + return _Synth_three_way{}(_Left.second, _Right.second); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { @@ -371,6 +383,7 @@ template _NODISCARD constexpr bool operator>=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ // ALIAS TEMPLATE _Unrefwrap_t template diff --git a/stl/inc/valarray b/stl/inc/valarray index 63b69f34fa4..5f5a6ab33fc 100644 --- a/stl/inc/valarray +++ b/stl/inc/valarray @@ -1373,6 +1373,12 @@ public: return _Stride; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const slice& _Left, const slice& _Right) noexcept /* strengthened */ { + return _Left.start() == _Right.start() && _Left.size() == _Right.size() && _Left.stride() == _Right.stride(); + } +#endif // _HAS_CXX20 + protected: size_t _Start = 0; // the starting offset size_t _Len = 0; // the number of elements diff --git a/stl/inc/variant b/stl/inc/variant index bccf5add82a..036e1d602aa 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -967,10 +967,25 @@ using _Variant_destroy_layer = conditional_t + +#if _HAS_CXX20 +// build Ti x[] = {std::forward(t)}; +template +auto _Construct_array(_TargetType(&&)[1]) -> _Meta_list, _TargetType>; + +template +using _Variant_type_resolver = decltype(_Construct_array<_Idx, _TargetType>({_STD declval<_InitializerType>()})); +#endif // _HAS_CXX20 + +template struct _Variant_init_single_overload { - using _FTy = _Meta_list, _Ty> (*)(_Ty); - operator _FTy(); +#if _HAS_CXX20 + template + auto operator()(_TargetType, _InitializerType&&) -> _Variant_type_resolver<_Idx, _TargetType, _InitializerType>; +#else // _HAS_CXX20 + template + auto operator()(_TargetType, _InitializerType&&) -> _Meta_list, _TargetType>; +#endif // _HAS_CXX20 }; template @@ -978,7 +993,9 @@ struct _Variant_init_overload_set_; template struct _Variant_init_overload_set_, _Types...> - : _Variant_init_single_overload<_Indices, _Types>... {}; + : _Variant_init_single_overload<_Indices, _Types>... { + using _Variant_init_single_overload<_Indices, _Types>::operator()...; +}; template using _Variant_init_overload_set = _Variant_init_overload_set_, _Types...>; @@ -987,11 +1004,12 @@ template struct _Variant_init_helper {}; // failure case (has no member "type") template -struct _Variant_init_helper{}(_STD declval<_Ty>()))>, _Ty, +struct _Variant_init_helper< + void_t{}(_STD declval<_Ty>(), _STD declval<_Ty>()))>, _Ty, _Types...> { // perform overload resolution to determine the unique alternative that should be initialized in // variant<_Types...> from an argument expression with type and value category _Ty - using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>())); + using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>(), _STD declval<_Ty>())); }; template // extract the type from _Variant_init_helper @@ -1365,13 +1383,14 @@ _NODISCARD constexpr add_pointer_t get_if( } // RELATIONAL OPERATORS [variant.relops] -template -struct _Variant_relop_visitor { // evaluate _Op with the contained value of two variants that hold the same alternative +template +struct _Variant_relop_visitor2 { // evaluate _Op with the contained value of two variants that hold the same alternative const _Variant_storage<_Types...>& _Left; template - _NODISCARD constexpr bool operator()(_Tagged _Right) const noexcept( - disjunction_v, is_nothrow_invocable_r>) { + _NODISCARD constexpr _Result operator()(_Tagged _Right) const + noexcept(disjunction_v, + is_nothrow_invocable_r<_Result, _Op, const _Ty&, const _Ty&>>) { // determine the relationship between the stored values of _Left and _Right // pre: _Left.index() == _Idx && _Right.index() == _Idx if constexpr (_Idx != variant_npos) { @@ -1387,7 +1406,7 @@ template _NODISCARD constexpr bool operator==(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments are both valueless or contain equal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() == _Right_index && _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1397,7 +1416,7 @@ template _NODISCARD constexpr bool operator!=(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments have different active alternatives or contain unequal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() != _Right_index || _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1408,7 +1427,7 @@ _NODISCARD constexpr bool operator<(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a lesser index(), or equal index() and lesser // contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1421,7 +1440,7 @@ _NODISCARD constexpr bool operator>(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a greater index(), or equal index() and // greater contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1434,7 +1453,7 @@ _NODISCARD constexpr bool operator<=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is less than _Right's, or equal and // _Left contains a value less than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1447,7 +1466,7 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is greater than _Right's, or equal and // _Left contains a value greater than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1455,6 +1474,27 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari && _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()})); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (three_way_comparable<_Types> && ...) +_NODISCARD constexpr common_comparison_category_t...> + operator<=>(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( + conjunction_v...>, + compare_three_way, const _Types&, const _Types&>...>) /* strengthened */ { + // clang-format on + // determine the three-way comparison of _Left's and _Right's index, if equal + // return the three-way comparison of the contained values of _Left and _Right + using _Visitor = _Variant_relop_visitor2...>, _Types...>; + const size_t _Left_offset = _Left.index() + 1; + const size_t _Right_offset = _Right.index() + 1; + const auto _Offset_order = _Left_offset <=> _Right_offset; + return _Offset_order != 0 ? _Offset_order + : _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()}); +} +#endif // __cpp_lib_concepts + // VISITATION [variant.visit] template inline constexpr size_t _Variant_total_states = @@ -1698,6 +1738,12 @@ struct monostate {}; _NODISCARD constexpr bool operator==(monostate, monostate) noexcept { return true; } + +#if _HAS_CXX20 +_NODISCARD constexpr strong_ordering operator<=>(monostate, monostate) noexcept { + return strong_ordering::equal; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(monostate, monostate) noexcept { return false; } @@ -1713,6 +1759,7 @@ _NODISCARD constexpr bool operator<=(monostate, monostate) noexcept { _NODISCARD constexpr bool operator>=(monostate, monostate) noexcept { return true; } +#endif // !_HAS_CXX20 // SPECIALIZED ALGORITHMS [variant.specalg] template _Adopt(_Pvector); } - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _Vector_const_iterator& operator=(const _Vector_const_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Ptr, "can't dereference value-initialized vector iterator"); @@ -54,7 +57,7 @@ public: return *_Ptr; } - _NODISCARD pointer operator->() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Ptr, "can't dereference value-initialized vector iterator"); @@ -65,7 +68,7 @@ public: return _Ptr; } - _Vector_const_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _Vector_const_iterator& operator++() noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Ptr, "can't increment value-initialized vector iterator"); @@ -76,13 +79,13 @@ public: return *this; } - _Vector_const_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _Vector_const_iterator operator++(int) noexcept { _Vector_const_iterator _Tmp = *this; ++*this; return _Tmp; } - _Vector_const_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _Vector_const_iterator& operator--() noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Ptr, "can't decrement value-initialized vector iterator"); @@ -93,13 +96,13 @@ public: return *this; } - _Vector_const_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _Vector_const_iterator operator--(int) noexcept { _Vector_const_iterator _Tmp = *this; --*this; return _Tmp; } - void _Verify_offset(const difference_type _Off) const noexcept { + _CONSTEXPR20_CONTAINER void _Verify_offset(const difference_type _Off) const noexcept { #if _ITERATOR_DEBUG_LEVEL == 0 (void) _Off; #else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ // vvv _ITERATOR_DEBUG_LEVEL != 0 vvv @@ -115,40 +118,48 @@ public: #endif // _ITERATOR_DEBUG_LEVEL == 0 } - _Vector_const_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vector_const_iterator& operator+=(const difference_type _Off) noexcept { _Verify_offset(_Off); _Ptr += _Off; return *this; } - _NODISCARD _Vector_const_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vector_const_iterator operator+(const difference_type _Off) const noexcept { _Vector_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _Vector_const_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vector_const_iterator& operator-=(const difference_type _Off) noexcept { return *this += -_Off; } - _NODISCARD _Vector_const_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vector_const_iterator operator-(const difference_type _Off) const noexcept { _Vector_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD difference_type operator-(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _Vector_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr - _Right._Ptr; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } - _NODISCARD bool operator==(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const _Vector_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _Vector_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Vector_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -169,8 +180,10 @@ public: _NODISCARD bool operator>=(const _Vector_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 - void _Compat(const _Vector_const_iterator& _Right) const noexcept { // test for compatible iterator pair + _CONSTEXPR20_CONTAINER void _Compat(const _Vector_const_iterator& _Right) const noexcept { + // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL == 0 (void) _Right; #else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ // vvv _ITERATOR_DEBUG_LEVEL != 0 vvv @@ -179,7 +192,8 @@ public: } #if _ITERATOR_DEBUG_LEVEL != 0 - friend void _Verify_range(const _Vector_const_iterator& _First, const _Vector_const_iterator& _Last) noexcept { + friend _CONSTEXPR20_CONTAINER void _Verify_range( + const _Vector_const_iterator& _First, const _Vector_const_iterator& _Last) noexcept { _STL_VERIFY(_First._Getcont() == _Last._Getcont(), "vector iterators in range are from different containers"); _STL_VERIFY(_First._Ptr <= _Last._Ptr, "vector iterator range transposed"); } @@ -187,11 +201,11 @@ public: using _Prevent_inheriting_unwrap = _Vector_const_iterator; - _NODISCARD const value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const value_type* _Unwrapped() const noexcept { return _Unfancy(_Ptr); } - void _Seek_to(const value_type* _It) noexcept { + _CONSTEXPR20_CONTAINER void _Seek_to(const value_type* _It) noexcept { _Ptr = _Refancy<_Tptr>(const_cast(_It)); } @@ -199,7 +213,7 @@ public: }; template -_NODISCARD _Vector_const_iterator<_Myvec> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _Vector_const_iterator<_Myvec> operator+( typename _Vector_const_iterator<_Myvec>::difference_type _Off, _Vector_const_iterator<_Myvec> _Next) noexcept { return _Next += _Off; } @@ -248,71 +262,83 @@ public: using _Mybase::_Mybase; - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _Vector_iterator& operator=(const _Vector_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { return const_cast(_Mybase::operator*()); } - _NODISCARD pointer operator->() const noexcept { - return _Const_cast(_Mybase::operator->()); + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + const auto _Mycont = static_cast(this->_Getcont()); + _STL_VERIFY(this->_Ptr, "can't dereference value-initialized vector iterator"); + _STL_VERIFY(_Mycont->_Myfirst <= this->_Ptr && this->_Ptr < _Mycont->_Mylast, + "can't dereference out of range vector iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return this->_Ptr; } - _Vector_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _Vector_iterator& operator++() noexcept { _Mybase::operator++(); return *this; } - _Vector_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _Vector_iterator operator++(int) noexcept { _Vector_iterator _Tmp = *this; _Mybase::operator++(); return _Tmp; } - _Vector_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _Vector_iterator& operator--() noexcept { _Mybase::operator--(); return *this; } - _Vector_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _Vector_iterator operator--(int) noexcept { _Vector_iterator _Tmp = *this; _Mybase::operator--(); return _Tmp; } - _Vector_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vector_iterator& operator+=(const difference_type _Off) noexcept { _Mybase::operator+=(_Off); return *this; } - _NODISCARD _Vector_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vector_iterator operator+(const difference_type _Off) const noexcept { _Vector_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _Vector_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vector_iterator& operator-=(const difference_type _Off) noexcept { _Mybase::operator-=(_Off); return *this; } using _Mybase::operator-; - _NODISCARD _Vector_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vector_iterator operator-(const difference_type _Off) const noexcept { _Vector_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return const_cast(_Mybase::operator[](_Off)); } using _Prevent_inheriting_unwrap = _Vector_iterator; - _NODISCARD value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER value_type* _Unwrapped() const noexcept { return _Unfancy(this->_Ptr); } }; template -_NODISCARD _Vector_iterator<_Myvec> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _Vector_iterator<_Myvec> operator+( typename _Vector_iterator<_Myvec>::difference_type _Off, _Vector_iterator<_Myvec> _Next) noexcept { return _Next += _Off; } @@ -372,19 +398,19 @@ public: using reference = value_type&; using const_reference = const value_type&; - _Vector_val() noexcept : _Myfirst(), _Mylast(), _Myend() {} + _CONSTEXPR20_CONTAINER _Vector_val() noexcept : _Myfirst(), _Mylast(), _Myend() {} - _Vector_val(pointer _First, pointer _Last, pointer _End) noexcept + _CONSTEXPR20_CONTAINER _Vector_val(pointer _First, pointer _Last, pointer _End) noexcept : _Myfirst(_First), _Mylast(_Last), _Myend(_End) {} - void _Swap_val(_Vector_val& _Right) noexcept { + _CONSTEXPR20_CONTAINER void _Swap_val(_Vector_val& _Right) noexcept { this->_Swap_proxy_and_iterators(_Right); _Swap_adl(_Myfirst, _Right._Myfirst); _Swap_adl(_Mylast, _Right._Mylast); _Swap_adl(_Myend, _Right._Myend); } - void _Take_contents(_Vector_val& _Right) noexcept { + _CONSTEXPR20_CONTAINER void _Take_contents(_Vector_val& _Right) noexcept { this->_Swap_proxy_and_iterators(_Right); _Myfirst = _Right._Myfirst; _Mylast = _Right._Mylast; @@ -402,12 +428,13 @@ public: // FUNCTION TEMPLATE _Unfancy_maybe_null template -auto _Unfancy_maybe_null(_Ptrty _Ptr) noexcept { // converts from a (potentially null) fancy pointer to a plain pointer +constexpr auto _Unfancy_maybe_null(_Ptrty _Ptr) noexcept { + // converts from a (potentially null) fancy pointer to a plain pointer return _Ptr ? _STD addressof(*_Ptr) : nullptr; } template -_Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain pointers +constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain pointers return _Ptr; } @@ -445,17 +472,18 @@ public: using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; - vector() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER vector() noexcept(is_nothrow_default_constructible_v<_Alty>) + : _Mypair(_Zero_then_variadic_args_t{}) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); } - explicit vector(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER explicit vector(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); } private: template - void _Construct_n_copies_of_ty(_CRT_GUARDOVERFLOW const size_type _Count, const _Ty2& _Val) { + _CONSTEXPR20_CONTAINER void _Construct_n_copies_of_ty(_CRT_GUARDOVERFLOW const size_type _Count, const _Ty2& _Val) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); auto& _My_data = _Mypair._Myval2; _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); @@ -470,19 +498,20 @@ private: } public: - explicit vector(_CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER explicit vector(_CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { _Construct_n_copies_of_ty(_Count, _Value_init_tag{}); } - vector(_CRT_GUARDOVERFLOW const size_type _Count, const _Ty& _Val, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER vector( + _CRT_GUARDOVERFLOW const size_type _Count, const _Ty& _Val, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { _Construct_n_copies_of_ty(_Count, _Val); } private: template - void _Range_construct_or_tidy(_Iter _First, _Iter _Last, input_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Range_construct_or_tidy(_Iter _First, _Iter _Last, input_iterator_tag) { _Tidy_guard _Guard{this}; for (; _First != _Last; ++_First) { emplace_back(*_First); // performance note: emplace_back()'s strong guarantee is unnecessary here @@ -492,7 +521,7 @@ private: } template - void _Range_construct_or_tidy(_Iter _First, _Iter _Last, forward_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Range_construct_or_tidy(_Iter _First, _Iter _Last, forward_iterator_tag) { const auto _Count = _Convert_size(static_cast(_STD distance(_First, _Last))); if (_Count != 0) { _Buy_nonzero(_Count); @@ -505,7 +534,8 @@ private: public: template , int> = 0> - vector(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER vector(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Adl_verify_range(_First, _Last); @@ -513,14 +543,15 @@ public: _Proxy._Release(); } - vector(initializer_list<_Ty> _Ilist, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER vector(initializer_list<_Ty> _Ilist, const _Alloc& _Al = _Alloc()) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Range_construct_or_tidy(_Ilist.begin(), _Ilist.end(), random_access_iterator_tag{}); _Proxy._Release(); } - vector(const vector& _Right) + _CONSTEXPR20_CONTAINER vector(const vector& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); auto& _My_data = _Mypair._Myval2; @@ -538,7 +569,7 @@ public: _Proxy._Release(); } - vector(const vector& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER vector(const vector& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); auto& _My_data = _Mypair._Myval2; const auto& _Right_data = _Right._Mypair._Myval2; @@ -556,11 +587,13 @@ public: } private: - void _Move_construct(vector& _Right, true_type) noexcept { // move from _Right, stealing its contents + _CONSTEXPR20_CONTAINER void _Move_construct(vector& _Right, true_type) noexcept { + // move from _Right, stealing its contents _Mypair._Myval2._Take_contents(_Right._Mypair._Myval2); } - void _Move_construct(vector& _Right, false_type) { // move from _Right, possibly moving its contents + _CONSTEXPR20_CONTAINER void _Move_construct(vector& _Right, false_type) { + // move from _Right, possibly moving its contents if constexpr (!_Alty_traits::is_always_equal::value) { if (_Getal() != _Right._Getal()) { const auto& _Right_data = _Right._Mypair._Myval2; @@ -581,7 +614,7 @@ private: } public: - vector(vector&& _Right) noexcept + _CONSTEXPR20_CONTAINER vector(vector&& _Right) noexcept : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal()), _STD exchange(_Right._Mypair._Myval2._Myfirst, nullptr), _STD exchange(_Right._Mypair._Myval2._Mylast, nullptr), @@ -590,7 +623,8 @@ public: _Mypair._Myval2._Swap_proxy_and_iterators(_Right._Mypair._Myval2); } - vector(vector&& _Right, const _Alloc& _Al) noexcept(_Alty_traits::is_always_equal::value) // strengthened + _CONSTEXPR20_CONTAINER vector(vector&& _Right, const _Alloc& _Al) noexcept( + _Alty_traits::is_always_equal::value) // strengthened : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -599,13 +633,13 @@ public: } private: - void _Move_assign(vector& _Right, _Equal_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(vector& _Right, _Equal_allocators) noexcept { _Tidy(); _Pocma(_Getal(), _Right._Getal()); _Mypair._Myval2._Take_contents(_Right._Mypair._Myval2); } - void _Move_assign(vector& _Right, _Propagate_allocators) noexcept /* terminates */ { + _CONSTEXPR20_CONTAINER void _Move_assign(vector& _Right, _Propagate_allocators) noexcept /* terminates */ { _Tidy(); #if _ITERATOR_DEBUG_LEVEL != 0 if (_Getal() != _Right._Getal()) { @@ -619,7 +653,7 @@ private: _Mypair._Myval2._Take_contents(_Right._Mypair._Myval2); } - void _Move_assign(vector& _Right, _No_propagate_allocators) { + _CONSTEXPR20_CONTAINER void _Move_assign(vector& _Right, _No_propagate_allocators) { if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { @@ -665,7 +699,8 @@ private: } public: - vector& operator=(vector&& _Right) noexcept(noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { + _CONSTEXPR20_CONTAINER vector& operator=(vector&& _Right) noexcept( + noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { if (this != _STD addressof(_Right)) { _Move_assign(_Right, _Choose_pocma<_Alty>{}); } @@ -673,7 +708,7 @@ public: return *this; } - ~vector() noexcept { + _CONSTEXPR20_CONTAINER ~vector() noexcept { _Tidy(); #if _ITERATOR_DEBUG_LEVEL != 0 auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -683,7 +718,7 @@ public: private: template - decltype(auto) _Emplace_back_with_unused_capacity(_Valty&&... _Val) { + _CONSTEXPR20_CONTAINER decltype(auto) _Emplace_back_with_unused_capacity(_Valty&&... _Val) { // insert by perfectly forwarding into element at end, provide strong guarantee auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; @@ -701,7 +736,7 @@ private: public: template - decltype(auto) emplace_back(_Valty&&... _Val) { + _CONSTEXPR20_CONTAINER decltype(auto) emplace_back(_Valty&&... _Val) { // insert by perfectly forwarding into element at end, provide strong guarantee auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; @@ -717,16 +752,17 @@ public: #endif // _HAS_CXX17 } - void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee + _CONSTEXPR20_CONTAINER void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee emplace_back(_Val); } - void push_back(_Ty&& _Val) { // insert by moving into element at end, provide strong guarantee + _CONSTEXPR20_CONTAINER void push_back(_Ty&& _Val) { + // insert by moving into element at end, provide strong guarantee emplace_back(_STD move(_Val)); } template - pointer _Emplace_reallocate(const pointer _Whereptr, _Valty&&... _Val) { + _CONSTEXPR20_CONTAINER pointer _Emplace_reallocate(const pointer _Whereptr, _Valty&&... _Val) { // reallocate and insert by perfectly forwarding _Val at _Whereptr _Alty& _Al = _Getal(); auto& _My_data = _Mypair._Myval2; @@ -771,7 +807,8 @@ public: } template - iterator emplace(const_iterator _Where, _Valty&&... _Val) { // insert by perfectly forwarding _Val at _Where + _CONSTEXPR20_CONTAINER iterator emplace(const_iterator _Where, _Valty&&... _Val) { + // insert by perfectly forwarding _Val at _Where const pointer _Whereptr = _Where._Ptr; auto& _My_data = _Mypair._Myval2; const pointer _Oldlast = _My_data._Mylast; @@ -801,15 +838,16 @@ public: return _Make_iterator(_Emplace_reallocate(_Whereptr, _STD forward<_Valty>(_Val)...)); } - iterator insert(const_iterator _Where, const _Ty& _Val) { // insert _Val at _Where + _CONSTEXPR20_CONTAINER iterator insert(const_iterator _Where, const _Ty& _Val) { // insert _Val at _Where return emplace(_Where, _Val); } - iterator insert(const_iterator _Where, _Ty&& _Val) { // insert by moving _Val at _Where + _CONSTEXPR20_CONTAINER iterator insert(const_iterator _Where, _Ty&& _Val) { // insert by moving _Val at _Where return emplace(_Where, _STD move(_Val)); } - iterator insert(const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Ty& _Val) { + _CONSTEXPR20_CONTAINER iterator insert( + const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Ty& _Val) { // insert _Count * _Val at _Where const pointer _Whereptr = _Where._Ptr; @@ -883,7 +921,7 @@ public: private: template - void _Insert_range(const_iterator _Where, _Iter _First, _Iter _Last, input_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Insert_range(const_iterator _Where, _Iter _First, _Iter _Last, input_iterator_tag) { // insert input range [_First, _Last) at _Where if (_First == _Last) { return; // nothing to do, avoid invalidating iterators @@ -909,7 +947,7 @@ private: } template - void _Insert_range(const_iterator _Where, _Iter _First, _Iter _Last, forward_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Insert_range(const_iterator _Where, _Iter _First, _Iter _Last, forward_iterator_tag) { // insert forward range [_First, _Last) at _Where const pointer _Whereptr = _Where._Ptr; const auto _Count = _Convert_size(static_cast(_STD distance(_First, _Last))); @@ -1018,7 +1056,7 @@ private: public: template , int> = 0> - iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) { + _CONSTEXPR20_CONTAINER iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) { const pointer _Whereptr = _Where._Ptr; auto& _My_data = _Mypair._Myval2; const pointer _Oldfirst = _My_data._Myfirst; @@ -1034,11 +1072,12 @@ public: return _Make_iterator_offset(_Whereoff); } - iterator insert(const_iterator _Where, initializer_list<_Ty> _Ilist) { + _CONSTEXPR20_CONTAINER iterator insert(const_iterator _Where, initializer_list<_Ty> _Ilist) { return insert(_Where, _Ilist.begin(), _Ilist.end()); } - void assign(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Ty& _Val) { // assign _Newsize * _Val + _CONSTEXPR20_CONTAINER void assign(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Ty& _Val) { + // assign _Newsize * _Val auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; @@ -1066,7 +1105,8 @@ public: private: template - void _Assign_range(_Iter _First, _Iter _Last, input_iterator_tag) { // assign input range [_First, _Last) + _CONSTEXPR20_CONTAINER void _Assign_range(_Iter _First, _Iter _Last, input_iterator_tag) { + // assign input range [_First, _Last) auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; @@ -1095,7 +1135,8 @@ private: } template - void _Assign_range(_Iter _First, _Iter _Last, forward_iterator_tag) { // assign forward range [_First, _Last) + _CONSTEXPR20_CONTAINER void _Assign_range(_Iter _First, _Iter _Last, forward_iterator_tag) { + // assign forward range [_First, _Last) const auto _Newsize = _Convert_size(static_cast(_STD distance(_First, _Last))); auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; @@ -1107,54 +1148,59 @@ private: if constexpr (conjunction_v::_Trivially_copyable>, _Uses_default_construct<_Alty, _Ty*, decltype(*_First)>, _Uses_default_destroy<_Alty, _Ty*>>) { - const auto _Oldcapacity = static_cast(_Myend - _Myfirst); - if (_Newsize > _Oldcapacity) { - _Clear_and_reserve_geometric(_Newsize); - } - - _Mylast = _Refancy(_Copy_memmove(_First, _Last, _Unfancy(_Myfirst))); - } else { - auto _Oldsize = static_cast(_Mylast - _Myfirst); - - if (_Newsize > _Oldsize) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { const auto _Oldcapacity = static_cast(_Myend - _Myfirst); - if (_Newsize > _Oldcapacity) { // reallocate + if (_Newsize > _Oldcapacity) { _Clear_and_reserve_geometric(_Newsize); - _Oldsize = 0; } - // performance note: traversing [_First, _Mid) twice - const _Iter _Mid = _STD next(_First, static_cast(_Oldsize)); - _Copy_unchecked(_First, _Mid, _Myfirst); - _Mylast = _Ucopy(_Mid, _Last, _Mylast); - } else { - const pointer _Newlast = _Myfirst + _Newsize; - _Copy_unchecked(_First, _Last, _Myfirst); - _Destroy(_Newlast, _Mylast); - _Mylast = _Newlast; + _Mylast = _Refancy(_Copy_memmove(_First, _Last, _Unfancy(_Myfirst))); + return; } } + auto _Oldsize = static_cast(_Mylast - _Myfirst); + + if (_Newsize > _Oldsize) { + const auto _Oldcapacity = static_cast(_Myend - _Myfirst); + if (_Newsize > _Oldcapacity) { // reallocate + _Clear_and_reserve_geometric(_Newsize); + _Oldsize = 0; + } + + // performance note: traversing [_First, _Mid) twice + const _Iter _Mid = _STD next(_First, static_cast(_Oldsize)); + _Copy_unchecked(_First, _Mid, _Myfirst); + _Mylast = _Ucopy(_Mid, _Last, _Mylast); + } else { + const pointer _Newlast = _Myfirst + _Newsize; + _Copy_unchecked(_First, _Last, _Myfirst); + _Destroy(_Newlast, _Mylast); + _Mylast = _Newlast; + } } public: template , int> = 0> - void assign(_Iter _First, _Iter _Last) { + _CONSTEXPR20_CONTAINER void assign(_Iter _First, _Iter _Last) { _Adl_verify_range(_First, _Last); _Assign_range(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Iter_cat_t<_Iter>{}); } - void assign(initializer_list<_Ty> _Ilist) { + _CONSTEXPR20_CONTAINER void assign(initializer_list<_Ty> _Ilist) { _Assign_range(_Ilist.begin(), _Ilist.end(), random_access_iterator_tag{}); } private: - void _Copy_assign(const vector& _Right, false_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const vector& _Right, false_type) { _Pocca(_Getal(), _Right._Getal()); auto& _Right_data = _Right._Mypair._Myval2; assign(_Right_data._Myfirst, _Right_data._Mylast); } - void _Copy_assign(const vector& _Right, true_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const vector& _Right, true_type) { if (_Getal() != _Right._Getal()) { _Tidy(); _Mypair._Myval2._Reload_proxy( @@ -1165,7 +1211,7 @@ private: } public: - vector& operator=(const vector& _Right) { + _CONSTEXPR20_CONTAINER vector& operator=(const vector& _Right) { if (this != _STD addressof(_Right)) { _Copy_assign(_Right, _Choose_pocca<_Alty>{}); } @@ -1173,14 +1219,14 @@ public: return *this; } - vector& operator=(initializer_list<_Ty> _Ilist) { + _CONSTEXPR20_CONTAINER vector& operator=(initializer_list<_Ty> _Ilist) { _Assign_range(_Ilist.begin(), _Ilist.end(), random_access_iterator_tag{}); return *this; } private: template - void _Resize_reallocate(const size_type _Newsize, const _Ty2& _Val) { + _CONSTEXPR20_CONTAINER void _Resize_reallocate(const size_type _Newsize, const _Ty2& _Val) { if (_Newsize > max_size()) { _Xlength(); } @@ -1209,7 +1255,8 @@ private: } template - void _Resize(const size_type _Newsize, const _Ty2& _Val) { // trim or append elements, provide strong guarantee + _CONSTEXPR20_CONTAINER void _Resize(const size_type _Newsize, const _Ty2& _Val) { + // trim or append elements, provide strong guarantee auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; @@ -1238,18 +1285,18 @@ private: } public: - void resize(_CRT_GUARDOVERFLOW const size_type _Newsize) { + _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize) { // trim or append value-initialized elements, provide strong guarantee _Resize(_Newsize, _Value_init_tag{}); } - void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Ty& _Val) { + _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Ty& _Val) { // trim or append copies of _Val, provide strong guarantee _Resize(_Newsize, _Val); } private: - void _Reallocate_exactly(const size_type _Newcapacity) { + _CONSTEXPR20_CONTAINER void _Reallocate_exactly(const size_type _Newcapacity) { // set capacity to _Newcapacity (without geometric growth), provide strong guarantee auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; @@ -1269,7 +1316,27 @@ private: _Change_array(_Newvec, _Size, _Newcapacity); } - void _Clear_and_reserve_geometric(const size_type _Newsize) { +#if _ITERATOR_DEBUG_LEVEL != 0 && defined(_ENABLE_STL_INTERNAL_CHECK) + void _Check_all_orphaned_locked() const noexcept { + _Lockit _Lock(_LOCK_DEBUG); + auto& _My_data = _Mypair._Myval2; + _STL_INTERNAL_CHECK(!_My_data._Myproxy->_Myfirstiter); + } + + _CONSTEXPR20_CONTAINER void _Check_all_orphaned() const noexcept { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + auto& _My_data = _Mypair._Myval2; + _STL_INTERNAL_CHECK(!_My_data._Myproxy->_Myfirstiter); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Check_all_orphaned_locked(); + } + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 && defined(_ENABLE_STL_INTERNAL_CHECK) + + _CONSTEXPR20_CONTAINER void _Clear_and_reserve_geometric(const size_type _Newsize) { auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; @@ -1277,10 +1344,7 @@ private: #if _ITERATOR_DEBUG_LEVEL != 0 && defined(_ENABLE_STL_INTERNAL_CHECK) _STL_INTERNAL_CHECK(_Newsize != 0); - { - _Lockit _Lock(_LOCK_DEBUG); - _STL_INTERNAL_CHECK(!_My_data._Myproxy->_Myfirstiter); // asserts that all iterators are orphaned - } // unlock + _Check_all_orphaned(); #endif // _ITERATOR_DEBUG_LEVEL != 0 && defined(_ENABLE_STL_INTERNAL_CHECK) if (_Newsize > max_size()) { @@ -1302,7 +1366,7 @@ private: } public: - void reserve(_CRT_GUARDOVERFLOW const size_type _Newcapacity) { + _CONSTEXPR20_CONTAINER void reserve(_CRT_GUARDOVERFLOW const size_type _Newcapacity) { // increase capacity to _Newcapacity (without geometric growth), provide strong guarantee if (_Newcapacity > capacity()) { // something to do (reserve() never shrinks) if (_Newcapacity > max_size()) { @@ -1313,7 +1377,7 @@ public: } } - void shrink_to_fit() { // reduce capacity to size, provide strong guarantee + _CONSTEXPR20_CONTAINER void shrink_to_fit() { // reduce capacity to size, provide strong guarantee auto& _My_data = _Mypair._Myval2; const pointer _Oldlast = _My_data._Mylast; if (_Oldlast != _My_data._Myend) { // something to do @@ -1326,20 +1390,21 @@ public: } } - void pop_back() noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void pop_back() noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(_My_data._Myfirst != _Mylast, "vector empty before pop"); - _Orphan_range(_Mylast - 1, _Mylast); #endif // _ITERATOR_DEBUG_LEVEL == 2 + _Orphan_range(_Mylast - 1, _Mylast); _Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1)); --_Mylast; } - iterator erase(const_iterator _Where) noexcept(is_nothrow_move_assignable_v) /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const_iterator _Where) noexcept( + is_nothrow_move_assignable_v) /* strengthened */ { const pointer _Whereptr = _Where._Ptr; auto& _My_data = _Mypair._Myval2; pointer& _Mylast = _My_data._Mylast; @@ -1348,16 +1413,16 @@ public: _STL_VERIFY( _Where._Getcont() == _STD addressof(_My_data) && _Whereptr >= _My_data._Myfirst && _Mylast > _Whereptr, "vector erase iterator outside range"); - _Orphan_range(_Whereptr, _Mylast); #endif // _ITERATOR_DEBUG_LEVEL == 2 + _Orphan_range(_Whereptr, _Mylast); _Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr); _Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1)); --_Mylast; return iterator(_Whereptr, _STD addressof(_My_data)); } - iterator erase(const_iterator _First, const_iterator _Last) noexcept( + _CONSTEXPR20_CONTAINER iterator erase(const_iterator _First, const_iterator _Last) noexcept( is_nothrow_move_assignable_v) /* strengthened */ { const pointer _Firstptr = _First._Ptr; const pointer _Lastptr = _Last._Ptr; @@ -1381,7 +1446,7 @@ public: return iterator(_Firstptr, _STD addressof(_My_data)); } - void clear() noexcept { // erase all + _CONSTEXPR20_CONTAINER void clear() noexcept { // erase all auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; @@ -1391,111 +1456,110 @@ public: _Mylast = _Myfirst; } -public: - void swap(vector& _Right) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void swap(vector& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); _Mypair._Myval2._Swap_val(_Right._Mypair._Myval2); } } - _NODISCARD _Ty* data() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ty* data() noexcept { return _Unfancy_maybe_null(_Mypair._Myval2._Myfirst); } - _NODISCARD const _Ty* data() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Ty* data() const noexcept { return _Unfancy_maybe_null(_Mypair._Myval2._Myfirst); } - _NODISCARD iterator begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator begin() noexcept { auto& _My_data = _Mypair._Myval2; return iterator(_My_data._Myfirst, _STD addressof(_My_data)); } - _NODISCARD const_iterator begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator begin() const noexcept { auto& _My_data = _Mypair._Myval2; return const_iterator(_My_data._Myfirst, _STD addressof(_My_data)); } - _NODISCARD iterator end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator end() noexcept { auto& _My_data = _Mypair._Myval2; return iterator(_My_data._Mylast, _STD addressof(_My_data)); } - _NODISCARD const_iterator end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator end() const noexcept { auto& _My_data = _Mypair._Myval2; return const_iterator(_My_data._Mylast, _STD addressof(_My_data)); } - _NODISCARD reverse_iterator rbegin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - _NODISCARD const_reverse_iterator rbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - _NODISCARD reverse_iterator rend() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - _NODISCARD const_reverse_iterator rend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - _NODISCARD const_iterator cbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cbegin() const noexcept { return begin(); } - _NODISCARD const_iterator cend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cend() const noexcept { return end(); } - _NODISCARD const_reverse_iterator crbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crbegin() const noexcept { return rbegin(); } - _NODISCARD const_reverse_iterator crend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crend() const noexcept { return rend(); } - pointer _Unchecked_begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer _Unchecked_begin() noexcept { return _Mypair._Myval2._Myfirst; } - const_pointer _Unchecked_begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_pointer _Unchecked_begin() const noexcept { return _Mypair._Myval2._Myfirst; } - pointer _Unchecked_end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer _Unchecked_end() noexcept { return _Mypair._Myval2._Mylast; } - const_pointer _Unchecked_end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_pointer _Unchecked_end() const noexcept { return _Mypair._Myval2._Mylast; } - _NODISCARD bool empty() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool empty() const noexcept { auto& _My_data = _Mypair._Myval2; return _My_data._Myfirst == _My_data._Mylast; } - _NODISCARD size_type size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type size() const noexcept { auto& _My_data = _Mypair._Myval2; return static_cast(_My_data._Mylast - _My_data._Myfirst); } - _NODISCARD size_type max_size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type max_size() const noexcept { return (_STD min)( static_cast((numeric_limits::max)()), _Alty_traits::max_size(_Getal())); } - _NODISCARD size_type capacity() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type capacity() const noexcept { auto& _My_data = _Mypair._Myval2; return static_cast(_My_data._Myend - _My_data._Myfirst); } - _NODISCARD _Ty& operator[](const size_type _Pos) noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER _Ty& operator[](const size_type _Pos) noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY( @@ -1505,7 +1569,7 @@ public: return _My_data._Myfirst[_Pos]; } - _NODISCARD const _Ty& operator[](const size_type _Pos) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const _Ty& operator[](const size_type _Pos) const noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY( @@ -1515,7 +1579,7 @@ public: return _My_data._Myfirst[_Pos]; } - _NODISCARD _Ty& at(const size_type _Pos) { + _NODISCARD _CONSTEXPR20_CONTAINER _Ty& at(const size_type _Pos) { auto& _My_data = _Mypair._Myval2; if (static_cast(_My_data._Mylast - _My_data._Myfirst) <= _Pos) { _Xrange(); @@ -1524,7 +1588,7 @@ public: return _My_data._Myfirst[_Pos]; } - _NODISCARD const _Ty& at(const size_type _Pos) const { + _NODISCARD _CONSTEXPR20_CONTAINER const _Ty& at(const size_type _Pos) const { auto& _My_data = _Mypair._Myval2; if (static_cast(_My_data._Mylast - _My_data._Myfirst) <= _Pos) { _Xrange(); @@ -1533,7 +1597,7 @@ public: return _My_data._Myfirst[_Pos]; } - _NODISCARD _Ty& front() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER _Ty& front() noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_My_data._Myfirst != _My_data._Mylast, "front() called on empty vector"); @@ -1542,7 +1606,7 @@ public: return *_My_data._Myfirst; } - _NODISCARD const _Ty& front() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const _Ty& front() const noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_My_data._Myfirst != _My_data._Mylast, "front() called on empty vector"); @@ -1551,7 +1615,7 @@ public: return *_My_data._Myfirst; } - _NODISCARD _Ty& back() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER _Ty& back() noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_My_data._Myfirst != _My_data._Mylast, "back() called on empty vector"); @@ -1560,7 +1624,7 @@ public: return _My_data._Mylast[-1]; } - _NODISCARD const _Ty& back() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const _Ty& back() const noexcept /* strengthened */ { auto& _My_data = _Mypair._Myval2; #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_My_data._Myfirst != _My_data._Mylast, "back() called on empty vector"); @@ -1569,51 +1633,54 @@ public: return _My_data._Mylast[-1]; } - _NODISCARD allocator_type get_allocator() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER allocator_type get_allocator() const noexcept { return static_cast(_Getal()); } private: - pointer _Ufill(pointer _Dest, const size_type _Count, const _Ty& _Val) { + _CONSTEXPR20_CONTAINER pointer _Ufill(pointer _Dest, const size_type _Count, const _Ty& _Val) { // fill raw _Dest with _Count copies of _Val, using allocator return _Uninitialized_fill_n(_Dest, _Count, _Val, _Getal()); } - pointer _Ufill(pointer _Dest, const size_type _Count, _Value_init_tag) { + _CONSTEXPR20_CONTAINER pointer _Ufill(pointer _Dest, const size_type _Count, _Value_init_tag) { // fill raw _Dest with _Count value-initialized objects, using allocator return _Uninitialized_value_construct_n(_Dest, _Count, _Getal()); } template - pointer _Ucopy(_Iter _First, _Iter _Last, pointer _Dest) { // copy [_First, _Last) to raw _Dest, using allocator + _CONSTEXPR20_CONTAINER pointer _Ucopy(_Iter _First, _Iter _Last, pointer _Dest) { + // copy [_First, _Last) to raw _Dest, using allocator return _Uninitialized_copy(_First, _Last, _Dest, _Getal()); } - pointer _Umove(pointer _First, pointer _Last, pointer _Dest) { // move [_First, _Last) to raw _Dest, using allocator + _CONSTEXPR20_CONTAINER pointer _Umove(pointer _First, pointer _Last, pointer _Dest) { + // move [_First, _Last) to raw _Dest, using allocator return _Uninitialized_move(_First, _Last, _Dest, _Getal()); } - void _Umove_if_noexcept1(pointer _First, pointer _Last, pointer _Dest, true_type) { + _CONSTEXPR20_CONTAINER void _Umove_if_noexcept1(pointer _First, pointer _Last, pointer _Dest, true_type) { // move [_First, _Last) to raw _Dest, using allocator _Uninitialized_move(_First, _Last, _Dest, _Getal()); } - void _Umove_if_noexcept1(pointer _First, pointer _Last, pointer _Dest, false_type) { + _CONSTEXPR20_CONTAINER void _Umove_if_noexcept1(pointer _First, pointer _Last, pointer _Dest, false_type) { // copy [_First, _Last) to raw _Dest, using allocator _Uninitialized_copy(_First, _Last, _Dest, _Getal()); } - void _Umove_if_noexcept(pointer _First, pointer _Last, pointer _Dest) { + _CONSTEXPR20_CONTAINER void _Umove_if_noexcept(pointer _First, pointer _Last, pointer _Dest) { // move_if_noexcept [_First, _Last) to raw _Dest, using allocator _Umove_if_noexcept1(_First, _Last, _Dest, bool_constant, negation>>>{}); } - void _Destroy(pointer _First, pointer _Last) { // destroy [_First, _Last) using allocator + _CONSTEXPR20_CONTAINER void _Destroy(pointer _First, pointer _Last) { + // destroy [_First, _Last) using allocator _Destroy_range(_First, _Last, _Getal()); } - size_type _Calculate_growth(const size_type _Newsize) const { + _CONSTEXPR20_CONTAINER size_type _Calculate_growth(const size_type _Newsize) const { // given _Oldcapacity and _Newsize, calculate geometric growth const size_type _Oldcapacity = capacity(); const auto _Max = max_size(); @@ -1631,7 +1698,7 @@ private: return _Geometric; // geometric growth is sufficient } - void _Buy_raw(const size_type _Newcapacity) { + _CONSTEXPR20_CONTAINER void _Buy_raw(const size_type _Newcapacity) { // allocate array with _Newcapacity elements auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; @@ -1647,7 +1714,7 @@ private: _Myend = _Newvec + _Newcapacity; } - void _Buy_nonzero(const size_type _Newcapacity) { + _CONSTEXPR20_CONTAINER void _Buy_nonzero(const size_type _Newcapacity) { // allocate array with _Newcapacity elements #ifdef _ENABLE_STL_INTERNAL_CHECK auto& _My_data = _Mypair._Myval2; @@ -1665,7 +1732,8 @@ private: _Buy_raw(_Newcapacity); } - void _Change_array(const pointer _Newvec, const size_type _Newsize, const size_type _Newcapacity) { + _CONSTEXPR20_CONTAINER void _Change_array( + const pointer _Newvec, const size_type _Newsize, const size_type _Newcapacity) { // orphan all iterators, discard old array, acquire new array auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; @@ -1684,7 +1752,7 @@ private: _Myend = _Newvec + _Newcapacity; } - void _Tidy() noexcept { // free all storage + _CONSTEXPR20_CONTAINER void _Tidy() noexcept { // free all storage auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; @@ -1710,39 +1778,55 @@ private: _Xout_of_range("invalid vector subscript"); } - void _Orphan_range(pointer _First, pointer _Last) const { // orphan iterators within specified (inclusive) range #if _ITERATOR_DEBUG_LEVEL == 2 - _Lockit _Lock(_LOCK_DEBUG); - + _CONSTEXPR20_CONTAINER void _Orphan_range_unlocked(pointer _First, pointer _Last) const { _Iterator_base12** _Pnext = &_Mypair._Myval2._Myproxy->_Myfirstiter; while (*_Pnext) { const auto _Pnextptr = static_cast(**_Pnext)._Ptr; if (_Pnextptr < _First || _Last < _Pnextptr) { // skip the iterator - _Pnext = &(*_Pnext)->_Mynextiter; + const auto _Temp = *_Pnext; // TRANSITION, VSO-1269037 + _Pnext = &_Temp->_Mynextiter; } else { // orphan the iterator - (*_Pnext)->_Myproxy = nullptr; - *_Pnext = (*_Pnext)->_Mynextiter; + const auto _Temp = *_Pnext; // TRANSITION, VSO-1269037 + _Temp->_Myproxy = nullptr; + *_Pnext = _Temp->_Mynextiter; } } -#else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 ^^^ // vvv _ITERATOR_DEBUG_LEVEL != 2 vvv - (void) _First; - (void) _Last; -#endif // _ITERATOR_DEBUG_LEVEL == 2 } - _Alty& _Getal() noexcept { + void _Orphan_range_locked(pointer _First, pointer _Last) const { + _Lockit _Lock(_LOCK_DEBUG); + _Orphan_range_unlocked(_First, _Last); + } + + _CONSTEXPR20_CONTAINER void _Orphan_range(pointer _First, pointer _Last) const { + // orphan iterators within specified (inclusive) range +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _Orphan_range_unlocked(_First, _Last); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Orphan_range_locked(_First, _Last); + } + } +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 ^^^ // vvv _ITERATOR_DEBUG_LEVEL != 2 vvv + _CONSTEXPR20_CONTAINER void _Orphan_range(pointer, pointer) const {} +#endif // _ITERATOR_DEBUG_LEVEL != 2 + + _NODISCARD _CONSTEXPR20_CONTAINER _Alty& _Getal() noexcept { return _Mypair._Get_first(); } - const _Alty& _Getal() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Alty& _Getal() const noexcept { return _Mypair._Get_first(); } - iterator _Make_iterator(const pointer _Ptr) noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator _Make_iterator(const pointer _Ptr) noexcept { return iterator(_Ptr, _STD addressof(_Mypair._Myval2)); } - iterator _Make_iterator_offset(const size_type _Offset) noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator _Make_iterator_offset(const size_type _Offset) noexcept { // return the iterator begin() + _Offset without a debugging check auto& _My_data = _Mypair._Myval2; return iterator(_My_data._Myfirst + _Offset, _STD addressof(_My_data)); @@ -1757,47 +1841,162 @@ template >, vector(_Iter, _Iter, _Alloc = _Alloc()) -> vector<_Iter_value_t<_Iter>, _Alloc>; #endif // _HAS_CXX17 -template -void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { - _Left.swap(_Right); -} +template +class vector; + +using _Vbase = unsigned int; // word type for vector representation +constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word template -_NODISCARD bool operator==(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() - && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); +_NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { + if (_Left.size() != _Right.size()) { + return false; + } + + if constexpr (is_same_v<_Ty, bool>) { + return _STD equal( + _Left._Myvec._Unchecked_begin(), _Left._Myvec._Unchecked_end(), _Right._Myvec._Unchecked_begin()); + } else { + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + } } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 + +// Optimize vector lexicographical comparisons. + +// There are several endianness/ordering issues to consider here. +// * Machine endianness is irrelevant. (That affects how an unsigned int is stored +// as a sequence of bytes. While all of our supported architectures are little-endian, +// that's irrelevant as long as we avoid reinterpreting unsigned int as a sequence of bytes.) +// * Appending bits to vector eventually appends words to its underlying storage. +// For example, vb[10] is stored within vb._Myvec[0], while vb[100] is stored within vb._Myvec[3]. +// This allows us to translate lexicographical comparisons from theoretical bits to physical words. +// * Unsigned integers are written and compared as big-endian (most significant bit first). +// For example, 0x10u > 0x07u. +// * However, vector packs bits into words as little-endian (least significant bit first). +// For example, vector{false, true, true, true} stores 0b0000'0000'0000'0000'0000'0000'0000'1110u. +// We could bit-reverse words before comparing, but we just need to find the least significant bit that differs. + +template +struct _Vbase_compare_three_way { + _NODISCARD constexpr _Ret operator()(const _Vbase _Left, const _Vbase _Right) const noexcept { + const _Vbase _Differing_bits = _Left ^ _Right; + + if (_Differing_bits == 0) { // improves _Countr_zero codegen below +#ifdef __cpp_lib_concepts + return strong_ordering::equal; +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + return 0; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + } + + const int _Bit_index = _Countr_zero(_Differing_bits); // number of least significant bits that match + _STL_INTERNAL_CHECK(_Bit_index < _VBITS); // because we return early for equality + + const _Vbase _Mask = _Vbase{1} << _Bit_index; // selects the least significant bit that differs + + // Instead of comparing (_Left & _Mask) to (_Right & _Mask), we know that exactly one side will be zero. +#ifdef __cpp_lib_concepts + return (_Left & _Mask) == 0 ? strong_ordering::less : strong_ordering::greater; +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + return (_Left & _Mask) == 0 ? -1 : 1; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + } +}; +#ifdef __cpp_lib_concepts template -_NODISCARD bool operator<(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare( - _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); +_NODISCARD _CONSTEXPR20_CONTAINER _Synth_three_way_result<_Ty> operator<=>( + const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + const auto _Min_word_size = (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + const auto _Left_words = _Left._Myvec._Unchecked_begin(); + const auto _Right_words = _Right._Myvec._Unchecked_begin(); + + using _Comp = _Vbase_compare_three_way; + + const strong_ordering _Word_comparison = _STD lexicographical_compare_three_way( + _Left_words, _Left_words + _Min_word_size, _Right_words, _Right_words + _Min_word_size, _Comp{}); + + if (_Word_comparison != 0) { + return _Word_comparison; + } + + return _Left.size() <=> _Right.size(); + } else { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); + } +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +template +_NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + auto _First = _Left._Myvec._Unchecked_begin(); + auto _Other = _Right._Myvec._Unchecked_begin(); + + const auto _Last = _First + (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + + for (; _First != _Last; ++_First, (void) ++_Other) { + using _Comp = _Vbase_compare_three_way; + const auto _Result = _Comp{}(*_First, *_Other); + + if (_Result < 0) { + return true; + } else if (_Result > 0) { + return false; + } + } + + return _Left.size() < _Right.size(); + } else { + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + } } template -_NODISCARD bool operator>(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { +_NODISCARD _CONSTEXPR20_CONTAINER bool operator>(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return _Right < _Left; } template -_NODISCARD bool operator<=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { +_NODISCARD _CONSTEXPR20_CONTAINER bool operator<=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Right < _Left); } template -_NODISCARD bool operator>=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { +_NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ -// CLASS TEMPLATE vector AND FRIENDS -using _Vbase = unsigned int; // word type for vector representation -constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word +template +_CONSTEXPR20_CONTAINER void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { + _Left.swap(_Right); +} + +#if _HAS_CXX20 +template +_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { + return _Erase_remove(_Cont, _Val); +} + +template +_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { + return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); +} +#endif // _HAS_CXX20 +// CLASS TEMPLATE vector AND FRIENDS template struct _Wrap_alloc { // TRANSITION, ABI compat, preserves symbol names of vector::iterator using _Alloc = _Alloc0; @@ -1813,21 +2012,24 @@ public: using _Difference_type = typename allocator_traits<_Alvbase>::difference_type; using _Mycont = vector>; - _Vb_iter_base() = default; + _CONSTEXPR20_CONTAINER _Vb_iter_base() = default; - _Vb_iter_base(const _Vbase* _Ptr, _Size_type _Off, const _Container_base* _Mypvbool) noexcept + _CONSTEXPR20_CONTAINER _Vb_iter_base(const _Vbase* _Ptr, _Size_type _Off, const _Container_base* _Mypvbool) noexcept : _Myptr(_Ptr), _Myoff(_Off) { this->_Adopt(_Mypvbool); } - void _Advance(_Size_type _Off) noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _Vb_iter_base& operator=(const _Vb_iter_base&) noexcept = default; + + _CONSTEXPR20_CONTAINER void _Advance(_Size_type _Off) noexcept { _Myoff += _Off; _Myptr += _Myoff / _VBITS; _Myoff %= _VBITS; } #if _ITERATOR_DEBUG_LEVEL != 0 - _Difference_type _Total_off(const _Mycont* _Cont) const noexcept { + _CONSTEXPR20_CONTAINER _Difference_type _Total_off(const _Mycont* _Cont) const noexcept { return static_cast<_Difference_type>(_VBITS * (_Myptr - _Cont->_Myvec.data()) + _Myoff); } #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -1845,18 +2047,19 @@ class _Vb_reference : public _Vb_iter_base<_Alvbase_wrapped> { using _Difference_type = typename _Mybase::_Difference_type; // TRANSITION, ABI: non-trivial constructor - _Vb_reference() = default; + _CONSTEXPR20_CONTAINER _Vb_reference() = default; public: - _Vb_reference(const _Vb_reference&) = default; + _CONSTEXPR20_CONTAINER _Vb_reference(const _Vb_reference&) = default; - _Vb_reference(const _Mybase& _Right) noexcept : _Mybase(_Right._Myptr, _Right._Myoff, _Right._Getcont()) {} + _CONSTEXPR20_CONTAINER _Vb_reference(const _Mybase& _Right) noexcept + : _Mybase(_Right._Myptr, _Right._Myoff, _Right._Getcont()) {} - _Vb_reference& operator=(const _Vb_reference& _Right) noexcept { + _CONSTEXPR20_CONTAINER _Vb_reference& operator=(const _Vb_reference& _Right) noexcept { return *this = static_cast(_Right); } - _Vb_reference& operator=(bool _Val) noexcept { + _CONSTEXPR20_CONTAINER _Vb_reference& operator=(bool _Val) noexcept { if (_Val) { *const_cast<_Vbase*>(_Getptr()) |= _Mask(); } else { @@ -1866,15 +2069,15 @@ public: return *this; } - void flip() noexcept { + _CONSTEXPR20_CONTAINER void flip() noexcept { *const_cast<_Vbase*>(_Getptr()) ^= _Mask(); } - operator bool() const noexcept { + _CONSTEXPR20_CONTAINER operator bool() const noexcept { return (*_Getptr() & _Mask()) != 0; } - const _Vbase* _Getptr() const noexcept { + _CONSTEXPR20_CONTAINER const _Vbase* _Getptr() const noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot dereference value-initialized vector iterator"); @@ -1885,14 +2088,14 @@ public: return this->_Myptr; } - friend void swap(_Vb_reference _Left, _Vb_reference _Right) noexcept { + friend _CONSTEXPR20_CONTAINER void swap(_Vb_reference _Left, _Vb_reference _Right) noexcept { bool _Val = _Left; // NOT _STD swap _Left = _Right; _Right = _Val; } protected: - _Vbase _Mask() const noexcept { + _CONSTEXPR20_CONTAINER _Vbase _Mask() const noexcept { return static_cast<_Vbase>(1) << this->_Myoff; } }; @@ -1916,11 +2119,15 @@ public: using pointer = const_reference*; using reference = const_reference; - _Vb_const_iterator() = default; + _CONSTEXPR20_CONTAINER _Vb_const_iterator() = default; + + _CONSTEXPR20_CONTAINER _Vb_const_iterator(const _Vbase* _Ptr, const _Container_base* _Mypvbool) noexcept + : _Mybase(_Ptr, 0, _Mypvbool) {} - _Vb_const_iterator(const _Vbase* _Ptr, const _Container_base* _Mypvbool) noexcept : _Mybase(_Ptr, 0, _Mypvbool) {} + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _Vb_const_iterator& operator=(const _Vb_const_iterator&) noexcept = default; - _NODISCARD const_reference operator*() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference operator*() const noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot dereference value-initialized vector iterator"); @@ -1931,29 +2138,29 @@ public: return _Reft(*this); } - _Vb_const_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _Vb_const_iterator& operator++() noexcept { _Inc(); return *this; } - _Vb_const_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _Vb_const_iterator operator++(int) noexcept { _Vb_const_iterator _Tmp = *this; _Inc(); return _Tmp; } - _Vb_const_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _Vb_const_iterator& operator--() noexcept { _Dec(); return *this; } - _Vb_const_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _Vb_const_iterator operator--(int) noexcept { _Vb_const_iterator _Tmp = *this; _Dec(); return _Tmp; } - _Vb_const_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vb_const_iterator& operator+=(const difference_type _Off) noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 if (_Off != 0) { const auto _Cont = static_cast(this->_Getcont()); @@ -1969,46 +2176,57 @@ public: #endif // _ITERATOR_DEBUG_LEVEL != 0 if (_Off < 0 && this->_Myoff < 0 - static_cast<_Size_type>(_Off)) { // add negative increment - this->_Myoff += _Off; + this->_Myoff += static_cast<_Size_type>(_Off); this->_Myptr -= 1 + (static_cast<_Size_type>(-1) - this->_Myoff) / _VBITS; this->_Myoff %= _VBITS; } else { // add non-negative increment - this->_Myoff += _Off; + this->_Myoff += static_cast<_Size_type>(_Off); this->_Myptr += this->_Myoff / _VBITS; this->_Myoff %= _VBITS; } return *this; } - _NODISCARD _Vb_const_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vb_const_iterator operator+(const difference_type _Off) const noexcept { _Vb_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _Vb_const_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vb_const_iterator& operator-=(const difference_type _Off) noexcept { return *this += -_Off; } - _NODISCARD _Vb_const_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vb_const_iterator operator-(const difference_type _Off) const noexcept { _Vb_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD difference_type operator-(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _Vb_const_iterator& _Right) const noexcept { _Compat(_Right); return static_cast(_VBITS * (this->_Myptr - _Right._Myptr)) + static_cast(this->_Myoff) - static_cast(_Right._Myoff); } - _NODISCARD const_reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } - _NODISCARD bool operator==(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const _Vb_const_iterator& _Right) const noexcept { _Compat(_Right); return this->_Myptr == _Right._Myptr && this->_Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _Vb_const_iterator& _Right) const noexcept { + _Compat(_Right); + if (const auto _CmpResult = this->_Myptr <=> _Right._Myptr; _CmpResult != 0) { + return _CmpResult; + } + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Vb_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -2029,8 +2247,10 @@ public: _NODISCARD bool operator>=(const _Vb_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 - void _Compat(const _Vb_const_iterator& _Right) const noexcept { // test for compatible iterator pair + _CONSTEXPR20_CONTAINER void _Compat(const _Vb_const_iterator& _Right) const noexcept { + // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL == 0 (void) _Right; #else // _ITERATOR_DEBUG_LEVEL == 0 @@ -2041,13 +2261,14 @@ public: #if _ITERATOR_DEBUG_LEVEL != 0 using _Prevent_inheriting_unwrap = _Vb_const_iterator; - friend void _Verify_range(const _Vb_const_iterator& _First, const _Vb_const_iterator& _Last) noexcept { + friend _CONSTEXPR20_CONTAINER void _Verify_range( + const _Vb_const_iterator& _First, const _Vb_const_iterator& _Last) noexcept { // note _Compat check inside <= _STL_VERIFY(_First <= _Last, "vector iterator range transposed"); } #endif // _ITERATOR_DEBUG_LEVEL != 0 - void _Dec() noexcept { // decrement bit position + _CONSTEXPR20_CONTAINER void _Dec() noexcept { // decrement bit position #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot decrement value-initialized vector iterator"); @@ -2062,7 +2283,7 @@ public: } } - void _Inc() noexcept { // increment bit position + _CONSTEXPR20_CONTAINER void _Inc() noexcept { // increment bit position #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot increment value-initialized vector iterator"); @@ -2080,7 +2301,7 @@ public: }; template -_NODISCARD _Vb_const_iterator<_Alvbase_wrapped> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _Vb_const_iterator<_Alvbase_wrapped> operator+( typename _Vb_const_iterator<_Alvbase_wrapped>::difference_type _Off, _Vb_const_iterator<_Alvbase_wrapped> _Right) noexcept { return _Right += _Off; @@ -2105,7 +2326,7 @@ public: using _Mybase::_Mybase; - _NODISCARD reference operator*() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot dereference value-initialized vector iterator"); @@ -2116,51 +2337,56 @@ public: return _Reft(*this); } - _Vb_iterator& operator++() noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _Vb_iterator& operator=(const _Vb_iterator&) noexcept = default; + + _CONSTEXPR20_CONTAINER _Vb_iterator& operator++() noexcept { _Mybase::operator++(); return *this; } - _Vb_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _Vb_iterator operator++(int) noexcept { _Vb_iterator _Tmp = *this; _Mybase::operator++(); return _Tmp; } - _Vb_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _Vb_iterator& operator--() noexcept { _Mybase::operator--(); return *this; } - _Vb_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _Vb_iterator operator--(int) noexcept { _Vb_iterator _Tmp = *this; _Mybase::operator--(); return _Tmp; } - _Vb_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vb_iterator& operator+=(const difference_type _Off) noexcept { _Mybase::operator+=(_Off); return *this; } - _NODISCARD _Vb_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vb_iterator operator+(const difference_type _Off) const noexcept { _Vb_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _Vb_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _Vb_iterator& operator-=(const difference_type _Off) noexcept { _Mybase::operator-=(_Off); return *this; } using _Mybase::operator-; - _NODISCARD _Vb_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Vb_iterator operator-(const difference_type _Off) const noexcept { _Vb_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } @@ -2168,7 +2394,7 @@ public: }; template -_NODISCARD _Vb_iterator<_Alvbase_wrapped> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _Vb_iterator<_Alvbase_wrapped> operator+( typename _Vb_iterator<_Alvbase_wrapped>::difference_type _Off, _Vb_iterator<_Alvbase_wrapped> _Right) noexcept { return _Right += _Off; } @@ -2183,39 +2409,41 @@ public: using _Alvbase_wrapped = _Wrap_alloc<_Alvbase>; using size_type = typename _Alvbase_traits::size_type; - _Vb_val() noexcept(is_nothrow_default_constructible_v<_Vectype>) : _Myvec(), _Mysize(0) { + _CONSTEXPR20_CONTAINER _Vb_val() noexcept(is_nothrow_default_constructible_v<_Vectype>) : _Myvec(), _Mysize(0) { this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - _Vb_val(const _Alloc& _Al) noexcept(is_nothrow_constructible_v<_Vectype, _Alvbase>) + _CONSTEXPR20_CONTAINER _Vb_val(const _Alloc& _Al) noexcept(is_nothrow_constructible_v<_Vectype, _Alvbase>) : _Myvec(static_cast<_Alvbase>(_Al)), _Mysize(0) { this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - _Vb_val(size_type _Count, const bool& _Val) : _Myvec(_Nw(_Count), static_cast<_Vbase>(_Val ? -1 : 0)), _Mysize(0) { + _CONSTEXPR20_CONTAINER _Vb_val(size_type _Count, const bool& _Val) + : _Myvec(_Nw(_Count), static_cast<_Vbase>(_Val ? -1 : 0)), _Mysize(0) { this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - _Vb_val(size_type _Count, const bool& _Val, const _Alloc& _Al) + _CONSTEXPR20_CONTAINER _Vb_val(size_type _Count, const bool& _Val, const _Alloc& _Al) : _Myvec(_Nw(_Count), static_cast<_Vbase>(_Val ? -1 : 0), static_cast<_Alvbase>(_Al)), _Mysize(0) { this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - _Vb_val(const _Vb_val& _Right) : _Myvec(_Right._Myvec), _Mysize(_Right._Mysize) { + _CONSTEXPR20_CONTAINER _Vb_val(const _Vb_val& _Right) : _Myvec(_Right._Myvec), _Mysize(_Right._Mysize) { this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - _Vb_val(const _Vb_val& _Right, const _Alloc& _Al) + _CONSTEXPR20_CONTAINER _Vb_val(const _Vb_val& _Right, const _Alloc& _Al) : _Myvec(_Right._Myvec, static_cast<_Alvbase>(_Al)), _Mysize(_Right._Mysize) { this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - _Vb_val(_Vb_val&& _Right) noexcept(is_nothrow_move_constructible_v<_Vectype>) + _CONSTEXPR20_CONTAINER _Vb_val(_Vb_val&& _Right) noexcept(is_nothrow_move_constructible_v<_Vectype>) : _Myvec(_STD move(_Right._Myvec)), _Mysize(_STD exchange(_Right._Mysize, size_type{0})) { this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - _Vb_val(_Vb_val&& _Right, const _Alloc& _Al) noexcept(is_nothrow_constructible_v<_Vectype, _Vectype, _Alvbase>) + _CONSTEXPR20_CONTAINER _Vb_val(_Vb_val&& _Right, const _Alloc& _Al) noexcept( + is_nothrow_constructible_v<_Vectype, _Vectype, _Alvbase>) : _Myvec(_STD move(_Right._Myvec), static_cast<_Alvbase>(_Al)), _Mysize(_Right._Mysize) { if (_Right._Myvec.empty()) { // we took _Right's buffer, so zero out size @@ -2225,7 +2453,7 @@ public: this->_Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alvbase, _Getal())); } - ~_Vb_val() noexcept { + _CONSTEXPR20_CONTAINER ~_Vb_val() noexcept { #if _ITERATOR_DEBUG_LEVEL != 0 this->_Orphan_all(); auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alvbase, this->_Getal()); @@ -2233,15 +2461,15 @@ public: #endif // _ITERATOR_DEBUG_LEVEL != 0 } - _Alvbase& _Getal() noexcept { + _CONSTEXPR20_CONTAINER _Alvbase& _Getal() noexcept { return _Myvec._Getal(); } - const _Alvbase& _Getal() const noexcept { + _CONSTEXPR20_CONTAINER const _Alvbase& _Getal() const noexcept { return _Myvec._Getal(); } - static size_type _Nw(size_type _Count) noexcept { + static _CONSTEXPR20_CONTAINER size_type _Nw(size_type _Count) noexcept { return (_Count + _VBITS - 1) / _VBITS; } @@ -2281,41 +2509,44 @@ public: static const int _VBITS = _STD _VBITS; enum { _EEN_VBITS = _VBITS }; // helper for expression evaluator - vector() noexcept(is_nothrow_default_constructible_v<_Mybase>) // strengthened + _CONSTEXPR20_CONTAINER vector() noexcept(is_nothrow_default_constructible_v<_Mybase>) // strengthened : _Mybase() {} - explicit vector(const _Alloc& _Al) noexcept(is_nothrow_constructible_v<_Mybase, const _Alloc&>) // strengthened + _CONSTEXPR20_CONTAINER explicit vector(const _Alloc& _Al) noexcept( + is_nothrow_constructible_v<_Mybase, const _Alloc&>) // strengthened : _Mybase(_Al) {} - explicit vector(_CRT_GUARDOVERFLOW size_type _Count, const _Alloc& _Al = _Alloc()) : _Mybase(_Count, false, _Al) { + _CONSTEXPR20_CONTAINER explicit vector(_CRT_GUARDOVERFLOW size_type _Count, const _Alloc& _Al = _Alloc()) + : _Mybase(_Count, false, _Al) { _Trim(_Count); } - vector(_CRT_GUARDOVERFLOW size_type _Count, const bool& _Val, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER vector(_CRT_GUARDOVERFLOW size_type _Count, const bool& _Val, const _Alloc& _Al = _Alloc()) : _Mybase(_Count, _Val, _Al) { _Trim(_Count); } - vector(const vector& _Right) : _Mybase(_Right) {} + _CONSTEXPR20_CONTAINER vector(const vector& _Right) : _Mybase(_Right) {} - vector(const vector& _Right, const _Alloc& _Al) : _Mybase(_Right, _Al) {} + _CONSTEXPR20_CONTAINER vector(const vector& _Right, const _Alloc& _Al) : _Mybase(_Right, _Al) {} template , int> = 0> - vector(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mybase(_Al) { + _CONSTEXPR20_CONTAINER vector(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mybase(_Al) { _BConstruct(_First, _Last); } template - void _BConstruct(_Iter _First, _Iter _Last) { + _CONSTEXPR20_CONTAINER void _BConstruct(_Iter _First, _Iter _Last) { insert(begin(), _First, _Last); } - vector(vector&& _Right) noexcept(is_nothrow_move_constructible_v<_Mybase>) // strengthened + _CONSTEXPR20_CONTAINER vector(vector&& _Right) noexcept(is_nothrow_move_constructible_v<_Mybase>) // strengthened : _Mybase(_STD move(_Right)) { this->_Swap_proxy_and_iterators(_Right); } - vector(vector&& _Right, const _Alloc& _Al) noexcept(is_nothrow_constructible_v<_Mybase, _Mybase, const _Alloc&>) + _CONSTEXPR20_CONTAINER vector(vector&& _Right, const _Alloc& _Al) noexcept( + is_nothrow_constructible_v<_Mybase, _Mybase, const _Alloc&>) : _Mybase(_STD move(_Right), _Al) { if constexpr (!_Alvbase_traits::is_always_equal::value) { if (this->_Getal() != _Right._Getal()) { @@ -2328,13 +2559,13 @@ public: private: #if _ITERATOR_DEBUG_LEVEL != 0 - void _Move_assign(vector& _Right, _Equal_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(vector& _Right, _Equal_allocators) noexcept { this->_Myvec = _STD move(_Right._Myvec); this->_Mysize = _STD exchange(_Right._Mysize, size_type{0}); this->_Swap_proxy_and_iterators(_Right); } - void _Move_assign(vector& _Right, _Propagate_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(vector& _Right, _Propagate_allocators) noexcept { using _Alproxy_type = _Rebind_alloc_t<_Alvbase, _Container_proxy>; if (this->_Getal() != _Right._Getal()) { // reload proxy // intentionally slams into noexcept on OOM, TRANSITION, VSO-466800 @@ -2353,7 +2584,7 @@ private: this->_Swap_proxy_and_iterators(_Right); } - void _Move_assign(vector& _Right, _No_propagate_allocators) { + _CONSTEXPR20_CONTAINER void _Move_assign(vector& _Right, _No_propagate_allocators) { this->_Myvec = _STD move(_Right._Myvec); this->_Mysize = _Right._Mysize; if (_Right._Myvec.empty()) { @@ -2368,7 +2599,7 @@ private: #endif // _ITERATOR_DEBUG_LEVEL != 0 public: - vector& operator=(vector&& _Right) noexcept(is_nothrow_move_assignable_v<_Mybase>) { + _CONSTEXPR20_CONTAINER vector& operator=(vector&& _Right) noexcept(is_nothrow_move_assignable_v<_Mybase>) { if (this != _STD addressof(_Right)) { #if _ITERATOR_DEBUG_LEVEL == 0 this->_Myvec = _STD move(_Right._Myvec); @@ -2382,7 +2613,7 @@ public: } template - decltype(auto) emplace_back(_Valty&&... _Val) { + _CONSTEXPR20_CONTAINER decltype(auto) emplace_back(_Valty&&... _Val) { bool _Tmp(_STD forward<_Valty>(_Val)...); push_back(_Tmp); @@ -2392,38 +2623,39 @@ public: } template - iterator emplace(const_iterator _Where, _Valty&&... _Val) { + _CONSTEXPR20_CONTAINER iterator emplace(const_iterator _Where, _Valty&&... _Val) { bool _Tmp(_STD forward<_Valty>(_Val)...); return insert(_Where, _Tmp); } - vector(initializer_list _Ilist, const _Alloc& _Al = allocator_type()) : _Mybase(0, false, _Al) { + _CONSTEXPR20_CONTAINER vector(initializer_list _Ilist, const _Alloc& _Al = allocator_type()) + : _Mybase(0, false, _Al) { insert(begin(), _Ilist.begin(), _Ilist.end()); } - vector& operator=(initializer_list _Ilist) { + _CONSTEXPR20_CONTAINER vector& operator=(initializer_list _Ilist) { assign(_Ilist.begin(), _Ilist.end()); return *this; } - void assign(initializer_list _Ilist) { + _CONSTEXPR20_CONTAINER void assign(initializer_list _Ilist) { assign(_Ilist.begin(), _Ilist.end()); } - iterator insert(const_iterator _Where, initializer_list _Ilist) { + _CONSTEXPR20_CONTAINER iterator insert(const_iterator _Where, initializer_list _Ilist) { return insert(_Where, _Ilist.begin(), _Ilist.end()); } - ~vector() noexcept {} + _CONSTEXPR20_CONTAINER ~vector() noexcept {} private: #if _ITERATOR_DEBUG_LEVEL != 0 - void _Copy_assign(const vector& _Right, false_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const vector& _Right, false_type) { this->_Myvec = _Right._Myvec; this->_Mysize = _Right._Mysize; } - void _Copy_assign(const vector& _Right, true_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const vector& _Right, true_type) { if (this->_Getal() == _Right._Getal()) { _Copy_assign(_Right, false_type{}); } else { @@ -2440,7 +2672,7 @@ private: #endif // _ITERATOR_DEBUG_LEVEL != 0 public: - vector& operator=(const vector& _Right) { + _CONSTEXPR20_CONTAINER vector& operator=(const vector& _Right) { if (this != _STD addressof(_Right)) { #if _ITERATOR_DEBUG_LEVEL == 0 this->_Myvec = _Right._Myvec; @@ -2454,70 +2686,70 @@ public: return *this; } - void reserve(_CRT_GUARDOVERFLOW size_type _Count) { + _CONSTEXPR20_CONTAINER void reserve(_CRT_GUARDOVERFLOW size_type _Count) { this->_Myvec.reserve(this->_Nw(_Count)); } - _NODISCARD size_type capacity() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type capacity() const noexcept { return this->_Myvec.capacity() * _VBITS; } - _NODISCARD iterator begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator begin() noexcept { return iterator(this->_Myvec.data(), this); } - _NODISCARD const_iterator begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator begin() const noexcept { return const_iterator(this->_Myvec.data(), this); } - _NODISCARD iterator end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator end() noexcept { return begin() + static_cast(this->_Mysize); } - _NODISCARD const_iterator end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator end() const noexcept { return begin() + static_cast(this->_Mysize); } - _NODISCARD const_iterator cbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cbegin() const noexcept { return begin(); } - _NODISCARD const_iterator cend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cend() const noexcept { return end(); } - _NODISCARD const_reverse_iterator crbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crbegin() const noexcept { return rbegin(); } - _NODISCARD const_reverse_iterator crend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crend() const noexcept { return rend(); } - _NODISCARD iterator _Unchecked_begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator _Unchecked_begin() noexcept { return iterator(this->_Myvec.data(), this); } - _NODISCARD const_iterator _Unchecked_begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator _Unchecked_begin() const noexcept { return const_iterator(this->_Myvec.data(), this); } - _NODISCARD iterator _Unchecked_end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator _Unchecked_end() noexcept { return _Unchecked_begin() + static_cast(this->_Mysize); } - _NODISCARD const_iterator _Unchecked_end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator _Unchecked_end() const noexcept { return _Unchecked_begin() + static_cast(this->_Mysize); } - void shrink_to_fit() { + _CONSTEXPR20_CONTAINER void shrink_to_fit() { if (this->_Myvec.capacity() != this->_Myvec.size()) { this->_Orphan_all(); this->_Myvec.shrink_to_fit(); } } - iterator _Make_iter(const_iterator _Where) noexcept { + _CONSTEXPR20_CONTAINER iterator _Make_iter(const_iterator _Where) noexcept { iterator _Tmp = begin(); if (0 < this->_Mysize) { _Tmp += _Where - begin(); @@ -2526,23 +2758,23 @@ public: return _Tmp; } - _NODISCARD reverse_iterator rbegin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - _NODISCARD const_reverse_iterator rbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - _NODISCARD reverse_iterator rend() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - _NODISCARD const_reverse_iterator rend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - void resize(_CRT_GUARDOVERFLOW size_type _Newsize, bool _Val = false) { + _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW size_type _Newsize, bool _Val = false) { if (size() < _Newsize) { _Insert_n(end(), _Newsize - size(), _Val); } else if (_Newsize < size()) { @@ -2550,11 +2782,11 @@ public: } } - _NODISCARD size_type size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type size() const noexcept { return this->_Mysize; } - _NODISCARD size_type max_size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type max_size() const noexcept { constexpr auto _Diff_max = static_cast((numeric_limits::max)()); const size_type _Ints_max = this->_Myvec.max_size(); if (_Ints_max > _Diff_max / _VBITS) { // max_size bound by difference_type limits @@ -2565,15 +2797,15 @@ public: return _Ints_max * _VBITS; } - _NODISCARD bool empty() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool empty() const noexcept { return size() == 0; } - _NODISCARD allocator_type get_allocator() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER allocator_type get_allocator() const noexcept { return static_cast(this->_Myvec.get_allocator()); } - _NODISCARD const_reference at(size_type _Off) const { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference at(size_type _Off) const { if (size() <= _Off) { _Xran(); } @@ -2581,7 +2813,7 @@ public: return (*this)[_Off]; } - _NODISCARD reference at(size_type _Off) { + _NODISCARD _CONSTEXPR20_CONTAINER reference at(size_type _Off) { if (size() <= _Off) { _Xran(); } @@ -2589,7 +2821,7 @@ public: return (*this)[_Off]; } - _NODISCARD const_reference operator[](size_type _Off) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference operator[](size_type _Off) const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off < this->_Mysize, "vector subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -2599,7 +2831,7 @@ public: return *_It; } - _NODISCARD reference operator[](size_type _Off) noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](size_type _Off) noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off < this->_Mysize, "vector subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -2609,7 +2841,7 @@ public: return *_It; } - _NODISCARD reference front() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference front() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Mysize != 0, "front() called on empty vector"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -2617,7 +2849,7 @@ public: return *begin(); } - _NODISCARD const_reference front() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference front() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Mysize != 0, "front() called on empty vector"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -2625,7 +2857,7 @@ public: return *begin(); } - _NODISCARD reference back() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference back() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Mysize != 0, "back() called on empty vector"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -2633,7 +2865,7 @@ public: return *(end() - 1); } - _NODISCARD const_reference back() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference back() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Mysize != 0, "back() called on empty vector"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -2641,42 +2873,43 @@ public: return *(end() - 1); } - void push_back(const bool& _Val) { + _CONSTEXPR20_CONTAINER void push_back(const bool& _Val) { insert(end(), _Val); } - void pop_back() noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void pop_back() noexcept /* strengthened */ { erase(end() - 1); } template , int> = 0> - void assign(_Iter _First, _Iter _Last) { + _CONSTEXPR20_CONTAINER void assign(_Iter _First, _Iter _Last) { clear(); insert(begin(), _First, _Last); } - void assign(_CRT_GUARDOVERFLOW size_type _Count, const bool& _Val) { + _CONSTEXPR20_CONTAINER void assign(_CRT_GUARDOVERFLOW size_type _Count, const bool& _Val) { clear(); _Insert_n(begin(), _Count, _Val); } - iterator insert(const_iterator _Where, const bool& _Val) { + _CONSTEXPR20_CONTAINER iterator insert(const_iterator _Where, const bool& _Val) { return _Insert_n(_Where, static_cast(1), _Val); } - iterator insert(const_iterator _Where, _CRT_GUARDOVERFLOW size_type _Count, const bool& _Val) { + _CONSTEXPR20_CONTAINER iterator insert( + const_iterator _Where, _CRT_GUARDOVERFLOW size_type _Count, const bool& _Val) { return _Insert_n(_Where, _Count, _Val); } template , int> = 0> - iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) { + _CONSTEXPR20_CONTAINER iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) { difference_type _Off = _Where - begin(); _Insert(_Where, _First, _Last, _Iter_cat_t<_Iter>{}); return begin() + _Off; } template - void _Insert(const_iterator _Where, _Iter _First, _Iter _Last, input_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Insert(const_iterator _Where, _Iter _First, _Iter _Last, input_iterator_tag) { difference_type _Off = _Where - begin(); for (; _First != _Last; ++_First, (void) ++_Off) { @@ -2685,14 +2918,14 @@ public: } template - void _Insert(const_iterator _Where, _Iter _First, _Iter _Last, forward_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Insert(const_iterator _Where, _Iter _First, _Iter _Last, forward_iterator_tag) { _Adl_verify_range(_First, _Last); auto _Count = _Convert_size(static_cast(_STD distance(_First, _Last))); size_type _Off = _Insert_x(_Where, _Count); _Copy_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), begin() + static_cast(_Off)); } - iterator erase(const_iterator _Where_arg) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const_iterator _Where_arg) noexcept /* strengthened */ { iterator _Where = _Make_iter(_Where_arg); difference_type _Off = _Where - begin(); @@ -2709,7 +2942,8 @@ public: return begin() + _Off; } - iterator erase(const_iterator _First_arg, const_iterator _Last_arg) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const_iterator _First_arg, const_iterator _Last_arg) noexcept + /* strengthened */ { iterator _First = _Make_iter(_First_arg); iterator _Last = _Make_iter(_Last_arg); difference_type _Off = _First - begin(); @@ -2730,13 +2964,13 @@ public: return begin() + _Off; } - void clear() noexcept { + _CONSTEXPR20_CONTAINER void clear() noexcept { this->_Orphan_all(); this->_Myvec.clear(); this->_Mysize = 0; } - void flip() noexcept { // toggle all elements + _CONSTEXPR20_CONTAINER void flip() noexcept { // toggle all elements for (auto& _Elem : this->_Myvec) { _Elem = ~_Elem; } @@ -2744,7 +2978,7 @@ public: _Trim(this->_Mysize); } - void swap(vector& _Right) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void swap(vector& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { this->_Swap_proxy_and_iterators(_Right); this->_Myvec.swap(_Right._Myvec); @@ -2752,7 +2986,7 @@ public: } } - static void swap(reference _Left, reference _Right) noexcept { + static _CONSTEXPR20_CONTAINER void swap(reference _Left, reference _Right) noexcept { bool _Val = _Left; // NOT _STD swap _Left = _Right; _Right = _Val; @@ -2760,14 +2994,14 @@ public: friend hash>; - iterator _Insert_n(const_iterator _Where, size_type _Count, const bool& _Val) { + _CONSTEXPR20_CONTAINER iterator _Insert_n(const_iterator _Where, size_type _Count, const bool& _Val) { size_type _Off = _Insert_x(_Where, _Count); const auto _Result = begin() + static_cast(_Off); _STD fill(_Result, _Result + static_cast(_Count), _Val); return _Result; } - size_type _Insert_x(const_iterator _Where, size_type _Count) { + _CONSTEXPR20_CONTAINER size_type _Insert_x(const_iterator _Where, size_type _Count) { difference_type _Off = _Where - begin(); #if _ITERATOR_DEBUG_LEVEL == 2 @@ -2799,28 +3033,46 @@ public: } #if _ITERATOR_DEBUG_LEVEL == 2 - void _Orphan_range(size_type _Offlo, size_type _Offhi) const { - _Lockit _Lock(_LOCK_DEBUG); + _CONSTEXPR20_CONTAINER void _Orphan_range_unlocked(size_type _Offlo, size_type _Offhi) const { const auto _Base = this->_Myvec.data(); _Iterator_base12** _Pnext = &this->_Myproxy->_Myfirstiter; while (*_Pnext) { // test offset from beginning of vector const auto& _Pnextiter = static_cast(**_Pnext); - const auto _Off = static_cast(_VBITS * (_Pnextiter._Myptr - _Base)) + _Pnextiter._Myoff; + const auto _Temp = *_Pnext; // TRANSITION, VSO-1269037 + if (!_Pnextiter._Myptr) { // orphan the iterator + _Temp->_Myproxy = nullptr; + *_Pnext = _Temp->_Mynextiter; + continue; + } + const auto _Off = static_cast(_VBITS * (_Pnextiter._Myptr - _Base)) + _Pnextiter._Myoff; if (_Off < _Offlo || _Offhi < _Off) { - _Pnext = &(*_Pnext)->_Mynextiter; + _Pnext = &_Temp->_Mynextiter; } else { // orphan the iterator - (*_Pnext)->_Myproxy = nullptr; - *_Pnext = (*_Pnext)->_Mynextiter; + _Temp->_Myproxy = nullptr; + *_Pnext = _Temp->_Mynextiter; } } } -#else // _ITERATOR_DEBUG_LEVEL == 2 - void _Orphan_range(size_type, size_type) const {} + void _Orphan_range_locked(size_type _Offlo, size_type _Offhi) const { + _Lockit _Lock(_LOCK_DEBUG); + _Orphan_range_unlocked(_Offlo, _Offhi); + } + + _CONSTEXPR20_CONTAINER void _Orphan_range(size_type _Offlo, size_type _Offhi) const { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _Orphan_range_unlocked(_Offlo, _Offhi); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Orphan_range_locked(_Offlo, _Offhi); + } + } #endif // _ITERATOR_DEBUG_LEVEL == 2 - void _Trim(size_type _Size) { + _CONSTEXPR20_CONTAINER void _Trim(size_type _Size) { if (max_size() < _Size) { _Xlen(); // result too long } @@ -2846,16 +3098,6 @@ public: } }; -template -_NODISCARD bool operator==(const vector& _Left, const vector& _Right) { - return _Left.size() == _Right.size() && _Left._Myvec == _Right._Myvec; -} - -template -_NODISCARD bool operator!=(const vector& _Left, const vector& _Right) { - return !(_Left == _Right); -} - // STRUCT TEMPLATE SPECIALIZATION hash template struct hash> { @@ -2867,18 +3109,6 @@ struct hash> { } }; -#if _HAS_CXX20 -template -typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { - return _Erase_remove(_Cont, _Val); -} - -template -typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { - return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); -} -#endif // _HAS_CXX20 - #if _HAS_CXX17 namespace pmr { template @@ -2920,13 +3150,13 @@ _CONSTEXPR20 void _Fill_vbool(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) { *_VbFirst = (*_VbFirst & _FirstDestMask) | (_FillVal & _FirstSourceMask); ++_VbFirst; -#ifdef __cpp_lib_is_constant_evaluated +#ifdef __cpp_lib_constexpr_dynamic_alloc if (_STD is_constant_evaluated()) { for (; _VbFirst != _VbLast; ++_VbFirst) { *_VbFirst = _FillVal; } } else -#endif // __cpp_lib_is_constant_evaluated +#endif // __cpp_lib_constexpr_dynamic_alloc { const auto _VbFirst_ch = reinterpret_cast(_VbFirst); const auto _VbLast_ch = reinterpret_cast(_VbLast); diff --git a/stl/inc/xatomic.h b/stl/inc/xatomic.h index af41c5af67a..a03ba25dde4 100644 --- a/stl/inc/xatomic.h +++ b/stl/inc/xatomic.h @@ -28,7 +28,11 @@ _STL_DISABLE_CLANG_WARNINGS #define _INTRIN_ACQUIRE(x) x #define _INTRIN_RELEASE(x) x #define _INTRIN_ACQ_REL(x) x +#ifdef _M_CEE_PURE #define _YIELD_PROCESSOR() +#else // ^^^ _M_CEE_PURE / !_M_CEE_PURE vvv +#define _YIELD_PROCESSOR() _mm_pause() +#endif // ^^^ !_M_CEE_PURE ^^^ #elif defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) #define _INTRIN_RELAXED(x) _CONCAT(x, _nf) diff --git a/stl/inc/xcharconv.h b/stl/inc/xcharconv.h index ecd69b6d02e..fff630353ae 100644 --- a/stl/inc/xcharconv.h +++ b/stl/inc/xcharconv.h @@ -39,6 +39,9 @@ _BITMASK_OPS(chars_format) struct to_chars_result { char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const to_chars_result&, const to_chars_result&) = default; +#endif // _HAS_CXX20 }; _STD_END diff --git a/stl/inc/xcharconv_ryu.h b/stl/inc/xcharconv_ryu.h index 1b2b95a8e05..5ed23a839db 100644 --- a/stl/inc/xcharconv_ryu.h +++ b/stl/inc/xcharconv_ryu.h @@ -1121,7 +1121,7 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con // Step 4: Find the shortest decimal representation in the interval of valid representations. int32_t __removed = 0; - uint32_t __output; + uint32_t _Output; if (__vmIsTrailingZeros || __vrIsTrailingZeros) { // General case, which happens rarely (~4.0%). while (__vp / 10 > __vm / 10) { @@ -1152,7 +1152,7 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con __lastRemovedDigit = 4; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); + _Output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); } else { // Specialized for the common case (~96.0%). Percentages below are relative to this. // Loop iterations below (approximately): @@ -1165,13 +1165,13 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con ++__removed; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + (__vr == __vm || __lastRemovedDigit >= 5); + _Output = __vr + (__vr == __vm || __lastRemovedDigit >= 5); } const int32_t __exp = __e10 + __removed; __floating_decimal_32 __fd; __fd.__exponent = __exp; - __fd.__mantissa = __output; + __fd.__mantissa = _Output; return __fd; } @@ -1302,9 +1302,9 @@ _NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, ch _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_32 __v, chars_format _Fmt, const uint32_t __ieeeMantissa, const uint32_t __ieeeExponent) { // Step 5: Print the decimal representation. - uint32_t __output = __v.__mantissa; + uint32_t _Output = __v.__mantissa; int32_t _Ryu_exponent = __v.__exponent; - const uint32_t __olength = __decimalLength9(__output); + const uint32_t __olength = __decimalLength9(_Output); int32_t _Scientific_exponent = _Ryu_exponent + static_cast(__olength) - 1; if (_Fmt == chars_format{}) { @@ -1344,7 +1344,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La } if (_Fmt == chars_format::fixed) { - // Example: __output == 1729, __olength == 4 + // Example: _Output == 1729, __olength == 4 // _Ryu_exponent | Printed | _Whole_digits | _Total_fixed_length | Notes // --------------|----------|---------------|----------------------|--------------------------------------- @@ -1366,7 +1366,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La uint32_t _Total_fixed_length; if (_Ryu_exponent >= 0) { // cases "172900" and "1729" _Total_fixed_length = static_cast(_Whole_digits); - if (__output == 1) { + if (_Output == 1) { // Rounding can affect the number of digits. // For example, 1e11f is exactly "99999997952" which is 11 digits instead of 12. // We can use a lookup table to detect this and adjust the total length. @@ -1439,28 +1439,28 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _Mid = _First + _Total_fixed_length; } - while (__output >= 10000) { + while (_Output >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 - const uint32_t __c = __output - 10000 * (__output / 10000); + const uint32_t __c = _Output - 10000 * (_Output / 10000); #else - const uint32_t __c = __output % 10000; + const uint32_t __c = _Output % 10000; #endif - __output /= 10000; + _Output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); } - if (__output >= 100) { - const uint32_t __c = (__output % 100) << 1; - __output /= 100; + if (_Output >= 100) { + const uint32_t __c = (_Output % 100) << 1; + _Output /= 100; _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); } - if (__output >= 10) { - const uint32_t __c = __output << 1; + if (_Output >= 10) { + const uint32_t __c = _Output << 1; _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); } else { - *--_Mid = static_cast('0' + __output); + *--_Mid = static_cast('0' + _Output); } if (_Ryu_exponent > 0) { // case "172900" with _Can_use_ryu @@ -1491,32 +1491,32 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La // Print the decimal digits. uint32_t __i = 0; - while (__output >= 10000) { + while (_Output >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 - const uint32_t __c = __output - 10000 * (__output / 10000); + const uint32_t __c = _Output - 10000 * (_Output / 10000); #else - const uint32_t __c = __output % 10000; + const uint32_t __c = _Output % 10000; #endif - __output /= 10000; + _Output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); __i += 4; } - if (__output >= 100) { - const uint32_t __c = (__output % 100) << 1; - __output /= 100; + if (_Output >= 100) { + const uint32_t __c = (_Output % 100) << 1; + _Output /= 100; _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c, 2); __i += 2; } - if (__output >= 10) { - const uint32_t __c = __output << 1; + if (_Output >= 10) { + const uint32_t __c = _Output << 1; // We can't use memcpy here: the decimal dot goes between these two digits. __result[2] = __DIGIT_TABLE[__c + 1]; __result[0] = __DIGIT_TABLE[__c]; } else { - __result[0] = static_cast('0' + __output); + __result[0] = static_cast('0' + _Output); } // Print decimal point if needed. @@ -1809,7 +1809,7 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con // Step 4: Find the shortest decimal representation in the interval of valid representations. int32_t __removed = 0; uint8_t __lastRemovedDigit = 0; - uint64_t __output; + uint64_t _Output; // On average, we remove ~2 digits. if (__vmIsTrailingZeros || __vrIsTrailingZeros) { // General case, which happens rarely (~0.7%). @@ -1853,7 +1853,7 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con __lastRemovedDigit = 4; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); + _Output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); } else { // Specialized for the common case (~99.3%). Percentages below are relative to this. bool __roundUp = false; @@ -1887,22 +1887,22 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con ++__removed; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + (__vr == __vm || __roundUp); + _Output = __vr + (__vr == __vm || __roundUp); } const int32_t __exp = __e10 + __removed; __floating_decimal_64 __fd; __fd.__exponent = __exp; - __fd.__mantissa = __output; + __fd.__mantissa = _Output; return __fd; } _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_64 __v, chars_format _Fmt, const double __f) { // Step 5: Print the decimal representation. - uint64_t __output = __v.__mantissa; + uint64_t _Output = __v.__mantissa; int32_t _Ryu_exponent = __v.__exponent; - const uint32_t __olength = __decimalLength17(__output); + const uint32_t __olength = __decimalLength17(_Output); int32_t _Scientific_exponent = _Ryu_exponent + static_cast(__olength) - 1; if (_Fmt == chars_format{}) { @@ -1942,7 +1942,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La } if (_Fmt == chars_format::fixed) { - // Example: __output == 1729, __olength == 4 + // Example: _Output == 1729, __olength == 4 // _Ryu_exponent | Printed | _Whole_digits | _Total_fixed_length | Notes // --------------|----------|---------------|----------------------|--------------------------------------- @@ -1964,7 +1964,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La uint32_t _Total_fixed_length; if (_Ryu_exponent >= 0) { // cases "172900" and "1729" _Total_fixed_length = static_cast(_Whole_digits); - if (__output == 1) { + if (_Output == 1) { // Rounding can affect the number of digits. // For example, 1e23 is exactly "99999999999999991611392" which is 23 digits instead of 24. // We can use a lookup table to detect this and adjust the total length. @@ -2056,13 +2056,13 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La // We prefer 32-bit operations, even on 64-bit platforms. // We have at most 17 digits, and uint32_t can store 9 digits. - // If __output doesn't fit into uint32_t, we cut off 8 digits, + // If _Output doesn't fit into uint32_t, we cut off 8 digits, // so the rest will fit into uint32_t. - if ((__output >> 32) != 0) { + if ((_Output >> 32) != 0) { // Expensive 64-bit division. - const uint64_t __q = __div1e8(__output); - uint32_t __output2 = static_cast(__output - 100000000 * __q); - __output = __q; + const uint64_t __q = __div1e8(_Output); + uint32_t __output2 = static_cast(_Output - 100000000 * __q); + _Output = __q; const uint32_t __c = __output2 % 10000; __output2 /= 10000; @@ -2077,7 +2077,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d0, 2); _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d1, 2); } - uint32_t __output2 = static_cast(__output); + uint32_t __output2 = static_cast(_Output); while (__output2 >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 const uint32_t __c = __output2 - 10000 * (__output2 / 10000); @@ -2132,13 +2132,13 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La uint32_t __i = 0; // We prefer 32-bit operations, even on 64-bit platforms. // We have at most 17 digits, and uint32_t can store 9 digits. - // If __output doesn't fit into uint32_t, we cut off 8 digits, + // If _Output doesn't fit into uint32_t, we cut off 8 digits, // so the rest will fit into uint32_t. - if ((__output >> 32) != 0) { + if ((_Output >> 32) != 0) { // Expensive 64-bit division. - const uint64_t __q = __div1e8(__output); - uint32_t __output2 = static_cast(__output) - 100000000 * static_cast(__q); - __output = __q; + const uint64_t __q = __div1e8(_Output); + uint32_t __output2 = static_cast(_Output) - 100000000 * static_cast(__q); + _Output = __q; const uint32_t __c = __output2 % 10000; __output2 /= 10000; @@ -2153,7 +2153,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _CSTD memcpy(__result + __olength - __i - 7, __DIGIT_TABLE + __d1, 2); __i += 8; } - uint32_t __output2 = static_cast(__output); + uint32_t __output2 = static_cast(_Output); while (__output2 >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 const uint32_t __c = __output2 - 10000 * (__output2 / 10000); diff --git a/stl/inc/xhash b/stl/inc/xhash index 21f0901e804..a7fc07566a0 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -196,12 +196,14 @@ struct _Reinterpret_move_iter { return _Lhs._Base == _Rhs._Base; } +#if !_HAS_CXX20 #ifndef __CUDACC__ // TRANSITION, VSO-568006 _NODISCARD #endif // TRANSITION, VSO-568006 friend bool operator!=(const _Reinterpret_move_iter& _Lhs, const _Reinterpret_move_iter& _Rhs) { return _Lhs._Base != _Rhs._Base; } +#endif // !_HAS_CXX20 }; // STRUCT TEMPLATE _List_head_construct_ptr @@ -1841,6 +1843,7 @@ protected: } } + template = 0> _NODISCARD bool _Multi_equal(const _Hash& _Right) const { static_assert(_Traits::_Multi, "This function only works with multi containers"); _STL_INTERNAL_CHECK(this->size() == _Right.size()); diff --git a/stl/inc/xkeycheck.h b/stl/inc/xkeycheck.h index 6baa330a1fd..60ab98f3e21 100644 --- a/stl/inc/xkeycheck.h +++ b/stl/inc/xkeycheck.h @@ -6,7 +6,10 @@ #pragma once #ifndef _XKEYCHECK_H #define _XKEYCHECK_H -#include + +// xkeycheck.h assumes that it's being included by yvals_core.h in a specific order. +// Nothing else should include xkeycheck.h. + #if _STL_COMPILER_PREPROCESSOR #if defined(__cplusplus) && !defined(_ALLOW_KEYWORD_MACROS) && !defined(__INTELLISENSE__) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 326a0db4703..5fe52ccd945 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -389,9 +389,11 @@ public: return _Ptr == _Loc._Ptr || (name().compare("*") != 0 && name().compare(_Loc.name()) == 0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const locale& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 static _MRTIMP2_PURE const locale& __CLRCALL_PURE_OR_CDECL classic(); // classic "C" locale @@ -3242,7 +3244,7 @@ protected: }; // FUNCTION TEMPLATE _Getloctxt -enum class _Case_sensitive : bool { _No, _Yes }; +enum class _Case_sensitive : bool { _Nope, _Yes }; template int __CRTDECL _Getloctxt( diff --git a/stl/inc/xloctime b/stl/inc/xloctime index 6edfeb10649..b29d17822bc 100644 --- a/stl/inc/xloctime +++ b/stl/inc/xloctime @@ -93,37 +93,38 @@ public: _State = ios_base::goodbit; for (; _Fmtfirst != _Fmtlast; ++_Fmtfirst) { - if (_Ctype_fac.narrow(*_Fmtfirst) != '%') { // match literal element + if (_State != ios_base::goodbit) { + // N4878 [locale.time.get.members]/8.2 + // _State is fail, eof, or bad. Do not proceed to the next fields. Return with current _State. + break; + } else if (_First == _Last) { + // N4878 [locale.time.get.members]/8.3 + _State = ios_base::eofbit | ios_base::failbit; + break; + } else if (_Ctype_fac.narrow(*_Fmtfirst) != '%') { // match literal element if (_Ctype_fac.is(_Ctype::space, *_Fmtfirst)) { while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) { ++_First; } - } else if (*_First != *_Fmtfirst) { // bad literal match + } else if (_Ctype_fac.tolower(*_First) != _Ctype_fac.tolower(*_Fmtfirst)) { // bad literal match _State |= ios_base::failbit; break; } else { ++_First; } - } else if (++_Fmtfirst == _Fmtlast) { // treat trailing % as literal match - if (*_First != _Fmtfirst[-1]) { - _State |= ios_base::failbit; - } else { - ++_First; - } + } else if (++_Fmtfirst == _Fmtlast) { + // N4878 [locale.time.get.members]/8.4: "If the number of elements in the range [fmt, fmtend) is not + // sufficient to unambiguously determine whether the conversion specification is complete and valid, + // the function evaluates err = ios_base::failbit." + _State = ios_base::failbit; break; } else { // get specifier after % char _Specifier = _Ctype_fac.narrow(*_Fmtfirst); char _Modifier = '\0'; - _Elem _Percent = _Fmtfirst[-1]; if (_Specifier == 'E' || _Specifier == 'O' || _Specifier == 'Q' || _Specifier == '#') { - if (++_Fmtfirst == _Fmtlast) { // no specifier, treat %[E0Q#] as literal match - if (*_First != _Percent || ++_First == _Last || _Ctype_fac.narrow(*_First) != _Specifier) { - _State |= ios_base::failbit; - } else { - ++_First; - } - + if (++_Fmtfirst == _Fmtlast) { // no specifier + _State = ios_base::failbit; break; } else { // save both qualifier and specifier _Modifier = _Specifier; @@ -132,18 +133,9 @@ public: } _First = do_get(_First, _Last, _Iosbase, _State, _Pt, _Specifier, _Modifier); // convert a single field - - if (_State != ios_base::goodbit) { - // _State is fail, eof, or bad. Do not proceed to the next fields. Return with current _State. - break; - } } } - if (_First == _Last) { - _State |= ios_base::eofbit; - } - return _First; } @@ -223,7 +215,7 @@ protected: if (_State != ios_base::goodbit || _Ctype_fac.narrow(*_First) != ':') { _State |= ios_base::failbit; // min field is bad } else { - _State |= _Getint(++_First, _Last, 0, 59, _Pt->tm_sec, _Ctype_fac); + _State |= _Getint(++_First, _Last, 0, 60, _Pt->tm_sec, _Ctype_fac); } return _First; @@ -326,7 +318,7 @@ protected: virtual _InIt __CLR_OR_THIS_CALL do_get_weekday(_InIt _First, _InIt _Last, ios_base&, ios_base::iostate& _State, tm* _Pt) const { // get weekday from [_First, _Last) into _Pt - int _Num = _Getloctxt(_First, _Last, 0, _Days, _Case_sensitive::_No); + int _Num = _Getloctxt(_First, _Last, 0, _Days, _Case_sensitive::_Nope); if (_Num < 0) { _State |= ios_base::failbit; } else { @@ -338,7 +330,7 @@ protected: virtual _InIt __CLR_OR_THIS_CALL do_get_monthname(_InIt _First, _InIt _Last, ios_base&, ios_base::iostate& _State, tm* _Pt) const { // get month from [_First, _Last) into _Pt - int _Num = _Getloctxt(_First, _Last, 0, _Months, _Case_sensitive::_No); + int _Num = _Getloctxt(_First, _Last, 0, _Months, _Case_sensitive::_Nope); if (_Num < 0) { _State |= ios_base::failbit; @@ -444,7 +436,7 @@ protected: break; case 'p': - _Ans = _Getloctxt(_First, _Last, 0, ":AM:am:PM:pm", _Case_sensitive::_No); + _Ans = _Getloctxt(_First, _Last, 0, ":AM:am:PM:pm", _Case_sensitive::_Nope); if (_Ans < 0) { _State |= ios_base::failbit; } else if (1 < _Ans) { @@ -546,7 +538,14 @@ private: char* _Ptr = _Ac; char _Ch; - if (_First != _Last) { + int _Digits_seen = 0; + + while (_First != _Last && _Digits_seen < _Hi_digits && _Ctype_fac.is(ctype_base::space, *_First)) { + ++_First; + ++_Digits_seen; + } + + if (_First != _Last && _Digits_seen < _Hi_digits) { if ((_Ch = _Ctype_fac.narrow(*_First)) == '+') { // copy plus sign *_Ptr++ = '+'; ++_First; @@ -556,9 +555,8 @@ private: } } - int _Digits_seen = 0; - - for (; _First != _Last && _Ctype_fac.narrow(*_First) == '0'; ++_First) { // strip leading zeros + for (; _First != _Last && _Digits_seen < _Hi_digits && _Ctype_fac.narrow(*_First) == '0'; + ++_First) { // strip leading zeros ++_Digits_seen; } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index de10851b299..68391f97693 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -15,6 +15,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -27,7 +31,7 @@ _STD_BEGIN template struct _NODISCARD _Tidy_guard { // class with destructor that calls _Tidy _Ty* _Target; - ~_Tidy_guard() { + _CONSTEXPR20_DYNALLOC ~_Tidy_guard() { if (_Target) { _Target->_Tidy(); } @@ -38,7 +42,7 @@ struct _NODISCARD _Tidy_guard { // class with destructor that calls _Tidy template struct _NODISCARD _Tidy_deallocate_guard { // class with destructor that calls _Tidy_deallocate _Ty* _Target; - ~_Tidy_deallocate_guard() { + _CONSTEXPR20_DYNALLOC ~_Tidy_deallocate_guard() { if (_Target) { _Target->_Tidy_deallocate(); } @@ -286,12 +290,12 @@ using _Rebind_pointer_t = typename pointer_traits<_Ptr>::template rebind<_Ty>; // FUNCTION TEMPLATE _Refancy template , int> = 0> -_Pointer _Refancy(typename pointer_traits<_Pointer>::element_type* _Ptr) noexcept { +_CONSTEXPR20 _Pointer _Refancy(typename pointer_traits<_Pointer>::element_type* _Ptr) noexcept { return pointer_traits<_Pointer>::pointer_to(*_Ptr); } template , int> = 0> -_Pointer _Refancy(_Pointer _Ptr) noexcept { +_CONSTEXPR20 _Pointer _Refancy(_Pointer _Ptr) noexcept { return _Ptr; } @@ -788,16 +792,20 @@ public: using value_type = _Ty; +#if _HAS_DEPRECATED_ALLOCATOR_MEMBERS _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef _Ty* pointer; _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef const _Ty* const_pointer; _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef _Ty& reference; _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef const _Ty& const_reference; +#endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS using size_type = size_t; using difference_type = ptrdiff_t; - using propagate_on_container_move_assignment = true_type; + using propagate_on_container_move_assignment = true_type; + +#if _HAS_DEPRECATED_ALLOCATOR_MEMBERS using is_always_equal _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS = true_type; template @@ -812,6 +820,7 @@ public: _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS _NODISCARD const _Ty* address(const _Ty& _Val) const noexcept { return _STD addressof(_Val); } +#endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS constexpr allocator() noexcept {} @@ -830,6 +839,7 @@ public: return static_cast<_Ty*>(_Allocate<_New_alignof<_Ty>>(_Get_size_of_n(_Count))); } +#if _HAS_DEPRECATED_ALLOCATOR_MEMBERS _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS _NODISCARD __declspec(allocator) _Ty* allocate( _CRT_GUARDOVERFLOW const size_t _Count, const void*) { return allocate(_Count); @@ -837,7 +847,7 @@ public: template _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS void construct(_Objty* const _Ptr, _Types&&... _Args) { - ::new (const_cast(static_cast(_Ptr))) _Objty(_STD forward<_Types>(_Args)...); + ::new (_Voidify_iter(_Ptr)) _Objty(_STD forward<_Types>(_Args)...); } template @@ -848,6 +858,7 @@ public: _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS _NODISCARD size_t max_size() const noexcept { return static_cast(-1) / sizeof(_Ty); } +#endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS }; // CLASS allocator @@ -855,19 +866,24 @@ template <> class allocator { public: using value_type = void; +#if _HAS_DEPRECATED_ALLOCATOR_MEMBERS _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef void* pointer; _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef const void* const_pointer; +#endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS using size_type = size_t; using difference_type = ptrdiff_t; - using propagate_on_container_move_assignment = true_type; + using propagate_on_container_move_assignment = true_type; + +#if _HAS_DEPRECATED_ALLOCATOR_MEMBERS using is_always_equal _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS = true_type; template struct _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS rebind { using other = allocator<_Other>; }; +#endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS }; template @@ -875,10 +891,12 @@ _NODISCARD _CONSTEXPR20_DYNALLOC bool operator==(const allocator<_Ty>&, const al return true; } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20_DYNALLOC bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { +_NODISCARD bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { return false; } +#endif // !_HAS_CXX20 #if _HAS_CXX17 // ALIAS TEMPLATE _Guide_size_type_t FOR DEDUCTION GUIDES, N4687 26.5.4.1 [unord.map.overview]/4 @@ -897,7 +915,7 @@ using _Alloc_size_t = typename allocator_traits<_Alloc>::size_type; // FUNCTION TEMPLATE _Pocca template -void _Pocca(_Alloc& _Left, const _Alloc& _Right) noexcept { +_CONSTEXPR20 void _Pocca(_Alloc& _Left, const _Alloc& _Right) noexcept { if constexpr (allocator_traits<_Alloc>::propagate_on_container_copy_assignment::value) { _Left = _Right; } @@ -905,7 +923,7 @@ void _Pocca(_Alloc& _Left, const _Alloc& _Right) noexcept { // FUNCTION TEMPLATE _Pocma template -void _Pocma(_Alloc& _Left, _Alloc& _Right) noexcept { // (maybe) propagate on container move assignment +_CONSTEXPR20 void _Pocma(_Alloc& _Left, _Alloc& _Right) noexcept { // (maybe) propagate on container move assignment if constexpr (allocator_traits<_Alloc>::propagate_on_container_move_assignment::value) { _Left = _STD move(_Right); } @@ -913,7 +931,7 @@ void _Pocma(_Alloc& _Left, _Alloc& _Right) noexcept { // (maybe) propagate on co // FUNCTION TEMPLATE _Pocs template -void _Pocs(_Alloc& _Left, _Alloc& _Right) noexcept { +_CONSTEXPR20 void _Pocs(_Alloc& _Left, _Alloc& _Right) noexcept { if constexpr (allocator_traits<_Alloc>::propagate_on_container_swap::value) { _Swap_adl(_Left, _Right); } else { @@ -923,7 +941,8 @@ void _Pocs(_Alloc& _Left, _Alloc& _Right) noexcept { // FUNCTION TEMPLATE _Destroy_range WITH ALLOC template -void _Destroy_range(_Alloc_ptr_t<_Alloc> _First, const _Alloc_ptr_t<_Alloc> _Last, _Alloc& _Al) noexcept { +_CONSTEXPR20_DYNALLOC void _Destroy_range( + _Alloc_ptr_t<_Alloc> _First, const _Alloc_ptr_t<_Alloc> _Last, _Alloc& _Al) noexcept { // note that this is an optimization for debug mode codegen; in release mode the BE removes all of this using _Ty = typename _Alloc::value_type; if constexpr (!conjunction_v, _Uses_default_destroy<_Alloc, _Ty*>>) { @@ -963,7 +982,7 @@ _NODISCARD constexpr size_t _Convert_size(const size_t _Len) noexcept { // FUNCTION TEMPLATE _Deallocate_plain template -void _Deallocate_plain(_Alloc& _Al, typename _Alloc::value_type* const _Ptr) noexcept { +_CONSTEXPR20_DYNALLOC void _Deallocate_plain(_Alloc& _Al, typename _Alloc::value_type* const _Ptr) noexcept { // deallocate a plain pointer using an allocator using _Alloc_traits = allocator_traits<_Alloc>; if constexpr (is_same_v<_Alloc_ptr_t<_Alloc>, typename _Alloc::value_type*>) { @@ -976,7 +995,7 @@ void _Deallocate_plain(_Alloc& _Al, typename _Alloc::value_type* const _Ptr) noe // FUNCTION TEMPLATE _Delete_plain_internal template -void _Delete_plain_internal(_Alloc& _Al, typename _Alloc::value_type* const _Ptr) noexcept { +_CONSTEXPR20_DYNALLOC void _Delete_plain_internal(_Alloc& _Al, typename _Alloc::value_type* const _Ptr) noexcept { // destroy *_Ptr in place, then deallocate _Ptr using _Al; used for internal container types the user didn't name using _Ty = typename _Alloc::value_type; _Ptr->~_Ty(); @@ -990,18 +1009,18 @@ struct _Alloc_construct_ptr { // pointer used to help construct 1 _Alloc::value_ _Alloc& _Al; pointer _Ptr; - explicit _Alloc_construct_ptr(_Alloc& _Al_) : _Al(_Al_), _Ptr(nullptr) {} + _CONSTEXPR20_DYNALLOC explicit _Alloc_construct_ptr(_Alloc& _Al_) : _Al(_Al_), _Ptr(nullptr) {} - _NODISCARD pointer _Release() noexcept { // disengage *this and return contained pointer + _NODISCARD _CONSTEXPR20_DYNALLOC pointer _Release() noexcept { // disengage *this and return contained pointer return _STD exchange(_Ptr, nullptr); } - void _Allocate() { // disengage *this, then allocate a new memory block + _CONSTEXPR20_DYNALLOC void _Allocate() { // disengage *this, then allocate a new memory block _Ptr = nullptr; // if allocate throws, prevents double-free _Ptr = _Al.allocate(1); } - ~_Alloc_construct_ptr() { // if this instance is engaged, deallocate storage + _CONSTEXPR20_DYNALLOC ~_Alloc_construct_ptr() { // if this instance is engaged, deallocate storage if (_Ptr) { _Al.deallocate(_Ptr, 1); } @@ -1015,15 +1034,15 @@ struct _Alloc_construct_ptr { // pointer used to help construct 1 _Alloc::value_ struct _Fake_allocator {}; struct _Container_base0 { - void _Orphan_all() noexcept {} - void _Swap_proxy_and_iterators(_Container_base0&) noexcept {} - void _Alloc_proxy(const _Fake_allocator&) noexcept {} - void _Reload_proxy(const _Fake_allocator&, const _Fake_allocator&) noexcept {} + _CONSTEXPR20_CONTAINER void _Orphan_all() noexcept {} + _CONSTEXPR20_CONTAINER void _Swap_proxy_and_iterators(_Container_base0&) noexcept {} + _CONSTEXPR20_CONTAINER void _Alloc_proxy(const _Fake_allocator&) noexcept {} + _CONSTEXPR20_CONTAINER void _Reload_proxy(const _Fake_allocator&, const _Fake_allocator&) noexcept {} }; struct _Iterator_base0 { - void _Adopt(const void*) noexcept {} - const _Container_base0* _Getcont() const noexcept { + _CONSTEXPR20_CONTAINER void _Adopt(const void*) noexcept {} + _CONSTEXPR20_CONTAINER const _Container_base0* _Getcont() const noexcept { return nullptr; } @@ -1033,25 +1052,25 @@ struct _Iterator_base0 { // CLASS _Container_proxy struct _Container_base12; struct _Container_proxy { // store head of iterator chain and back pointer - _Container_proxy() noexcept : _Mycont(nullptr), _Myfirstiter(nullptr) {} - _Container_proxy(_Container_base12* _Mycont_) noexcept : _Mycont(_Mycont_), _Myfirstiter(nullptr) {} + _CONSTEXPR20_CONTAINER _Container_proxy() noexcept = default; + _CONSTEXPR20_CONTAINER _Container_proxy(_Container_base12* _Mycont_) noexcept : _Mycont(_Mycont_) {} - const _Container_base12* _Mycont; - _Iterator_base12* _Myfirstiter; + const _Container_base12* _Mycont = nullptr; + mutable _Iterator_base12* _Myfirstiter = nullptr; }; struct _Container_base12 { public: - _Container_base12() noexcept : _Myproxy(nullptr) {} + _CONSTEXPR20_CONTAINER _Container_base12() noexcept = default; _Container_base12(const _Container_base12&) = delete; _Container_base12& operator=(const _Container_base12&) = delete; - void _Orphan_all() noexcept; - void _Swap_proxy_and_iterators(_Container_base12&) noexcept; + _CONSTEXPR20_CONTAINER void _Orphan_all() noexcept; + _CONSTEXPR20_CONTAINER void _Swap_proxy_and_iterators(_Container_base12&) noexcept; template - void _Alloc_proxy(_Alloc&& _Al) { + _CONSTEXPR20_CONTAINER void _Alloc_proxy(_Alloc&& _Al) { _Container_proxy* const _New_proxy = _Unfancy(_Al.allocate(1)); _Construct_in_place(*_New_proxy, this); _Myproxy = _New_proxy; @@ -1059,7 +1078,7 @@ public: } template - void _Reload_proxy(_Alloc&& _Old_alloc, _Alloc&& _New_alloc) { + _CONSTEXPR20_CONTAINER void _Reload_proxy(_Alloc&& _Old_alloc, _Alloc&& _New_alloc) { // pre: no iterators refer to the existing proxy _Container_proxy* const _New_proxy = _Unfancy(_New_alloc.allocate(1)); _Construct_in_place(*_New_proxy, this); @@ -1067,113 +1086,160 @@ public: _Delete_plain_internal(_Old_alloc, _STD exchange(_Myproxy, _New_proxy)); } - _Container_proxy* _Myproxy; + _Container_proxy* _Myproxy = nullptr; + +private: + _CONSTEXPR20_CONTAINER void _Orphan_all_unlocked() noexcept; + _CONSTEXPR20_CONTAINER void _Swap_proxy_and_iterators_unlocked(_Container_base12&) noexcept; + + void _Orphan_all_locked() noexcept { + _Lockit _Lock(_LOCK_DEBUG); + _Orphan_all_unlocked(); + } + + void _Swap_proxy_and_iterators_locked(_Container_base12& _Right) noexcept { + _Lockit _Lock(_LOCK_DEBUG); + _Swap_proxy_and_iterators_unlocked(_Right); + } }; struct _Iterator_base12 { // store links to container proxy, next iterator - _Iterator_base12() noexcept : _Myproxy(nullptr), _Mynextiter(nullptr) {} // construct orphaned iterator +public: + _CONSTEXPR20_CONTAINER _Iterator_base12() noexcept = default; // construct orphaned iterator - _Iterator_base12(const _Iterator_base12& _Right) noexcept : _Myproxy(nullptr), _Mynextiter(nullptr) { + _CONSTEXPR20_CONTAINER _Iterator_base12(const _Iterator_base12& _Right) noexcept { *this = _Right; } - _Iterator_base12& operator=(const _Iterator_base12& _Right) noexcept { + _CONSTEXPR20_CONTAINER _Iterator_base12& operator=(const _Iterator_base12& _Right) noexcept { if (_Myproxy != _Right._Myproxy) { if (_Right._Myproxy) { _Adopt(_Right._Myproxy->_Mycont); } else { // becoming invalid, disown current parent #if _ITERATOR_DEBUG_LEVEL == 2 - _Lockit _Lock(_LOCK_DEBUG); - _Orphan_me(); + _Orphan_me_v2(); #else // _ITERATOR_DEBUG_LEVEL == 2 _Myproxy = nullptr; #endif // _ITERATOR_DEBUG_LEVEL == 2 } } - return *this; } - ~_Iterator_base12() noexcept { #if _ITERATOR_DEBUG_LEVEL == 2 - _Lockit _Lock(_LOCK_DEBUG); - _Orphan_me(); -#endif // _ITERATOR_DEBUG_LEVEL == 2 + _CONSTEXPR20_CONTAINER ~_Iterator_base12() noexcept { + _Orphan_me_v2(); } - void _Adopt(const _Container_base12* _Parent) noexcept { - if (_Parent) { - // have a parent, do adoption + _CONSTEXPR20_CONTAINER void _Adopt(const _Container_base12* _Parent) noexcept { + if (_Parent) { // have a parent, do adoption _Container_proxy* _Parent_proxy = _Parent->_Myproxy; - -#if _ITERATOR_DEBUG_LEVEL == 2 if (_Myproxy != _Parent_proxy) { // change parentage - _Lockit _Lock(_LOCK_DEBUG); - _Orphan_me(); - _Mynextiter = _Parent_proxy->_Myfirstiter; - _Parent_proxy->_Myfirstiter = this; - _Myproxy = _Parent_proxy; +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _Adopt_unlocked(_Parent_proxy); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Adopt_locked(_Parent_proxy); + } } - -#else // _ITERATOR_DEBUG_LEVEL == 2 - _Myproxy = _Parent_proxy; -#endif // _ITERATOR_DEBUG_LEVEL == 2 - } else { - // no future parent, just disown current parent -#if _ITERATOR_DEBUG_LEVEL == 2 - _Lockit _Lock(_LOCK_DEBUG); - _Orphan_me(); -#else // _ITERATOR_DEBUG_LEVEL == 2 - _Myproxy = nullptr; -#endif // _ITERATOR_DEBUG_LEVEL == 2 + } else { // no future parent, just disown current parent + _Orphan_me_v2(); } } - const _Container_base12* _Getcont() const noexcept { - return _Myproxy ? _Myproxy->_Mycont : nullptr; - } - -#if _ITERATOR_DEBUG_LEVEL == 2 - void _Orphan_me() noexcept { + _CONSTEXPR20_CONTAINER void _Orphan_me_v2() noexcept { if (_Myproxy) { // adopted, remove self from list - _Iterator_base12** _Pnext = &_Myproxy->_Myfirstiter; - while (*_Pnext && *_Pnext != this) { - _Pnext = &(*_Pnext)->_Mynextiter; +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _Orphan_me_unlocked(); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Orphan_me_locked(); } + } + } - _STL_VERIFY(*_Pnext, "ITERATOR LIST CORRUPTED!"); - *_Pnext = _Mynextiter; +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 ^^^ / vvv _ITERATOR_DEBUG_LEVEL != 2 vvv + _CONSTEXPR20_CONTAINER void _Adopt(const _Container_base12* _Parent) noexcept { + if (_Parent) { // have a parent, do adoption + _Myproxy = _Parent->_Myproxy; + } else { // no future parent, just disown current parent _Myproxy = nullptr; } } -#endif // _ITERATOR_DEBUG_LEVEL == 2 +#endif // _ITERATOR_DEBUG_LEVEL != 2 + + _CONSTEXPR20_CONTAINER const _Container_base12* _Getcont() const noexcept { + return _Myproxy ? _Myproxy->_Mycont : nullptr; + } static constexpr bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; - _Container_proxy* _Myproxy; - _Iterator_base12* _Mynextiter; -}; + mutable _Container_proxy* _Myproxy = nullptr; + mutable _Iterator_base12* _Mynextiter = nullptr; -// MEMBER FUNCTIONS FOR _Container_base12 -inline void _Container_base12::_Orphan_all() noexcept { #if _ITERATOR_DEBUG_LEVEL == 2 - if (_Myproxy) { // proxy allocated, drain it +private: + _CONSTEXPR20_CONTAINER void _Adopt_unlocked(_Container_proxy* _Parent_proxy) noexcept { + if (_Myproxy) { // adopted, remove self from list + _Orphan_me_unlocked(); + } + _Mynextiter = _Parent_proxy->_Myfirstiter; + _Parent_proxy->_Myfirstiter = this; + _Myproxy = _Parent_proxy; + } + + void _Adopt_locked(_Container_proxy* _Parent_proxy) noexcept { _Lockit _Lock(_LOCK_DEBUG); + _Adopt_unlocked(_Parent_proxy); + } - for (auto _Pnext = &_Myproxy->_Myfirstiter; *_Pnext; *_Pnext = (*_Pnext)->_Mynextiter) { - (*_Pnext)->_Myproxy = nullptr; + _CONSTEXPR20_CONTAINER void _Orphan_me_unlocked() noexcept { + _Iterator_base12** _Pnext = &_Myproxy->_Myfirstiter; + while (*_Pnext && *_Pnext != this) { + const auto _Temp = *_Pnext; // TRANSITION, VSO-1269037 + _Pnext = &_Temp->_Mynextiter; } - _Myproxy->_Myfirstiter = nullptr; + _STL_VERIFY(*_Pnext, "ITERATOR LIST CORRUPTED!"); + *_Pnext = _Mynextiter; + _Myproxy = nullptr; + } + + void _Orphan_me_locked() noexcept { + _Lockit _Lock(_LOCK_DEBUG); + _Orphan_me_unlocked(); } #endif // _ITERATOR_DEBUG_LEVEL == 2 +}; + +// MEMBER FUNCTIONS FOR _Container_base12 +_CONSTEXPR20_CONTAINER void _Container_base12::_Orphan_all_unlocked() noexcept { + for (auto& _Pnext = _Myproxy->_Myfirstiter; _Pnext; _Pnext = _Pnext->_Mynextiter) { // TRANSITION, VSO-1269037 + _Pnext->_Myproxy = nullptr; + } + _Myproxy->_Myfirstiter = nullptr; } -inline void _Container_base12::_Swap_proxy_and_iterators(_Container_base12& _Right) noexcept { +_CONSTEXPR20_CONTAINER void _Container_base12::_Orphan_all() noexcept { #if _ITERATOR_DEBUG_LEVEL == 2 - _Lockit _Lock(_LOCK_DEBUG); + if (_Myproxy) { // proxy allocated, drain it +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _Orphan_all_unlocked(); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Orphan_all_locked(); + } + } #endif // _ITERATOR_DEBUG_LEVEL == 2 +} +_CONSTEXPR20_CONTAINER void _Container_base12::_Swap_proxy_and_iterators_unlocked(_Container_base12& _Right) noexcept { _Container_proxy* _Temp = _Myproxy; _Myproxy = _Right._Myproxy; _Right._Myproxy = _Temp; @@ -1187,6 +1253,21 @@ inline void _Container_base12::_Swap_proxy_and_iterators(_Container_base12& _Rig } } +_CONSTEXPR20_CONTAINER void _Container_base12::_Swap_proxy_and_iterators(_Container_base12& _Right) noexcept { +#if _ITERATOR_DEBUG_LEVEL == 2 +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _Swap_proxy_and_iterators_unlocked(_Right); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Swap_proxy_and_iterators_locked(_Right); + } +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 ^^^ / vvv _ITERATOR_DEBUG_LEVEL != 2 vvv + _Swap_proxy_and_iterators_unlocked(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 2 +} + #if _ITERATOR_DEBUG_LEVEL == 0 using _Container_base = _Container_base0; using _Iterator_base = _Iterator_base0; @@ -1203,23 +1284,23 @@ struct _Leave_proxy_unbound { struct _Fake_proxy_ptr_impl { // fake replacement for a container proxy smart pointer when no container proxy is in use _Fake_proxy_ptr_impl(const _Fake_proxy_ptr_impl&) = delete; _Fake_proxy_ptr_impl& operator=(const _Fake_proxy_ptr_impl&) = delete; - _Fake_proxy_ptr_impl(const _Fake_allocator&, _Leave_proxy_unbound) noexcept {} - _Fake_proxy_ptr_impl(const _Fake_allocator&, const _Container_base0&) noexcept {} + _CONSTEXPR20_CONTAINER _Fake_proxy_ptr_impl(const _Fake_allocator&, _Leave_proxy_unbound) noexcept {} + _CONSTEXPR20_CONTAINER _Fake_proxy_ptr_impl(const _Fake_allocator&, const _Container_base0&) noexcept {} - void _Bind(const _Fake_allocator&, _Container_base0*) noexcept {} - void _Release() noexcept {} + _CONSTEXPR20_CONTAINER void _Bind(const _Fake_allocator&, _Container_base0*) noexcept {} + _CONSTEXPR20_CONTAINER void _Release() noexcept {} }; struct _Basic_container_proxy_ptr12 { // smart pointer components for a _Container_proxy * that don't depend on the allocator - _Container_proxy* _Ptr; + _Container_proxy* _Ptr = nullptr; - void _Release() noexcept { // disengage this _Basic_container_proxy_ptr12 + constexpr void _Release() noexcept { // disengage this _Basic_container_proxy_ptr12 _Ptr = nullptr; } protected: - _Basic_container_proxy_ptr12() = default; + _CONSTEXPR20_CONTAINER _Basic_container_proxy_ptr12() = default; _Basic_container_proxy_ptr12(const _Basic_container_proxy_ptr12&) = delete; _Basic_container_proxy_ptr12(_Basic_container_proxy_ptr12&&) = delete; }; @@ -1229,26 +1310,27 @@ struct _Container_proxy_ptr12 : _Basic_container_proxy_ptr12 { // smart pointer components for a _Container_proxy * for an allocator family _Alloc& _Al; - _Container_proxy_ptr12(_Alloc& _Al_, _Leave_proxy_unbound) : _Al(_Al_) { // create a new unbound _Container_proxy + _CONSTEXPR20_CONTAINER _Container_proxy_ptr12(_Alloc& _Al_, _Leave_proxy_unbound) : _Al(_Al_) { + // create a new unbound _Container_proxy _Ptr = _Unfancy(_Al_.allocate(1)); _Construct_in_place(*_Ptr); } - _Container_proxy_ptr12(_Alloc& _Al_, _Container_base12& _Mycont) - : _Al(_Al_) { // create a new _Container_proxy pointing at _Mycont + _CONSTEXPR20_CONTAINER _Container_proxy_ptr12(_Alloc& _Al_, _Container_base12& _Mycont) : _Al(_Al_) { + // create a new _Container_proxy pointing at _Mycont _Ptr = _Unfancy(_Al_.allocate(1)); _Construct_in_place(*_Ptr, _STD addressof(_Mycont)); _Mycont._Myproxy = _Ptr; } - void _Bind(_Alloc& _Old_alloc, _Container_base12* _Mycont) noexcept { + _CONSTEXPR20_CONTAINER void _Bind(_Alloc& _Old_alloc, _Container_base12* _Mycont) noexcept { // Attach the proxy stored in *this to _Mycont, and destroy _Mycont's existing proxy // with _Old_alloc. Requires that no iterators are alive referring to _Mycont. _Ptr->_Mycont = _Mycont; _Delete_plain_internal(_Old_alloc, _STD exchange(_Mycont->_Myproxy, _STD exchange(_Ptr, nullptr))); } - ~_Container_proxy_ptr12() { + _CONSTEXPR20_CONTAINER ~_Container_proxy_ptr12() { if (_Ptr) { _Delete_plain_internal(_Al, _Ptr); } @@ -1256,7 +1338,8 @@ struct _Container_proxy_ptr12 : _Basic_container_proxy_ptr12 { }; #if _ITERATOR_DEBUG_LEVEL == 0 -#define _GET_PROXY_ALLOCATOR(_Alty, _Al) _Fake_allocator() +_INLINE_VAR constexpr _Fake_allocator _Fake_alloc{}; +#define _GET_PROXY_ALLOCATOR(_Alty, _Al) _Fake_alloc // TRANSITION, VSO-1284799, should be _Fake_allocator{} template using _Container_proxy_ptr = _Fake_proxy_ptr_impl; #else // _ITERATOR_DEBUG_LEVEL == 0 @@ -1381,18 +1464,18 @@ struct _NODISCARD _Uninitialized_backout { _Uninitialized_backout(const _Uninitialized_backout&) = delete; _Uninitialized_backout& operator=(const _Uninitialized_backout&) = delete; - ~_Uninitialized_backout() { + _CONSTEXPR20_DYNALLOC ~_Uninitialized_backout() { _Destroy_range(_First, _Last); } template - void _Emplace_back(_Types&&... _Vals) { + _CONSTEXPR20_DYNALLOC void _Emplace_back(_Types&&... _Vals) { // construct a new element at *_Last and increment _Construct_in_place(*_Last, _STD forward<_Types>(_Vals)...); ++_Last; } - _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last + constexpr _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last _First = _Last; return _Last; } @@ -1425,20 +1508,95 @@ namespace ranges { // FUNCTION TEMPLATE _Uninitialized_move_unchecked template -_NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { +_CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_move_unchecked( + _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // move [_First, _Last) to raw [_Dest, ...) if constexpr (_Ptr_move_cat<_InIt, _NoThrowFwdIt>::_Really_trivial) { - return _Copy_memmove(_First, _Last, _Dest); - } else { - _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; - for (; _First != _Last; ++_First) { - _Backout._Emplace_back(_STD move(*_First)); +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { + return _Copy_memmove(_First, _Last, _Dest); } - - return _Backout._Release(); } + _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; + for (; _First != _Last; ++_First) { + _Backout._Emplace_back(_STD move(*_First)); + } + + return _Backout._Release(); } +#ifdef __cpp_lib_concepts +namespace ranges { + // clang-format off + // CONCEPT _No_throw_input_iterator + template + concept _No_throw_input_iterator = input_iterator<_It> + && is_lvalue_reference_v> + && same_as>, iter_value_t<_It>>; + + // CONCEPT _No_throw_sentinel_for + template + concept _No_throw_sentinel_for = sentinel_for<_Se, _It>; + + // CONCEPT _No_throw_forward_iterator + template + concept _No_throw_forward_iterator = _No_throw_input_iterator<_It> + && forward_iterator<_It> + && _No_throw_sentinel_for<_It, _It>; + + // CONCEPT _No_throw_input_range + template + concept _No_throw_input_range = range<_Rng> + && _No_throw_input_iterator> + && _No_throw_sentinel_for, iterator_t<_Rng>>; + + // CONCEPT _No_throw_forward_range + template + concept _No_throw_forward_range = _No_throw_input_range<_Rng> + && _No_throw_forward_iterator>; + // clang-format on + + template + in_out_result<_InIt, _OutIt> _Copy_memcpy_common( + _InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) noexcept { + const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirst)); + const auto _ILast_ch = const_cast(reinterpret_cast(_ILast)); + const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirst)); + const auto _OLast_ch = const_cast(reinterpret_cast(_OLast)); + const auto _Count = static_cast((_STD min)(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch)); + _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count); + return {reinterpret_cast<_InIt>(_IFirst_ch + _Count), reinterpret_cast<_OutIt>(_OFirst_ch + _Count)}; + } + + // ALIAS TEMPLATE uninitialized_move_result + template + using uninitialized_move_result = in_out_result<_In, _Out>; + + // FUNCTION TEMPLATE _Uninitialized_move_unchecked + // clang-format off + template _Se, _No_throw_forward_iterator _Out, + _No_throw_sentinel_for<_Out> _OSe> + requires constructible_from, iter_rvalue_reference_t<_It>> + uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( + _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { + // clang-format on + if constexpr (is_same_v<_Se, _It> && is_same_v<_OSe, _Out> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { + return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) { + _Backout._Emplace_back(_RANGES iter_move(_IFirst)); + } + + return {_STD move(_IFirst), _Backout._Release()}; + } + } +} // namespace ranges +#endif // __cpp_lib_concepts + // STRUCT TEMPLATE _Uninitialized_backout_al template class _NODISCARD _Uninitialized_backout_al { @@ -1446,22 +1604,23 @@ class _NODISCARD _Uninitialized_backout_al { using pointer = _Alloc_ptr_t<_Alloc>; public: - _Uninitialized_backout_al(pointer _Dest, _Alloc& _Al_) : _First(_Dest), _Last(_Dest), _Al(_Al_) {} + _CONSTEXPR20_DYNALLOC _Uninitialized_backout_al(pointer _Dest, _Alloc& _Al_) + : _First(_Dest), _Last(_Dest), _Al(_Al_) {} _Uninitialized_backout_al(const _Uninitialized_backout_al&) = delete; _Uninitialized_backout_al& operator=(const _Uninitialized_backout_al&) = delete; - ~_Uninitialized_backout_al() { + _CONSTEXPR20_DYNALLOC ~_Uninitialized_backout_al() { _Destroy_range(_First, _Last, _Al); } template - void _Emplace_back(_Types&&... _Vals) { // construct a new element at *_Last and increment + _CONSTEXPR20_DYNALLOC void _Emplace_back(_Types&&... _Vals) { // construct a new element at *_Last and increment allocator_traits<_Alloc>::construct(_Al, _Unfancy(_Last), _STD forward<_Types>(_Vals)...); ++_Last; } - pointer _Release() { // suppress any exception handling backout and return _Last + constexpr pointer _Release() { // suppress any exception handling backout and return _Last _First = _Last; return _Last; } @@ -1474,7 +1633,7 @@ private: // FUNCTION TEMPLATE _Uninitialized_copy WITH ALLOCATOR template -_Alloc_ptr_t<_Alloc> _Uninitialized_copy( +_CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_copy( const _InIt _First, const _InIt _Last, _Alloc_ptr_t<_Alloc> _Dest, _Alloc& _Al) { // copy [_First, _Last) to raw _Dest, using _Al // note: only called internally from elsewhere in the STL @@ -1485,21 +1644,46 @@ _Alloc_ptr_t<_Alloc> _Uninitialized_copy( if constexpr (conjunction_v::_Really_trivial>, _Uses_default_construct<_Alloc, _Ptrval, decltype(*_UFirst)>>) { - _Copy_memmove(_UFirst, _ULast, _Unfancy(_Dest)); - _Dest += _ULast - _UFirst; - } else { - _Uninitialized_backout_al<_Alloc> _Backout{_Dest, _Al}; - for (; _UFirst != _ULast; ++_UFirst) { - _Backout._Emplace_back(*_UFirst); +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Copy_memmove(_UFirst, _ULast, _Unfancy(_Dest)); + _Dest += _ULast - _UFirst; + return _Dest; } + } - _Dest = _Backout._Release(); + _Uninitialized_backout_al<_Alloc> _Backout{_Dest, _Al}; + for (; _UFirst != _ULast; ++_UFirst) { + _Backout._Emplace_back(*_UFirst); } - return _Dest; + return _Backout._Release(); } // FUNCTION TEMPLATE uninitialized_copy +template +_CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_copy_unchecked( + _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { + // copy [_First, _Last) to raw [_Dest, ...) + if constexpr (_Ptr_copy_cat<_InIt, _NoThrowFwdIt>::_Really_trivial) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { + return _Copy_memmove(_First, _Last, _Dest); + } + } + + _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; + for (; _First != _Last; ++_First) { + _Backout._Emplace_back(*_First); + } + + return _Backout._Release(); +} + template _NoThrowFwdIt uninitialized_copy(const _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // copy [_First, _Last) to raw [_Dest, ...) @@ -1507,24 +1691,13 @@ _NoThrowFwdIt uninitialized_copy(const _InIt _First, const _InIt _Last, _NoThrow auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_InIt>(_UFirst, _ULast)); - if constexpr (_Ptr_copy_cat::_Really_trivial) { - _UDest = _Copy_memmove(_UFirst, _ULast, _UDest); - } else { - _Uninitialized_backout _Backout{_UDest}; - for (; _UFirst != _ULast; ++_UFirst) { - _Backout._Emplace_back(*_UFirst); - } - - _UDest = _Backout._Release(); - } - - _Seek_wrapped(_Dest, _UDest); + _Seek_wrapped(_Dest, _Uninitialized_copy_unchecked(_UFirst, _ULast, _UDest)); return _Dest; } // FUNCTION TEMPLATE _Uninitialized_move WITH ALLOCATOR template -_Alloc_ptr_t<_Alloc> _Uninitialized_move( +_CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_move( const _InIt _First, const _InIt _Last, _Alloc_ptr_t<_Alloc> _Dest, _Alloc& _Al) { // move [_First, _Last) to raw _Dest, using _Al // note: only called internally from elsewhere in the STL @@ -1533,41 +1706,55 @@ _Alloc_ptr_t<_Alloc> _Uninitialized_move( const auto _ULast = _Get_unwrapped(_Last); if constexpr (conjunction_v::_Really_trivial>, _Uses_default_construct<_Alloc, _Ptrval, decltype(_STD move(*_UFirst))>>) { - _Copy_memmove(_UFirst, _ULast, _Unfancy(_Dest)); - return _Dest + (_ULast - _UFirst); - } else { - _Uninitialized_backout_al<_Alloc> _Backout{_Dest, _Al}; - for (; _UFirst != _ULast; ++_UFirst) { - _Backout._Emplace_back(_STD move(*_UFirst)); +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Copy_memmove(_UFirst, _ULast, _Unfancy(_Dest)); + return _Dest + (_ULast - _UFirst); } + } - return _Backout._Release(); + _Uninitialized_backout_al<_Alloc> _Backout{_Dest, _Al}; + for (; _UFirst != _ULast; ++_UFirst) { + _Backout._Emplace_back(_STD move(*_UFirst)); } + + return _Backout._Release(); } // FUNCTION TEMPLATE _Uninitialized_fill_n WITH ALLOCATOR template -_Alloc_ptr_t<_Alloc> _Uninitialized_fill_n( +_CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_fill_n( _Alloc_ptr_t<_Alloc> _First, _Alloc_size_t<_Alloc> _Count, const typename _Alloc::value_type& _Val, _Alloc& _Al) { // 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) { - _Fill_memset(_Unfancy(_First), _Val, static_cast(_Count)); - return _First + _Count; - } else { - if constexpr (_Fill_zero_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { + _Fill_memset(_Unfancy(_First), _Val, static_cast(_Count)); + return _First + _Count; + } + } else if constexpr (_Fill_zero_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_Unfancy(_First), static_cast(_Count)); return _First + _Count; } } - _Uninitialized_backout_al<_Alloc> _Backout{_First, _Al}; - for (; 0 < _Count; --_Count) { - _Backout._Emplace_back(_Val); - } + } - return _Backout._Release(); + _Uninitialized_backout_al<_Alloc> _Backout{_First, _Al}; + for (; 0 < _Count; --_Count) { + _Backout._Emplace_back(_Val); } + + return _Backout._Release(); } // FUNCTION TEMPLATE uninitialized_fill @@ -1586,6 +1773,7 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c return; } } + _Uninitialized_backout<_Unwrapped_t> _Backout{_UFirst}; while (_Backout._Last != _ULast) { _Backout._Emplace_back(_Val); @@ -1611,22 +1799,27 @@ _Ptr _Zero_range(const _Ptr _First, const _Ptr _Last) { // fill [_First, _Last) } template -_Alloc_ptr_t<_Alloc> _Uninitialized_value_construct_n( +_CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_value_construct_n( _Alloc_ptr_t<_Alloc> _First, _Alloc_size_t<_Alloc> _Count, _Alloc& _Al) { // value-initialize _Count objects to raw _First, using _Al using _Ptrty = typename _Alloc::value_type*; if constexpr (_Use_memset_value_construct_v<_Ptrty> && _Uses_default_construct<_Alloc, _Ptrty>::value) { - auto _PFirst = _Unfancy(_First); - _Zero_range(_PFirst, _PFirst + _Count); - return _First + _Count; - } else { - _Uninitialized_backout_al<_Alloc> _Backout{_First, _Al}; - for (; 0 < _Count; --_Count) { - _Backout._Emplace_back(); +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { + auto _PFirst = _Unfancy(_First); + _Zero_range(_PFirst, _PFirst + _Count); + return _First + _Count; } + } - return _Backout._Release(); + _Uninitialized_backout_al<_Alloc> _Backout{_First, _Al}; + for (; 0 < _Count; --_Count) { + _Backout._Emplace_back(); } + + return _Backout._Release(); } template @@ -1703,10 +1896,13 @@ struct _In_place_key_extract_map<_Key, pair<_First, _Second>> { }; // STRUCT TEMPLATE _Wrap +#pragma warning(push) +#pragma warning(disable : 4624) // '%s': destructor was implicitly defined as deleted template struct _Wrap { _Ty _Value; // workaround for "T^ is not allowed in a union" }; +#pragma warning(pop) // STRUCT TEMPLATE _Alloc_temporary template @@ -1789,7 +1985,8 @@ _NODISCARD _CONSTEXPR20 _FwdIt remove_if(_FwdIt _First, const _FwdIt _Last, _Pr // FUNCTION TEMPLATE _Erase_remove template -typename _Container::size_type _Erase_remove(_Container& _Cont, const _Uty& _Val) { // erase each element matching _Val +_CONSTEXPR20_DYNALLOC typename _Container::size_type _Erase_remove(_Container& _Cont, const _Uty& _Val) { + // erase each element matching _Val auto _First = _Cont.begin(); const auto _Last = _Cont.end(); const auto _Old_size = _Cont.size(); @@ -1800,7 +1997,8 @@ typename _Container::size_type _Erase_remove(_Container& _Cont, const _Uty& _Val // FUNCTION TEMPLATE _Erase_remove_if template -typename _Container::size_type _Erase_remove_if(_Container& _Cont, _Pr _Pred) { // erase each element satisfying _Pred +_CONSTEXPR20_DYNALLOC typename _Container::size_type _Erase_remove_if(_Container& _Cont, _Pr _Pred) { + // erase each element satisfying _Pred auto _First = _Cont.begin(); const auto _Last = _Cont.end(); const auto _Old_size = _Cont.size(); @@ -1811,7 +2009,8 @@ typename _Container::size_type _Erase_remove_if(_Container& _Cont, _Pr _Pred) { // FUNCTION TEMPLATE _Erase_nodes_if template -typename _Container::size_type _Erase_nodes_if(_Container& _Cont, _Pr _Pred) { // erase each element satisfying _Pred +typename _Container::size_type _Erase_nodes_if(_Container& _Cont, _Pr _Pred) { + // erase each element satisfying _Pred auto _First = _Cont.begin(); const auto _Last = _Cont.end(); const auto _Old_size = _Cont.size(); @@ -1824,6 +2023,98 @@ typename _Container::size_type _Erase_nodes_if(_Container& _Cont, _Pr _Pred) { / } return _Old_size - _Cont.size(); } + +#if _HAS_CXX20 +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Types&&... _Args) noexcept { + if constexpr (!uses_allocator_v<_Ty, _Alloc>) { + static_assert(is_constructible_v<_Ty, _Types...>, + "If uses_allocator_v does not hold, T must be constructible from Types..."); + (void) _Al; + return _STD forward_as_tuple(_STD forward<_Types>(_Args)...); + } else if constexpr (is_constructible_v<_Ty, allocator_arg_t, const _Alloc&, _Types...>) { + using _ReturnType = tuple; + return _ReturnType{allocator_arg, _Al, _STD forward<_Types>(_Args)...}; + } else if constexpr (is_constructible_v<_Ty, _Types..., const _Alloc&>) { + return _STD forward_as_tuple(_STD forward<_Types>(_Args)..., _Al); + } else { + static_assert(_Always_false<_Ty>, + "T must be constructible from either (allocator_arg_t, const Alloc&, Types...) " + "or (Types..., const Alloc&) if uses_allocator_v is true"); + } +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args( + const _Alloc& _Al, piecewise_construct_t, _Tuple1&& _Tup1, _Tuple2&& _Tup2) noexcept { + return _STD make_tuple(piecewise_construct, + _STD apply( + [&_Al](auto&&... _Tuple_args) { + return _STD uses_allocator_construction_args( + _Al, _STD forward(_Tuple_args)...); + }, + _STD forward<_Tuple1>(_Tup1)), + _STD apply( + [&_Al](auto&&... _Tuple_args) { + return _STD uses_allocator_construction_args( + _Al, _STD forward(_Tuple_args)...); + }, + _STD forward<_Tuple2>(_Tup2))); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, tuple<>{}, tuple<>{}); + return _STD make_tuple(piecewise_construct, _STD uses_allocator_construction_args(_Al), + _STD uses_allocator_construction_args(_Al)); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Uty1&& _Val1, _Uty2&& _Val2) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_STD forward<_Uty1>(_Val1)), _STD forward_as_tuple(_STD forward<_Uty2>(_Val2))); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _STD forward<_Uty1>(_Val1)), + _STD uses_allocator_construction_args(_Al, _STD forward<_Uty2>(_Val2))); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args( + const _Alloc& _Al, const pair<_Uty1, _Uty2>& _Pair) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_Pair.first), _STD forward_as_tuple(_Pair.second)); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _Pair.first), + _STD uses_allocator_construction_args(_Al, _Pair.second)); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, pair<_Uty1, _Uty2>&& _Pair) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_STD move(_Pair).first), _STD forward_as_tuple(_STD move(_Pair).second)); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _STD move(_Pair).first), + _STD uses_allocator_construction_args(_Al, _STD move(_Pair).second)); +} + +template +_NODISCARD constexpr _Ty make_obj_using_allocator(const _Alloc& _Al, _Types&&... _Args) { + return _STD make_from_tuple<_Ty>(_STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...)); +} + +template +constexpr _Ty* uninitialized_construct_using_allocator(_Ty* _Ptr, const _Alloc& _Al, _Types&&... _Args) { + return _STD apply( + [&](auto&&... _Construct_args) { + return _STD construct_at(_Ptr, _STD forward(_Construct_args)...); + }, + _STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...)); +} +#endif // _HAS_CXX20 _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xpolymorphic_allocator.h b/stl/inc/xpolymorphic_allocator.h index 47b1bdc68ae..6df84ce7f46 100644 --- a/stl/inc/xpolymorphic_allocator.h +++ b/stl/inc/xpolymorphic_allocator.h @@ -22,6 +22,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN +#if !_HAS_CXX20 // FUNCTION TEMPLATE _Uses_allocator_construct template void _Uses_allocator_construct2( @@ -133,6 +134,7 @@ void _Uses_allocator_construct( _Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_STD forward<_Uty>(_Pair.first)), _STD forward_as_tuple(_STD forward<_Vty>(_Pair.second))); } +#endif // !_HAS_CXX20 #if _HAS_CXX17 namespace pmr { @@ -168,9 +170,11 @@ namespace pmr { return &_Left == &_Right || _Left.is_equal(_Right); } +#if !_HAS_CXX20 _NODISCARD inline bool operator!=(const memory_resource& _Left, const memory_resource& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION get_default_resource extern "C" _CRT_SATELLITE_1 memory_resource* __cdecl _Aligned_get_default_resource() noexcept; @@ -269,8 +273,12 @@ namespace pmr { template void construct(_Uty* const _Ptr, _Types&&... _Args) { // propagate allocator *this if uses_allocator_v<_Uty, polymorphic_allocator> +#if _HAS_CXX20 + _STD uninitialized_construct_using_allocator(_Ptr, *this, _STD forward<_Types>(_Args)...); +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv allocator _Al{}; _Uses_allocator_construct(_Ptr, _Al, *this, _STD forward<_Types>(_Args)...); +#endif // ^^^ !_HAS_CXX20 ^^^ } template @@ -299,11 +307,13 @@ namespace pmr { return *_Left.resource() == *_Right.resource(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const polymorphic_allocator<_Ty1>& _Left, const polymorphic_allocator<_Ty2>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 } // namespace pmr diff --git a/stl/inc/xstring b/stl/inc/xstring index 80355987c2c..9e63cab02b2 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -39,6 +39,9 @@ struct _Char_traits { // properties of a string or stream element using pos_type = streampos; using off_type = streamoff; using state_type = _Mbstatet; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 // For copy/move, we can uniformly call memcpy/memmove (or their builtin versions) for all element types. @@ -67,8 +70,8 @@ struct _Char_traits { // properties of a string or stream element _Pre_satisfies_(_Dest_size >= _Count) static _CONSTEXPR20 _Elem* _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _First1, - const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, - const size_t _Count) noexcept { // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) + const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept { + // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) _STL_VERIFY(_Count <= _Dest_size, "invalid argument"); return copy(_First1, _First2, _Count); } @@ -161,18 +164,33 @@ struct _Char_traits { // properties of a string or stream element } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) - for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { - *_Next = _Ch; +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { + _STD construct_at(_Next, _Ch); + } + } else +#endif // __cpp_lib_constexpr_string + { + for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { + *_Next = _Ch; + } } return _First; } static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { - _Left = _Right; +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _STD construct_at(_STD addressof(_Left), _Right); + } else +#endif // __cpp_lib_constexpr_string + { + _Left = _Right; + } } _NODISCARD static constexpr bool eq(const _Elem& _Left, const _Elem& _Right) noexcept { @@ -217,6 +235,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -265,8 +286,7 @@ public: } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -355,6 +375,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -412,8 +435,7 @@ public: } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -1115,6 +1137,17 @@ public: #endif // _ITERATOR_DEBUG_LEVEL } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot compare incompatible string_view iterators"); + return _Myoff <=> _Right._Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr <=> _Right._Myptr; +#endif // _ITERATOR_DEBUG_LEVEL + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const _String_view_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1140,6 +1173,7 @@ public: _NODISCARD constexpr bool operator>=(const _String_view_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL >= 1 friend constexpr void _Verify_range(const _String_view_iterator& _First, const _String_view_iterator& _Last) { @@ -1659,11 +1693,13 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } +#if !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { return _Lhs._Equal(_Rhs); } +#endif // !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( @@ -1671,7 +1707,7 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } - +#if !_HAS_CXX20 // FUNCTION TEMPLATES operator!= FOR basic_string_view template _NODISCARD constexpr bool operator!=( @@ -1770,7 +1806,37 @@ _NODISCARD constexpr bool operator>=( const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { return _Lhs.compare(_Rhs) >= 0; } +#endif // !_HAS_CXX20 +#if _HAS_CXX20 +template +struct _Get_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_comparison_category<_Traits, void_t> { + using type = typename _Traits::comparison_category; + + static_assert(_Is_any_of_v, + "N4878 [string.view.comparison]/4: Mandates: R denotes a comparison category type."); +}; + +template +using _Get_comparison_category_t = typename _Get_comparison_category<_Traits>::type; + +template +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} +#endif // _HAS_CXX20 // TYPEDEFS FOR basic_string_view using string_view = basic_string_view; @@ -1841,13 +1907,17 @@ public: using pointer = typename _Mystr::const_pointer; using reference = const value_type&; - _String_const_iterator() noexcept : _Ptr() {} + _CONSTEXPR20_CONTAINER _String_const_iterator() noexcept : _Ptr() {} - _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept : _Ptr(_Parg) { + _CONSTEXPR20_CONTAINER _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept + : _Ptr(_Parg) { this->_Adopt(_Pstring); } - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _String_const_iterator& operator=(const _String_const_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot dereference value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1863,11 +1933,11 @@ public: return *_Ptr; } - _NODISCARD pointer operator->() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } - _String_const_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator++() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot increment value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1880,13 +1950,13 @@ public: return *this; } - _String_const_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator operator++(int) noexcept { _String_const_iterator _Tmp = *this; ++*this; return _Tmp; } - _String_const_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator--() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot decrement value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1899,13 +1969,13 @@ public: return *this; } - _String_const_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator operator--(int) noexcept { _String_const_iterator _Tmp = *this; --*this; return _Tmp; } - void _Verify_offset(const difference_type _Off) const noexcept { + _CONSTEXPR20_CONTAINER void _Verify_offset(const difference_type _Off) const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 if (_Off == 0) { return; @@ -1932,7 +2002,7 @@ public: #endif // _ITERATOR_DEBUG_LEVEL >= 1 } - _String_const_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator+=(const difference_type _Off) noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _Verify_offset(_Off); #endif // _ITERATOR_DEBUG_LEVEL >= 1 @@ -1940,34 +2010,42 @@ public: return *this; } - _NODISCARD _String_const_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator operator+(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _String_const_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator-=(const difference_type _Off) noexcept { return *this += -_Off; } - _NODISCARD _String_const_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator operator-(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD difference_type operator-(const _String_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr - _Right._Ptr; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } - _NODISCARD bool operator==(const _String_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1988,8 +2066,10 @@ public: _NODISCARD bool operator>=(const _String_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 - void _Compat(const _String_const_iterator& _Right) const noexcept { // test for compatible iterator pair + _CONSTEXPR20_CONTAINER void _Compat(const _String_const_iterator& _Right) const noexcept { + // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(this->_Getcont() == _Right._Getcont(), "string iterators incompatible (e.g." " point to different string instances)"); @@ -1999,7 +2079,8 @@ public: } #if _ITERATOR_DEBUG_LEVEL >= 1 - friend void _Verify_range(const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept { + friend _CONSTEXPR20_CONTAINER void _Verify_range( + const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept { _STL_VERIFY(_First._Getcont() == _Last._Getcont(), "string iterators in range are from different containers"); _STL_VERIFY(_First._Ptr <= _Last._Ptr, "string iterator range transposed"); } @@ -2007,11 +2088,11 @@ public: using _Prevent_inheriting_unwrap = _String_const_iterator; - _NODISCARD const value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const value_type* _Unwrapped() const noexcept { return _Unfancy(_Ptr); } - void _Seek_to(const value_type* _It) noexcept { + _CONSTEXPR20_CONTAINER void _Seek_to(const value_type* _It) noexcept { _Ptr = _Refancy(const_cast(_It)); } @@ -2019,7 +2100,7 @@ public: }; template -_NODISCARD _String_const_iterator<_Mystr> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator<_Mystr> operator+( typename _String_const_iterator<_Mystr>::difference_type _Off, _String_const_iterator<_Mystr> _Next) noexcept { return _Next += _Off; } @@ -2072,71 +2153,76 @@ public: using _Mybase::_Mybase; - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _String_iterator& operator=(const _String_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { return const_cast(_Mybase::operator*()); } - _NODISCARD pointer operator->() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } - _String_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator++() noexcept { _Mybase::operator++(); return *this; } - _String_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator operator++(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator++(); return _Tmp; } - _String_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator--() noexcept { _Mybase::operator--(); return *this; } - _String_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator operator--(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator--(); return _Tmp; } - _String_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator+=(const difference_type _Off) noexcept { _Mybase::operator+=(_Off); return *this; } - _NODISCARD _String_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_iterator operator+(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _String_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator-=(const difference_type _Off) noexcept { _Mybase::operator-=(_Off); return *this; } using _Mybase::operator-; - _NODISCARD _String_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_iterator operator-(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return const_cast(_Mybase::operator[](_Off)); } using _Prevent_inheriting_unwrap = _String_iterator; - _NODISCARD value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER value_type* _Unwrapped() const noexcept { return const_cast(_Unfancy(this->_Ptr)); } }; template -_NODISCARD _String_iterator<_Mystr> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _String_iterator<_Mystr> operator+( typename _String_iterator<_Mystr>::difference_type _Off, _String_iterator<_Mystr> _Next) noexcept { return _Next += _Off; } @@ -2195,7 +2281,7 @@ public: using reference = value_type&; using const_reference = const value_type&; - _String_val() noexcept : _Bx(), _Mysize(0), _Myres(0) {} + _CONSTEXPR20_CONTAINER _String_val() noexcept : _Bx() {} // length of internal buffer, [1, 16]: static constexpr size_type _BUF_SIZE = 16 / sizeof(value_type) < 1 ? 1 : 16 / sizeof(value_type); @@ -2206,7 +2292,7 @@ public: : sizeof(value_type) <= 8 ? 1 : 0; - value_type* _Myptr() noexcept { + _CONSTEXPR20_CONTAINER value_type* _Myptr() noexcept { value_type* _Result = _Bx._Buf; if (_Large_string_engaged()) { _Result = _Unfancy(_Bx._Ptr); @@ -2215,7 +2301,7 @@ public: return _Result; } - const value_type* _Myptr() const noexcept { + _CONSTEXPR20_CONTAINER const value_type* _Myptr() const noexcept { const value_type* _Result = _Bx._Buf; if (_Large_string_engaged()) { _Result = _Unfancy(_Bx._Ptr); @@ -2224,17 +2310,24 @@ public: return _Result; } - bool _Large_string_engaged() const noexcept { + _CONSTEXPR20_CONTAINER bool _Large_string_engaged() const noexcept { +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + return true; + } +#endif // __cpp_lib_constexpr_string return _BUF_SIZE <= _Myres; } - void _Check_offset(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()] + _CONSTEXPR20_CONTAINER void _Check_offset(const size_type _Off) const { + // checks whether _Off is in the bounds of [0, size()] if (_Mysize < _Off) { _Xran(); } } - void _Check_offset_exclusive(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()) + _CONSTEXPR20_CONTAINER void _Check_offset_exclusive(const size_type _Off) const { + // checks whether _Off is in the bounds of [0, size()) if (_Mysize <= _Off) { _Xran(); } @@ -2244,23 +2337,23 @@ public: _Xout_of_range("invalid string position"); } - size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { + _CONSTEXPR20_CONTAINER size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { // trims _Size to the longest it can be assuming a string at/after _Off return (_STD min)(_Size, _Mysize - _Off); } union _Bxty { // storage for small buffer or pointer to larger one - _Bxty() noexcept {} // user-provided, for fancy pointers + _CONSTEXPR20_CONTAINER _Bxty() noexcept : _Ptr() {} // user-provided, for fancy pointers - ~_Bxty() noexcept {} // user-provided, for fancy pointers + _CONSTEXPR20_CONTAINER ~_Bxty() noexcept {} // user-provided, for fancy pointers value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr) } _Bx; - size_type _Mysize; // current length of string - size_type _Myres; // current storage reserved for string + size_type _Mysize = 0; // current length of string + size_type _Myres = 0; // current storage reserved for string }; // CLASS TEMPLATE basic_string @@ -2354,7 +2447,7 @@ private: #endif // _HAS_CXX17 public: - basic_string(const basic_string& _Right) + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2362,24 +2455,27 @@ public: _Proxy._Release(); } - basic_string(const basic_string& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right, const _Alloc& _Al) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Construct_lv_contents(_Right); _Proxy._Release(); } - basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) + : _Mypair(_Zero_then_variadic_args_t{}) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } - explicit basic_string(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER explicit basic_string(const _Alloc& _Al) noexcept + : _Mypair(_One_then_variadic_args_t{}, _Al) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } - basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, ) auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2388,7 +2484,7 @@ public: _Proxy._Release(); } - basic_string( + _CONSTEXPR20_CONTAINER basic_string( const basic_string& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2398,7 +2494,8 @@ public: _Proxy._Release(); } - basic_string(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) + _CONSTEXPR20_CONTAINER basic_string( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) : _Mypair(_Zero_then_variadic_args_t{}) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2407,7 +2504,7 @@ public: _Proxy._Release(); } - basic_string( + _CONSTEXPR20_CONTAINER basic_string( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2417,7 +2514,7 @@ public: _Proxy._Release(); } - basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2428,7 +2525,8 @@ public: #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 - basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2436,7 +2534,8 @@ public: _Proxy._Release(); } - basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) + : _Mypair(_Zero_then_variadic_args_t{}) { // construct from _Count * _Ch auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2448,7 +2547,7 @@ public: #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 - basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al) + _CONSTEXPR20_CONTAINER basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Count * _Ch with allocator auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2458,7 +2557,8 @@ public: } template , int> = 0> - basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2468,7 +2568,7 @@ public: } template - void _Construct(_Iter _First, const _Iter _Last, input_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(_Iter _First, const _Iter _Last, input_iterator_tag) { // initialize from [_First, _Last), input iterators _Tidy_deallocate_guard _Guard{this}; for (; _First != _Last; ++_First) { @@ -2479,33 +2579,35 @@ public: } template - void _Construct(const _Iter _First, const _Iter _Last, forward_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(const _Iter _First, const _Iter _Last, forward_iterator_tag) { // initialize from [_First, _Last), forward iterators const size_type _Count = _Convert_size(static_cast(_STD distance(_First, _Last))); reserve(_Count); _Construct(_First, _Last, input_iterator_tag{}); } - void _Construct(_Elem* const _First, _Elem* const _Last, random_access_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(_Elem* const _First, _Elem* const _Last, random_access_iterator_tag) { // initialize from [_First, _Last), pointers if (_First != _Last) { assign(_First, _Convert_size(static_cast(_Last - _First))); } } - void _Construct(const _Elem* const _First, const _Elem* const _Last, random_access_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct( + const _Elem* const _First, const _Elem* const _Last, random_access_iterator_tag) { // initialize from [_First, _Last), const pointers if (_First != _Last) { assign(_First, _Convert_size(static_cast(_Last - _First))); } } - basic_string(basic_string&& _Right) noexcept : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { + _CONSTEXPR20_CONTAINER basic_string(basic_string&& _Right) noexcept + : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } - basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept( + _CONSTEXPR20_CONTAINER basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept( _Alty_traits::is_always_equal::value) // strengthened : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2518,12 +2620,13 @@ public: } } - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); _Proxy._Release(); } - basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, const _Elem* const _Left_ptr, - const size_type _Left_size, const _Elem* const _Right_ptr, const size_type _Right_size) + _CONSTEXPR20_CONTAINER basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, + const _Elem* const _Left_ptr, const size_type _Left_size, const _Elem* const _Right_ptr, + const size_type _Right_size) : _Mypair( _One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Source_of_al._Getal())) { _STL_INTERNAL_CHECK(_Left_size <= max_size()); @@ -2535,13 +2638,28 @@ public: _Elem* _Ptr = _My_data._Bx._Buf; auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws - if (_New_capacity < _New_size) { - _New_capacity = _Calculate_growth(_New_size, _BUF_SIZE - 1, max_size()); - const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws - _Ptr = _Unfancy(_Fancyptr); + +#ifdef __cpp_lib_constexpr_string + const bool _Activate_large_mode = _New_capacity < _New_size || _STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Activate_large_mode = _New_capacity < _New_size; +#endif // __cpp_lib_constexpr_string + + if (_Activate_large_mode) { + // we should never allocate less than _BUF_SIZE space (_New_size could be small if constant evaluated) + const size_type _Requested_size = (_STD max)(_New_size, _BUF_SIZE); + _New_capacity = _Calculate_growth(_Requested_size, _BUF_SIZE - 1, max_size()); + const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws + _Ptr = _Unfancy(_Fancyptr); _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); } +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Ptr, _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; _Traits::copy(_Ptr, _Left_ptr, _Left_size); @@ -2550,7 +2668,7 @@ public: _Proxy._Release(); } - basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right) + _CONSTEXPR20_CONTAINER basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Left._Getal()) { auto& _My_data = _Mypair._Myval2; auto& _Left_data = _Left._Mypair._Myval2; @@ -2568,7 +2686,7 @@ public: if (_Fits_in_left && _Right_capacity <= _Left_capacity) { // take _Left's buffer, max_size() is OK because _Fits_in_left _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block - _Take_contents(_Left, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Left); const auto _Ptr = _My_data._Myptr(); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1); _My_data._Mysize = _New_size; @@ -2588,7 +2706,7 @@ public: // therefore: _Right must have more than the minimum capacity, so it must be _Large_string_engaged() _STL_INTERNAL_CHECK(_Right_data._Large_string_engaged()); _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); const auto _Ptr = _Unfancy(_My_data._Bx._Ptr); _Traits::move(_Ptr + _Left_size, _Ptr, _Right_size + 1); _Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size); @@ -2607,6 +2725,11 @@ public: _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws // nothrow hereafter +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_Fancyptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; @@ -2618,7 +2741,7 @@ public: #if _HAS_CXX17 template = 0> - explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2628,7 +2751,7 @@ public: } template = 0> - basic_string( + _CONSTEXPR20_CONTAINER basic_string( const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) using _Al auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2642,19 +2765,20 @@ public: #if _HAS_CXX20 basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al) : _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) { + // Used exclusively by basic_stringbuf _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } #endif // _HAS_CXX20 private: - void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { _Tidy_deallocate(); _Pocma(_Getal(), _Right._Getal()); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } - void _Move_assign(basic_string& _Right, _Propagate_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _Propagate_allocators) noexcept { if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { @@ -2663,11 +2787,11 @@ private: _Mypair._Myval2._Reload_proxy( _GET_PROXY_ALLOCATOR(_Alty, _Getal()), _GET_PROXY_ALLOCATOR(_Alty, _Right._Getal())); _Pocma(_Getal(), _Right._Getal()); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } } - void _Move_assign(basic_string& _Right, _No_propagate_allocators) { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _No_propagate_allocators) { if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { @@ -2677,8 +2801,8 @@ private: public: #if _HAS_CXX20 - bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { - // Move assign from a buffer, used by basic_stringbuf; returns _Large_string_engaged() + _NODISCARD bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { + // Move assign from a buffer, used exclusively by basic_stringbuf; returns _Large_string_engaged() _Tidy_deallocate(); pointer _Fancy_right = _Refancy(_Right); auto& _My_data = _Mypair._Myval2; @@ -2702,7 +2826,7 @@ public: }; _NODISCARD _Released_buffer _Release_to_buffer(_Alloc& _Al) { - // Release to a buffer, or allocate a new one if in small string mode + // Release to a buffer, or allocate a new one if in small string mode; used exclusively by basic_stringbuf _Released_buffer _Result; auto& _My_data = _Mypair._Myval2; _Result._Size = _My_data._Mysize; @@ -2721,7 +2845,8 @@ public: } #endif // _HAS_CXX20 - basic_string& operator=(basic_string&& _Right) noexcept(noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { + _CONSTEXPR20_CONTAINER basic_string& operator=(basic_string&& _Right) noexcept( + noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { if (this != _STD addressof(_Right)) { _Move_assign(_Right, _Choose_pocma<_Alty>{}); } @@ -2729,7 +2854,7 @@ public: return *this; } - basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) { + _CONSTEXPR20_CONTAINER basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) { *this = _STD move(_Right); return *this; } @@ -2744,33 +2869,35 @@ private: _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); } - void _Take_contents(basic_string& _Right, true_type) noexcept { - // assign by stealing _Right's buffer, memcpy optimization - // pre: this != &_Right - // pre: allocator propagation (POCMA) from _Right, if necessary, is complete - // pre: *this owns no memory, iterators orphaned (note: - // _Buf/_Ptr/_Mysize/_Myres may be garbage init) -#if _ITERATOR_DEBUG_LEVEL != 0 - if (_Right._Mypair._Myval2._Large_string_engaged()) { - // take ownership of _Right's iterators along with its buffer - _Swap_proxy_and_iterators(_Right); - } else { - _Right._Mypair._Myval2._Orphan_all(); - } -#endif // _ITERATOR_DEBUG_LEVEL != 0 - - _Memcpy_val_from(_Right); - _Right._Tidy_init(); - } - - void _Take_contents(basic_string& _Right, false_type) noexcept { - // assign by stealing _Right's buffer, general case + _CONSTEXPR20_CONTAINER void _Take_contents(basic_string& _Right) noexcept { + // assign by stealing _Right's buffer // pre: this != &_Right // pre: allocator propagation (POCMA) from _Right, if necessary, is complete // pre: *this owns no memory, iterators orphaned // (note: _Buf/_Ptr/_Mysize/_Myres may be garbage init) auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; + + if constexpr (_Can_memcpy_val) { +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { +#if _ITERATOR_DEBUG_LEVEL != 0 + if (_Right_data._Large_string_engaged()) { + // take ownership of _Right's iterators along with its buffer + _Swap_proxy_and_iterators(_Right); + } else { + _Right_data._Orphan_all(); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + _Memcpy_val_from(_Right); + _Right._Tidy_init(); + return; + } + } + if (_Right_data._Large_string_engaged()) { // steal buffer _Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); _Right_data._Bx._Ptr = nullptr; @@ -2785,7 +2912,7 @@ private: _Right._Tidy_init(); } - void _Construct_lv_contents(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER void _Construct_lv_contents(const basic_string& _Right) { // assign by copying data stored in _Right // pre: this != &_Right // pre: *this owns no memory, iterators orphaned (note: @@ -2794,7 +2921,16 @@ private: const size_type _Right_size = _Right_data._Mysize; const _Elem* const _Right_ptr = _Right_data._Myptr(); auto& _My_data = _Mypair._Myval2; - if (_Right_size < _BUF_SIZE) { // stay small, don't allocate + +#ifdef __cpp_lib_constexpr_string + const bool _Stay_small = _Right_size < _BUF_SIZE && !_STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Stay_small = _Right_size < _BUF_SIZE; +#endif // __cpp_lib_constexpr_string + + // NOTE: even if _Right is in large mode, we only go into large mode ourselves if the actual size of _Right + // requires it + if (_Stay_small) { // stay small, don't allocate _Traits::copy(_My_data._Bx._Buf, _Right_ptr, _BUF_SIZE); _My_data._Mysize = _Right_size; _My_data._Myres = _BUF_SIZE - 1; @@ -2805,13 +2941,19 @@ private: const size_type _New_capacity = (_STD min)(_Right_size | _ALLOC_MASK, max_size()); const pointer _New_array = _Al.allocate(_New_capacity + 1); // throws _Construct_in_place(_My_data._Bx._Ptr, _New_array); + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_array), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Traits::copy(_Unfancy(_New_array), _Right_ptr, _Right_size + 1); _My_data._Mysize = _Right_size; _My_data._Myres = _New_capacity; } public: - basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) + _CONSTEXPR20_CONTAINER basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2820,23 +2962,23 @@ public: _Proxy._Release(); } - basic_string& operator=(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& operator=(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& operator+=(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& assign(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& assign(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& append(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& append(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } - iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -2845,7 +2987,7 @@ public: return begin() + static_cast(_Off); } - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const initializer_list<_Elem> _Ilist) { // replace with initializer_list _Adl_verify_range(_First, _Last); @@ -2857,7 +2999,7 @@ public: return replace(_Offset, _Length, _Ilist.begin(), _Convert_size(_Ilist.size())); } - ~basic_string() noexcept { + _CONSTEXPR20_CONTAINER ~basic_string() noexcept { _Tidy_deallocate(); #if _ITERATOR_DEBUG_LEVEL != 0 auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2872,23 +3014,28 @@ public: private: void _Copy_assign_val_from_small(const basic_string& _Right) { // TRANSITION, VSO-761321; inline into only caller when that's fixed +#ifdef __cpp_lib_constexpr_string + _STL_ASSERT(!_STD is_constant_evaluated(), "SSO should be disabled in a constexpr context"); +#endif // __cpp_lib_constexpr_string _Tidy_deallocate(); if constexpr (_Can_memcpy_val) { _Memcpy_val_from(_Right); } else { - _Traits::copy( - _Mypair._Myval2._Bx._Buf, _Right._Mypair._Myval2._Bx._Buf, _Right._Mypair._Myval2._Mysize + 1); - _Mypair._Myval2._Mysize = _Right._Mypair._Myval2._Mysize; - _Mypair._Myval2._Myres = _Right._Mypair._Myval2._Myres; + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); + _My_data._Mysize = _Right_data._Mysize; + _My_data._Myres = _Right_data._Myres; } } - void _Copy_assign(const basic_string& _Right, false_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const basic_string& _Right, false_type) { _Pocca(_Getal(), _Right._Getal()); assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - void _Copy_assign(const basic_string& _Right, true_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const basic_string& _Right, true_type) { auto& _Al = _Getal(); const auto& _Right_al = _Right._Getal(); if (_Al == _Right_al) { @@ -2905,6 +3052,13 @@ private: const auto _New_capacity = _Calculate_growth(_New_size, 0, _Right.max_size()); auto _Right_al_non_const = _Right_al; const auto _New_ptr = _Right_al_non_const.allocate(_New_capacity); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_size + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_Right._Mypair._Myval2._Bx._Ptr), _New_size + 1); _Tidy_deallocate(); _Mypair._Myval2._Bx._Ptr = _New_ptr; @@ -2919,7 +3073,7 @@ private: } public: - basic_string& operator=(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator=(const basic_string& _Right) { if (this != _STD addressof(_Right)) { _Copy_assign(_Right, _Choose_pocca<_Alty>{}); } @@ -2929,16 +3083,16 @@ public: #if _HAS_CXX17 template = 0> - basic_string& operator=(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator=(const _StringViewIsh& _Right) { return assign(_Right); } #endif // _HAS_CXX17 - basic_string& operator=(_In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& operator=(_In_z_ const _Elem* const _Ptr) { return assign(_Ptr); } - basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} + _CONSTEXPR20_CONTAINER basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} _Mypair._Myval2._Mysize = 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[0], _Ch); @@ -2946,31 +3100,32 @@ public: return *this; } - basic_string& operator+=(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(const basic_string& _Right) { return append(_Right); } #if _HAS_CXX17 template = 0> - basic_string& operator+=(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(const _StringViewIsh& _Right) { return append(_Right); } #endif // _HAS_CXX17 - basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) + _CONSTEXPR20_CONTAINER basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return append(_Ptr); } - basic_string& operator+=(_Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(_Elem _Ch) { push_back(_Ch); return *this; } - basic_string& append(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& append(const basic_string& _Right) { return append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& append(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& append( + const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -2979,20 +3134,22 @@ public: #if _HAS_CXX17 template = 0> - basic_string& append(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& append(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return append(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& append(const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& append( + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return append(_As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& append(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { + _CONSTEXPR20_CONTAINER basic_string& append( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // append [_Ptr, _Ptr + _Count) const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { @@ -3014,11 +3171,12 @@ public: _Ptr, _Count); } - basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) + _CONSTEXPR20_CONTAINER basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return append(_Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // append _Count * _Ch + _CONSTEXPR20_CONTAINER basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + // append _Count * _Ch const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { _Mypair._Myval2._Mysize = _Old_size + _Count; @@ -3040,7 +3198,8 @@ public: } template , int> = 0> - basic_string& append(const _Iter _First, const _Iter _Last) { // append [_First, _Last), input iterators + _CONSTEXPR20_CONTAINER basic_string& append(const _Iter _First, const _Iter _Last) { + // append [_First, _Last), input iterators _Adl_verify_range(_First, _Last); const auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); @@ -3052,12 +3211,13 @@ public: } } - basic_string& assign(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& assign(const basic_string& _Right) { *this = _Right; return *this; } - basic_string& assign(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& assign( + const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -3066,20 +3226,22 @@ public: #if _HAS_CXX17 template = 0> - basic_string& assign(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& assign(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return assign(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& assign(const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& assign( + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return assign(_As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& assign(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { + _CONSTEXPR20_CONTAINER basic_string& assign( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count) if (_Count <= _Mypair._Myval2._Myres) { _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); @@ -3098,11 +3260,12 @@ public: _Ptr); } - basic_string& assign(_In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& assign(_In_z_ const _Elem* const _Ptr) { return assign(_Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // assign _Count * _Ch + _CONSTEXPR20_CONTAINER basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + // assign _Count * _Ch if (_Count <= _Mypair._Myval2._Myres) { _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; @@ -3121,7 +3284,7 @@ public: } template , int> = 0> - basic_string& assign(const _Iter _First, const _Iter _Last) { + _CONSTEXPR20_CONTAINER basic_string& assign(const _Iter _First, const _Iter _Last) { _Adl_verify_range(_First, _Last); const auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); @@ -3131,7 +3294,7 @@ public: basic_string _Right(_UFirst, _ULast, get_allocator()); if (_Mypair._Myval2._Myres < _Right._Mypair._Myval2._Myres) { _Mypair._Myval2._Orphan_all(); - _Swap_data(_Right, bool_constant<_Can_memcpy_val>{}); + _Swap_data(_Right); return *this; } else { return assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); @@ -3139,11 +3302,12 @@ public: } } - basic_string& insert(const size_type _Off, const basic_string& _Right) { // insert _Right at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, const basic_string& _Right) { + // insert _Right at _Off return insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& insert( + _CONSTEXPR20_CONTAINER basic_string& insert( const size_type _Off, const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off _Right._Mypair._Myval2._Check_offset(_Roff); @@ -3153,33 +3317,44 @@ public: #if _HAS_CXX17 template = 0> - basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) { // insert _Right at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) { + // insert _Right at _Off const basic_string_view<_Elem, _Traits> _As_view = _Right; return insert(_Off, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& insert(const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, - const size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off + _CONSTEXPR20_CONTAINER basic_string& insert( + const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + // insert _Right [_Roff, _Roff + _Count) at _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return insert(_Off, _As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& insert( + _CONSTEXPR20_CONTAINER basic_string& insert( const size_type _Off, _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // insert [_Ptr, _Ptr + _Count) at _Off _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; - if (_Count <= _Mypair._Myval2._Myres - _Old_size) { + + // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate + // and copy to the new buffer if constant evaluated +#ifdef __cpp_lib_constexpr_string + const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size && !_STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size; +#endif // __cpp_lib_constexpr_string + + if (_Check_overlap) { _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; // the range [_Ptr, _Ptr + _Ptr_shifted_after) is left alone by moving the suffix out, // while the range [_Ptr + _Ptr_shifted_after, _Ptr + _Count) shifts down by _Count size_type _Ptr_shifted_after; - if (_Ptr + _Count <= _Insert_at - || _Ptr > _Old_ptr + _Old_size) { // inserted content is before the shifted region, or does not alias + if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { + // inserted content is before the shifted region, or does not alias _Ptr_shifted_after = _Count; // none of _Ptr's data shifts } else if (_Insert_at <= _Ptr) { // all of [_Ptr, _Ptr + _Count) shifts _Ptr_shifted_after = 0; @@ -3205,11 +3380,13 @@ public: _Off, _Ptr, _Count); } - basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) { // insert [_Ptr, ) at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) { + // insert [_Ptr, ) at _Off return insert(_Off, _Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& insert(const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& insert( + const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Ch at _Off _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; @@ -3233,7 +3410,7 @@ public: _Off, _Count, _Ch); } - iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -3242,7 +3419,8 @@ public: return begin() + static_cast(_Off); } - iterator insert(const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER iterator insert( + const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Elem at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); @@ -3253,7 +3431,7 @@ public: } template , int> = 0> - iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) { + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) { // insert [_First, _Last) at _Where, input iterators #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); @@ -3272,14 +3450,14 @@ public: return begin() + static_cast(_Off); } - basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...) + _CONSTEXPR20_CONTAINER basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...) _Mypair._Myval2._Check_offset(_Off); _Eos(_Off); return *this; } private: - basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept { + _CONSTEXPR20_CONTAINER basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept { _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); const size_type _Old_size = _Mypair._Myval2._Mysize; _Elem* const _My_ptr = _Mypair._Myval2._Myptr(); @@ -3291,12 +3469,13 @@ private: } public: - basic_string& erase(const size_type _Off, const size_type _Count) { // erase elements [_Off, _Off + _Count) + _CONSTEXPR20_CONTAINER basic_string& erase(const size_type _Off, const size_type _Count) { + // erase elements [_Off, _Off + _Count) _Mypair._Myval2._Check_offset(_Off); return _Erase_noexcept(_Off, _Count); } - iterator erase(const const_iterator _Where) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const const_iterator _Where) noexcept /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -3305,7 +3484,8 @@ public: return begin() + static_cast(_Off); } - iterator erase(const const_iterator _First, const const_iterator _Last) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const const_iterator _First, const const_iterator _Last) noexcept + /* strengthened */ { _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); @@ -3315,17 +3495,18 @@ public: return begin() + static_cast(_Off); } - void clear() noexcept { // erase all + _CONSTEXPR20_CONTAINER void clear() noexcept { // erase all _Eos(0); } - basic_string& replace(const size_type _Off, const size_type _Nx, const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, const basic_string& _Right) { // replace [_Off, _Off + _Nx) with _Right return replace(_Off, _Nx, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right, const size_type _Roff, - size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right, + const size_type _Roff, size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -3334,22 +3515,23 @@ public: #if _HAS_CXX17 template = 0> - basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) { // replace [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return replace(_Off, _Nx, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, - const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& replace(const size_type _Off, const size_type _Nx, + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return replace(_Off, _Nx, _As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const size_type _Off, size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); @@ -3371,31 +3553,39 @@ public: } const size_type _Growth = static_cast(_Count - _Nx); - if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits - _Mypair._Myval2._Mysize = _Old_size + _Growth; - _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); - _Elem* const _Insert_at = _Old_ptr + _Off; - _Elem* const _Suffix_at = _Insert_at + _Nx; - size_type _Ptr_shifted_after; // see rationale in insert - if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { - _Ptr_shifted_after = _Count; - } else if (_Suffix_at <= _Ptr) { - _Ptr_shifted_after = 0; - } else { - _Ptr_shifted_after = static_cast(_Suffix_at - _Ptr); - } + // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate + // and copy to the new buffer if constant evaluated +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits + _Mypair._Myval2._Mysize = _Old_size + _Growth; + _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); + _Elem* const _Insert_at = _Old_ptr + _Off; + _Elem* const _Suffix_at = _Insert_at + _Nx; + + size_type _Ptr_shifted_after; // see rationale in insert + if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { + _Ptr_shifted_after = _Count; + } else if (_Suffix_at <= _Ptr) { + _Ptr_shifted_after = 0; + } else { + _Ptr_shifted_after = static_cast(_Suffix_at - _Ptr); + } - _Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size); - // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; - // this case doesn't occur in insert because the new content must come from outside the removed - // content there (because in insert there is no removed content) - _Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after); - // the next case can be copy, because it comes from the chunk moved out of the way in the - // first move, and the hole we're filling can't alias the chunk we moved out of the way - _Traits::copy( - _Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after); - return *this; + _Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size); + // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; + // this case doesn't occur in insert because the new content must come from outside the removed + // content there (because in insert there is no removed content) + _Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after); + // the next case can be copy, because it comes from the chunk moved out of the way in the + // first move, and the hole we're filling can't alias the chunk we moved out of the way + _Traits::copy( + _Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after); + return *this; + } } return _Reallocate_grow_by( @@ -3409,12 +3599,14 @@ public: _Off, _Nx, _Ptr, _Count); } - basic_string& replace(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) { // replace [_Off, _Off + _Nx) with [_Ptr, ) return replace(_Off, _Nx, _Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& replace(const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) { // replace [_Off, _Off + _Nx) with _Count * _Ch _Mypair._Myval2._Check_offset(_Off); _Nx = _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx); @@ -3424,8 +3616,8 @@ public: } const size_type _Old_size = _Mypair._Myval2._Mysize; - if (_Count < _Nx - || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { // either we are shrinking, or the growth fits + if (_Count < _Nx || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { + // either we are shrinking, or the growth fits _Mypair._Myval2._Mysize = _Old_size + _Count - _Nx; // may temporarily overflow; // OK because size_type must be unsigned _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); @@ -3446,7 +3638,8 @@ public: _Off, _Nx, _Count, _Ch); } - basic_string& replace(const const_iterator _First, const const_iterator _Last, const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, const basic_string& _Right) { // replace [_First, _Last) with _Right _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3458,7 +3651,8 @@ public: #if _HAS_CXX17 template = 0> - basic_string& replace(const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) { // replace [_First, _Last) with _Right _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3469,7 +3663,7 @@ public: } #endif // _HAS_CXX17 - basic_string& replace(const const_iterator _First, const const_iterator _Last, + _CONSTEXPR20_CONTAINER basic_string& replace(const const_iterator _First, const const_iterator _Last, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_First, _Last) with [_Ptr, _Ptr + _Count) _Adl_verify_range(_First, _Last); @@ -3480,7 +3674,8 @@ public: static_cast(_Last._Ptr - _First._Ptr), _Ptr, _Count); } - basic_string& replace(const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) { // replace [_First, _Last) with [_Ptr, ) _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3490,7 +3685,7 @@ public: static_cast(_Last._Ptr - _First._Ptr), _Ptr); } - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const size_type _Count, const _Elem _Ch) { // replace [_First, _Last) with _Count * _Ch _Adl_verify_range(_First, _Last); @@ -3502,7 +3697,7 @@ public: } template , int> = 0> - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const _Iter _First2, const _Iter _Last2) { // replace [_First, _Last) with [_First2, _Last2), input iterators _Adl_verify_range(_First, _Last); @@ -3522,89 +3717,107 @@ public: } } - _NODISCARD iterator begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator begin() noexcept { return iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } - _NODISCARD const_iterator begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator begin() const noexcept { return const_iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } - _NODISCARD iterator end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator end() noexcept { return iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } - _NODISCARD const_iterator end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator end() const noexcept { return const_iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } - _Elem* _Unchecked_begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Elem* _Unchecked_begin() noexcept { return _Mypair._Myval2._Myptr(); } - const _Elem* _Unchecked_begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Elem* _Unchecked_begin() const noexcept { return _Mypair._Myval2._Myptr(); } - _Elem* _Unchecked_end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Elem* _Unchecked_end() noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } - const _Elem* _Unchecked_end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Elem* _Unchecked_end() const noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } - _NODISCARD reverse_iterator rbegin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - _NODISCARD const_reverse_iterator rbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - _NODISCARD reverse_iterator rend() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - _NODISCARD const_reverse_iterator rend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - _NODISCARD const_iterator cbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cbegin() const noexcept { return begin(); } - _NODISCARD const_iterator cend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cend() const noexcept { return end(); } - _NODISCARD const_reverse_iterator crbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crbegin() const noexcept { return rbegin(); } - _NODISCARD const_reverse_iterator crend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crend() const noexcept { return rend(); } - void shrink_to_fit() { // reduce capacity + _CONSTEXPR20_CONTAINER void shrink_to_fit() { // reduce capacity auto& _My_data = _Mypair._Myval2; - if (!_My_data._Large_string_engaged()) { // can't shrink from small mode - return; - } - if (_My_data._Mysize < _BUF_SIZE) { - _Become_small(); - return; +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + if (!_My_data._Large_string_engaged()) { // can't shrink from small mode + return; + } + + if (_My_data._Mysize < _BUF_SIZE) { + _Become_small(); + return; + } } - const size_type _Target_capacity = (_STD min)(_My_data._Mysize | _ALLOC_MASK, max_size()); + size_type _Target_capacity = (_STD min)(_My_data._Mysize | _ALLOC_MASK, max_size()); +#ifdef __cpp_lib_constexpr_string + // must allocate at least _BUF_SIZE space + _Target_capacity = (_STD max)(_Target_capacity, _BUF_SIZE); +#endif // __cpp_lib_constexpr_string + if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _Target_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _My_data._Orphan_all(); _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_My_data._Bx._Ptr), _My_data._Mysize + 1); _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1); @@ -3613,24 +3826,25 @@ public: } } - _NODISCARD reference at(const size_type _Off) { + _NODISCARD _CONSTEXPR20_CONTAINER reference at(const size_type _Off) { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD const_reference at(const size_type _Off) const { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference at(const size_type _Off) const { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD reference operator[](const size_type _Off) noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const size_type _Off) noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD const_reference operator[](const size_type _Off) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference operator[](const size_type _Off) const noexcept + /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3638,13 +3852,13 @@ public: } #if _HAS_CXX17 - /* implicit */ operator basic_string_view<_Elem, _Traits>() const noexcept { + /* implicit */ _CONSTEXPR20_CONTAINER operator basic_string_view<_Elem, _Traits>() const noexcept { // return a string_view around *this's character-type sequence return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize); } #endif // _HAS_CXX17 - void push_back(const _Elem _Ch) { // insert element at end + _CONSTEXPR20_CONTAINER void push_back(const _Elem _Ch) { // insert element at end const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Old_size < _Mypair._Myval2._Myres) { _Mypair._Myval2._Mysize = _Old_size + 1; @@ -3664,7 +3878,7 @@ public: _Ch); } - void pop_back() noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void pop_back() noexcept /* strengthened */ { const size_type _Old_size = _Mypair._Myval2._Mysize; #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Old_size != 0, "invalid to pop_back empty string"); @@ -3672,7 +3886,7 @@ public: _Eos(_Old_size - 1); } - _NODISCARD reference front() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference front() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3680,7 +3894,7 @@ public: return _Mypair._Myval2._Myptr()[0]; } - _NODISCARD const_reference front() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference front() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3688,7 +3902,7 @@ public: return _Mypair._Myval2._Myptr()[0]; } - _NODISCARD reference back() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference back() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3696,7 +3910,7 @@ public: return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } - _NODISCARD const_reference back() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference back() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3704,29 +3918,29 @@ public: return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } - _NODISCARD _Ret_z_ const _Elem* c_str() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ const _Elem* c_str() const noexcept { return _Mypair._Myval2._Myptr(); } - _NODISCARD _Ret_z_ const _Elem* data() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ const _Elem* data() const noexcept { return _Mypair._Myval2._Myptr(); } #if _HAS_CXX17 - _NODISCARD _Ret_z_ _Elem* data() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ _Elem* data() noexcept { return _Mypair._Myval2._Myptr(); } #endif // _HAS_CXX17 - _NODISCARD size_type length() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type length() const noexcept { return _Mypair._Myval2._Mysize; } - _NODISCARD size_type size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type size() const noexcept { return _Mypair._Myval2._Mysize; } - _NODISCARD size_type max_size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type max_size() const noexcept { const size_type _Alloc_max = _Alty_traits::max_size(_Getal()); const size_type _Storage_max = // can always store small string (_STD max)(_Alloc_max, static_cast(_BUF_SIZE)); @@ -3735,7 +3949,7 @@ public: ); } - void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Elem _Ch = _Elem()) { + _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Elem _Ch = _Elem()) { // determine new length, padding with _Ch elements as needed const size_type _Old_size = size(); if (_Newsize <= _Old_size) { @@ -3745,12 +3959,13 @@ public: } } - _NODISCARD size_type capacity() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type capacity() const noexcept { return _Mypair._Myval2._Myres; } #if _HAS_CXX20 - void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) { // determine new minimum length of allocated storage + _CONSTEXPR20_CONTAINER void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) { + // determine new minimum length of allocated storage if (_Mypair._Myval2._Myres >= _Newcap) { // requested capacity is not larger than current capacity, ignore return; // nothing to do } @@ -3800,11 +4015,12 @@ public: } #endif // _HAS_CXX20 - _NODISCARD bool empty() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool empty() const noexcept { return size() == 0; } - size_type copy(_Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { + _CONSTEXPR20_CONTAINER size_type copy( + _Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); @@ -3812,8 +4028,9 @@ public: return _Count; } - _Pre_satisfies_(_Dest_size >= _Count) size_type _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, - const size_type _Dest_size, size_type _Count, const size_type _Off = 0) const { + _CONSTEXPR20_CONTAINER _Pre_satisfies_(_Dest_size >= _Count) size_type + _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count, + const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Dest, _Dest + _Dest_size) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); @@ -3821,30 +4038,37 @@ public: return _Count; } - void _Swap_data(basic_string& _Right, true_type) { - // exchange _String_val instances with _Right, memcpy optimization - const auto _My_data_mem = - reinterpret_cast(_STD addressof(_Mypair._Myval2)) + _Memcpy_val_offset; - const auto _Right_data_mem = - reinterpret_cast(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset; - unsigned char _Temp_mem[_Memcpy_val_size]; - _CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size); - _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); - _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); - } - void _Swap_bx_large_with_small(_Scary_val& _Starts_large, _Scary_val& _Starts_small) { // exchange a string in large mode with one in small mode + // (not _CONSTEXPR20_CONTAINER; SSO should be disabled in a constexpr context) + const pointer _Ptr = _Starts_large._Bx._Ptr; _Destroy_in_place(_Starts_large._Bx._Ptr); _Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE); _Construct_in_place(_Starts_small._Bx._Ptr, _Ptr); } - void _Swap_data(basic_string& _Right, false_type) { - // exchange _String_val instances with _Right, general case - auto& _My_data = _Mypair._Myval2; - auto& _Right_data = _Right._Mypair._Myval2; + _CONSTEXPR20_CONTAINER void _Swap_data(basic_string& _Right) { + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + if constexpr (_Can_memcpy_val) { +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + const auto _My_data_mem = + reinterpret_cast(_STD addressof(_My_data)) + _Memcpy_val_offset; + const auto _Right_data_mem = + reinterpret_cast(_STD addressof(_Right_data)) + _Memcpy_val_offset; + unsigned char _Temp_mem[_Memcpy_val_size]; + _CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size); + _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); + _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); + return; + } + } + const bool _My_large = _My_data._Large_string_engaged(); const bool _Right_large = _Right_data._Large_string_engaged(); if (_My_large) { @@ -3868,7 +4092,7 @@ public: _STD swap(_My_data._Myres, _Right_data._Myres); } - void swap(basic_string& _Right) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void swap(basic_string& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); @@ -3889,12 +4113,12 @@ public: #endif // _ITERATOR_DEBUG_LEVEL != 0 } - _Swap_data(_Right, bool_constant<_Can_memcpy_val>{}); + _Swap_data(_Right); } #if _HAS_CXX17 template = 0> - _NODISCARD size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for _Right beginning at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find<_Traits>( @@ -3902,27 +4126,29 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for _Right beginning at or after _Off return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize)); } - _NODISCARD size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning at or after _Off return static_cast( _Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count)); } - _NODISCARD size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning at or after _Off return static_cast(_Traits_find<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); } - _NODISCARD size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept + /* strengthened */ { // look for _Ch at or after _Off return static_cast( _Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -3930,7 +4156,7 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for _Right beginning before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_rfind<_Traits>( @@ -3938,27 +4164,29 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type rfind(const basic_string& _Right, const size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind( + const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for _Right beginning before _Off return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize)); } - _NODISCARD size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning before _Off return static_cast( _Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count)); } - _NODISCARD size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning before _Off return static_cast(_Traits_rfind<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); } - _NODISCARD size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept + /* strengthened */ { // look for _Ch before _Off return static_cast( _Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -3966,7 +4194,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_first_of(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for one of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, @@ -3974,28 +4203,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_first_of(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept + /* strengthened */ { // look for _Ch at or after _Off return static_cast( _Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4003,7 +4234,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_last_of(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for one of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, @@ -4011,28 +4243,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_last_of(const basic_string& _Right, size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const basic_string& _Right, size_type _Off = npos) const noexcept { // look for one of _Right before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for one of [_Ptr, ) before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { // look for _Ch before _Off return static_cast( _Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4040,7 +4274,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_first_not_of(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for none of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast( @@ -4049,29 +4284,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_first_not_of(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(_In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + _In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { // look for non-_Ch at or after _Off return static_cast( _Traits_find_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4079,7 +4315,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_last_not_of(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for none of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast( @@ -4088,29 +4325,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_last_not_of(const basic_string& _Right, const size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for none of _Right before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for none of [_Ptr, ) before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { // look for non-_Ch before _Off return static_cast( _Traits_rfind_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4118,34 +4356,40 @@ public: #if _HAS_CXX17 _NODISCARD bool _Starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + // Used exclusively by filesystem return basic_string_view<_Elem, _Traits>(*this)._Starts_with(_Right); } #endif // _HAS_CXX17 - _NODISCARD basic_string substr(const size_type _Off = 0, const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER basic_string substr( + const size_type _Off = 0, const size_type _Count = npos) const { // return [_Off, _Off + _Count) as new string return basic_string(*this, _Off, _Count, get_allocator()); } - bool _Equal(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right for equality + _CONSTEXPR20_CONTAINER bool _Equal(const basic_string& _Right) const noexcept { + // compare [0, size()) with _Right for equality return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept { // compare [0, size()) with _Ptr for equality + _CONSTEXPR20_CONTAINER bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept { + // compare [0, size()) with _Ptr for equality return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr)); } #if _HAS_CXX17 template = 0> - _NODISCARD int compare(const _StringViewIsh& _Right) const { // compare [0, size()) with _Right + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const _StringViewIsh& _Right) const { + // compare [0, size()) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _As_view.data(), _As_view.size()); } template = 0> - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare( + const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const { // compare [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); @@ -4154,8 +4398,8 @@ public: } template = 0> - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, - const size_type _Roff, const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); @@ -4165,20 +4409,21 @@ public: } #endif // _HAS_CXX17 - _NODISCARD int compare(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const basic_string& _Right) const noexcept { + // compare [0, size()) with _Right return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - _NODISCARD int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const { // compare [_Off, _Off + _Nx) with _Right _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right, const size_type _Roff, - const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right, + const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Mypair._Myval2._Check_offset(_Off); _Right._Mypair._Myval2._Check_offset(_Roff); @@ -4186,57 +4431,59 @@ public: _Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count)); } - _NODISCARD int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { // compare [0, size()) with [_Ptr, ) return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr)); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare( + const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { // compare [_Off, _Off + _Nx) with [_Ptr, ) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Ptr, _Traits::length(_Ptr)); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, - const size_type _Count) const { // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, + _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const { + // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Ptr, _Count); } #if _HAS_CXX20 - _NODISCARD bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool starts_with(const _Elem _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } - _NODISCARD bool ends_with(const _Elem _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } - _NODISCARD bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } #endif // _HAS_CXX20 - _NODISCARD allocator_type get_allocator() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER allocator_type get_allocator() const noexcept { return static_cast(_Getal()); } private: - _NODISCARD static size_type _Calculate_growth( + _NODISCARD static _CONSTEXPR20_CONTAINER size_type _Calculate_growth( const size_type _Requested, const size_type _Old, const size_type _Max) noexcept { const size_type _Masked = _Requested | _ALLOC_MASK; if (_Masked > _Max) { // the mask overflows, settle for max_size() @@ -4250,12 +4497,12 @@ private: return (_STD max)(_Masked, _Old + _Old / 2); } - _NODISCARD size_type _Calculate_growth(const size_type _Requested) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type _Calculate_growth(const size_type _Requested) const noexcept { return _Calculate_growth(_Requested, _Mypair._Myval2._Myres, max_size()); } template - basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) { + _CONSTEXPR20_CONTAINER basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) { // reallocate to store exactly _New_size elements, new buffer prepared by // _Fn(_New_ptr, _New_size, _Args...) if (_New_size > max_size()) { @@ -4266,6 +4513,12 @@ private: const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Mypair._Myval2._Orphan_all(); _Mypair._Myval2._Mysize = _New_size; _Mypair._Myval2._Myres = _New_capacity; @@ -4281,7 +4534,8 @@ private: } template - basic_string& _Reallocate_grow_by(const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { + _CONSTEXPR20_CONTAINER basic_string& _Reallocate_grow_by( + const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { // reallocate to increase size by _Size_increase elements, new buffer prepared by // _Fn(_New_ptr, _Old_ptr, _Old_size, _Args...) auto& _My_data = _Mypair._Myval2; @@ -4295,6 +4549,12 @@ private: const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _My_data._Orphan_all(); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; @@ -4316,6 +4576,8 @@ private: // release any held storage and return to small string mode // pre: *this is in large string mode // pre: this is small enough to return to small string mode + // (not _CONSTEXPR20_CONTAINER; SSO should be disabled in a constexpr context) + _Mypair._Myval2._Orphan_all(); const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; auto& _Al = _Getal(); @@ -4325,18 +4587,33 @@ private: _Mypair._Myval2._Myres = _BUF_SIZE - 1; } - void _Eos(const size_type _Newsize) { // set new length and null terminator + _CONSTEXPR20_CONTAINER void _Eos(const size_type _Newsize) { // set new length and null terminator _Traits::assign(_Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize = _Newsize], _Elem()); } - void _Tidy_init() noexcept { // initialize basic_string data members - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = _BUF_SIZE - 1; - // the _Traits::assign is last so the codegen doesn't think the char write can alias this - _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); + _CONSTEXPR20_CONTAINER void _Tidy_init() noexcept { // initialize basic_string data members + auto& _My_data = _Mypair._Myval2; + _My_data._Mysize = 0; + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _My_data._Myres = _BUF_SIZE; // SSO disabled in constexpr context + auto& _Al = _Getal(); + const pointer _New_ptr = _Al.allocate(_BUF_SIZE + 1); // throws + _My_data._Bx._Ptr = _New_ptr; + + _Elem* const _Raw_new = _Unfancy(_New_ptr); + _Traits::assign(_Raw_new, _BUF_SIZE + 1, _Elem()); + } else +#endif // __cpp_lib_constexpr_string + { + _My_data._Myres = _BUF_SIZE - 1; + // the _Traits::assign is last so the codegen doesn't think the char write can alias this + _Traits::assign(_My_data._Bx._Buf[0], _Elem()); + } } - void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage + _CONSTEXPR20_CONTAINER void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage _Mypair._Myval2._Orphan_all(); if (_Mypair._Myval2._Large_string_engaged()) { const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; @@ -4345,27 +4622,36 @@ private: _Al.deallocate(_Ptr, _Mypair._Myval2._Myres + 1); } - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = _BUF_SIZE - 1; - // the _Traits::assign is last so the codegen doesn't think the char write can alias this - _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _Mypair._Myval2._Bx._Ptr = nullptr; + _Mypair._Myval2._Mysize = 0; + _Mypair._Myval2._Myres = 0; + } else +#endif // __cpp_lib_constexpr_string + { + _Mypair._Myval2._Mysize = 0; + _Mypair._Myval2._Myres = _BUF_SIZE - 1; + // the _Traits::assign is last so the codegen doesn't think the char write can alias this + _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); + } } public: - void _Orphan_all() noexcept { // used by filesystem::path + _CONSTEXPR20_CONTAINER void _Orphan_all() noexcept { // used by filesystem::path _Mypair._Myval2._Orphan_all(); } private: - void _Swap_proxy_and_iterators(basic_string& _Right) { + _CONSTEXPR20_CONTAINER void _Swap_proxy_and_iterators(basic_string& _Right) { _Mypair._Myval2._Swap_proxy_and_iterators(_Right._Mypair._Myval2); } - _Alty& _Getal() noexcept { + _CONSTEXPR20_CONTAINER _Alty& _Getal() noexcept { return _Mypair._Get_first(); } - const _Alty& _Getal() const noexcept { + _CONSTEXPR20_CONTAINER const _Alty& _Getal() const noexcept { return _Mypair._Get_first(); } @@ -4390,13 +4676,13 @@ basic_string(basic_string_view<_Elem, _Traits>, _Guide_size_type_t<_Alloc>, _Gui #endif // _HAS_CXX17 template -void swap(basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept -/* strengthened */ { +_CONSTEXPR20_CONTAINER void swap(basic_string<_Elem, _Traits, _Alloc>& _Left, + basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { _Left.swap(_Right); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Left_size = _Left.size(); const auto _Right_size = _Right.size(); @@ -4408,7 +4694,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Convert_size<_Size_type>(_Traits::length(_Left)); @@ -4421,7 +4707,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Right_size = _Right.size(); if (_Right_size == _Right.max_size()) { @@ -4432,7 +4718,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Left.size(); @@ -4445,7 +4731,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const _Elem _Right) { const auto _Left_size = _Left.size(); if (_Left_size == _Left.max_size()) { @@ -4456,19 +4742,19 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { return _STD move(_Left.append(_Right)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(_STD addressof(_Left) != _STD addressof(_Right), @@ -4481,44 +4767,58 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, 1, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, _In_z_ const _Elem* const _Right) { return _STD move(_Left.append(_Right)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const _Elem _Right) { _Left.push_back(_Right); return _STD move(_Left); } template -_NODISCARD bool operator==( +_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return _Left._Equal(_Right); } template -_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { - return _Right._Equal(_Left); +_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( + const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return _Left._Equal(_Right); } +#if _HAS_CXX20 template -_NODISCARD bool operator==(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { - return _Left._Equal(_Right); +_NODISCARD _CONSTEXPR20_CONTAINER _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} + +template +_NODISCARD _CONSTEXPR20_CONTAINER _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { + return _Right._Equal(_Left); } template @@ -4600,6 +4900,7 @@ template _NODISCARD bool operator>=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { return !(_Left < _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ using string = basic_string, allocator>; using wstring = basic_string, allocator>; @@ -4685,39 +4986,49 @@ basic_ostream<_Elem, _Traits>& operator<<( // basic_string LITERALS inline namespace literals { inline namespace string_literals { - _NODISCARD inline string operator"" s(const char* _Str, size_t _Len) { + +#ifdef __EDG__ // TRANSITION, VSO-1273381 +#define _CONSTEXPR20_STRING_LITERALS inline +#else // ^^^ workaround / no workaround vvv +#define _CONSTEXPR20_STRING_LITERALS _CONSTEXPR20_CONTAINER +#endif // ^^^ no workaround ^^^ + + _NODISCARD _CONSTEXPR20_STRING_LITERALS string operator"" s(const char* _Str, size_t _Len) { return string(_Str, _Len); } - _NODISCARD inline wstring operator"" s(const wchar_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS wstring operator"" s(const wchar_t* _Str, size_t _Len) { return wstring(_Str, _Len); } #ifdef __cpp_char8_t - _NODISCARD inline basic_string operator"" s(const char8_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS basic_string operator"" s(const char8_t* _Str, size_t _Len) { return basic_string(_Str, _Len); } #endif // __cpp_char8_t - _NODISCARD inline u16string operator"" s(const char16_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS u16string operator"" s(const char16_t* _Str, size_t _Len) { return u16string(_Str, _Len); } - _NODISCARD inline u32string operator"" s(const char32_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS u32string operator"" s(const char32_t* _Str, size_t _Len) { return u32string(_Str, _Len); } + +#undef _CONSTEXPR20_STRING_LITERALS // TRANSITION, VSO-1273381 + } // namespace string_literals } // namespace literals #if _HAS_CXX20 template -typename basic_string<_Elem, _Traits, _Alloc>::size_type erase( +_CONSTEXPR20_CONTAINER typename basic_string<_Elem, _Traits, _Alloc>::size_type erase( basic_string<_Elem, _Traits, _Alloc>& _Cont, const _Uty& _Val) { return _Erase_remove(_Cont, _Val); } template -typename basic_string<_Elem, _Traits, _Alloc>::size_type erase_if( +_CONSTEXPR20_CONTAINER typename basic_string<_Elem, _Traits, _Alloc>::size_type erase_if( basic_string<_Elem, _Traits, _Alloc>& _Cont, _Pr _Pred) { return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); } diff --git a/stl/inc/xtree b/stl/inc/xtree index aba7f46baee..3d9e6786ee0 100644 --- a/stl/inc/xtree +++ b/stl/inc/xtree @@ -98,18 +98,22 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return !!_Ptr->_Isnil; // TRANSITION, avoid warning C4800: // "Implicit conversion from 'char' to bool. Possible information loss" (/Wall) } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return !_Ptr->_Isnil; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -232,9 +236,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Tree_const_iterator& _First, const _Tree_const_iterator& _Last) noexcept { diff --git a/stl/inc/xutility b/stl/inc/xutility index 2787de4a4ee..2911a66b1cb 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -131,18 +131,26 @@ _NODISCARD constexpr void* _Voidify_iter(_Iter _It) noexcept { // FUNCTION TEMPLATE construct_at #if _HAS_CXX20 -template -_CONSTEXPR20_DYNALLOC auto construct_at(_Ty* const _Location, _Types&&... _Args) noexcept( - noexcept(::new (_Voidify_iter(_Location)) _Ty(_STD forward<_Types>(_Args)...))) // strengthened - -> decltype(::new (_Voidify_iter(_Location)) _Ty(_STD forward<_Types>(_Args)...)) { +template ()) _Ty(_STD declval<_Types>()...))>> +_CONSTEXPR20_DYNALLOC _Ty* construct_at(_Ty* const _Location, _Types&&... _Args) noexcept( + noexcept(::new (_Voidify_iter(_Location)) _Ty(_STD forward<_Types>(_Args)...))) /* strengthened */ { return ::new (_Voidify_iter(_Location)) _Ty(_STD forward<_Types>(_Args)...); } #endif // _HAS_CXX20 // FUNCTION TEMPLATE _Construct_in_place template -void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { - ::new (_Voidify_iter(_STD addressof(_Obj))) _Ty(_STD forward<_Types>(_Args)...); +_CONSTEXPR20_DYNALLOC void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_Ty, _Types...>) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _STD construct_at(_STD addressof(_Obj), _STD forward<_Types>(_Args)...); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + ::new (_Voidify_iter(_STD addressof(_Obj))) _Ty(_STD forward<_Types>(_Args)...); + } } // FUNCTION TEMPLATE _Default_construct_in_place @@ -766,11 +774,11 @@ concept indirectly_writable = requires(_It&& __i, _Ty&& __t) { }; // CONCEPT _Integer_like -// clang-format off template -concept _Integer_like = _Is_nonbool_integral<_Ty>; +concept _Integer_like = _Is_nonbool_integral>; // CONCEPT _Signed_integer_like +// clang-format off template concept _Signed_integer_like = _Integer_like<_Ty> && static_cast<_Ty>(-1) < static_cast<_Ty>(0); // clang-format on @@ -779,10 +787,20 @@ concept _Signed_integer_like = _Integer_like<_Ty> && static_cast<_Ty>(-1) < stat template using _Make_unsigned_like_t = make_unsigned_t<_Ty>; +template <_Integer_like _Ty> +_NODISCARD constexpr auto _To_unsigned_like(const _Ty _Value) noexcept { + return static_cast<_Make_unsigned_like_t<_Ty>>(_Value); +} + // ALIAS TEMPLATE _Make_signed_like_t template using _Make_signed_like_t = make_signed_t<_Ty>; +template <_Integer_like _Ty> +_NODISCARD constexpr auto _To_signed_like(const _Ty _Value) noexcept { + return static_cast<_Make_signed_like_t<_Ty>>(_Value); +} + // CONCEPT weakly_incrementable // clang-format off template @@ -3763,7 +3781,7 @@ public: #endif // __cpp_lib_concepts } - _NODISCARD _CXX20_DEPRECATE_MOVE_ITERATOR_ARROW _CONSTEXPR17 pointer operator->() const { + _CXX20_DEPRECATE_MOVE_ITERATOR_ARROW _NODISCARD _CONSTEXPR17 pointer operator->() const { return _Current; } @@ -4108,17 +4126,6 @@ _OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _O return _Copy_memmove(_First.base(), _Last.base(), _Dest); } -template -_OutIt _Copy_memcpy_common(_InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) noexcept { - const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirst)); - const auto _ILast_ch = const_cast(reinterpret_cast(_ILast)); - const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirst)); - const auto _OLast_ch = const_cast(reinterpret_cast(_OLast)); - const auto _Count = static_cast((_STD min)(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch)); - _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count); - return reinterpret_cast<_OutIt>(_OFirst_ch + _Count); -} - // VARIABLE TEMPLATE _Is_vb_iterator template _INLINE_VAR constexpr bool _Is_vb_iterator = false; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index e37d8daf036..973143b93e3 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -133,11 +133,14 @@ // _HAS_CXX20 directly controls: // P0019R8 atomic_ref // P0020R6 atomic, atomic, atomic +// P0053R7 // P0122R7 // P0202R3 constexpr For And exchange() // P0318R1 unwrap_reference, unwrap_ref_decay // P0325R4 to_array() // P0339R6 polymorphic_allocator<> +// P0355R7 Calendars And Time Zones +// (partially implemented) // P0356R5 bind_front() // P0357R3 Supporting Incomplete Types In reference_wrapper // P0408R7 Efficient Access To basic_stringbuf's Buffer @@ -146,6 +149,8 @@ // P0457R2 starts_with()/ends_with() For basic_string/basic_string_view // P0458R2 contains() For Ordered And Unordered Associative Containers // P0463R1 endian +// P0466R5 Layout-Compatibility And Pointer-Interconvertibility Traits +// P0475R1 Guaranteed Copy Elision For Piecewise Construction // P0476R2 bit_cast // P0482R6 Library Support For char8_t // (mbrtoc8 and c8rtomb not yet implemented) @@ -155,7 +160,9 @@ // P0553R4 Rotating And Counting Functions // P0556R3 Integral Power-Of-2 Operations (renamed by P1956R1) // P0586R2 Integer Comparison Functions +// P0591R4 Utility Functions For Uses-Allocator Construction // P0595R2 is_constant_evaluated() +// P0608R3 Improving variant's Converting Constructor/Assignment // P0616R0 Using move() In // P0631R8 Math Constants // P0646R1 list/forward_list remove()/remove_if()/unique() Return size_type @@ -164,6 +171,7 @@ // P0660R10 And jthread // P0674R1 make_shared() For Arrays // P0718R2 atomic>, atomic> +// P0753R2 osyncstream Manipulators // P0758R1 is_nothrow_convertible // P0768R1 Library Support For The Spaceship Comparison Operator <=> // P0769R2 shift_left(), shift_right() @@ -177,7 +185,9 @@ // P0912R5 Library Support For Coroutines // P0919R3 Heterogeneous Lookup For Unordered Containers // P0966R1 string::reserve() Should Not Shrink +// P0980R1 constexpr std::string // P1001R2 execution::unseq +// P1004R2 constexpr std::vector // P1006R1 constexpr For pointer_traits::pointer_to() // P1007R3 assume_aligned() // P1020R1 Smart Pointer Creation With Default Initialization @@ -192,6 +202,7 @@ // P1135R6 The C++20 Synchronization Library // P1207R4 Movability Of Single-Pass Iterators // (partially implemented) +// P1208R6 // P1209R0 erase_if(), erase() // P1227R2 Signed std::ssize(), Unsigned span::size() // P1243R4 Rangify New Algorithms @@ -204,6 +215,7 @@ // P1456R1 Move-Only Views // P1474R1 Helpful Pointers For contiguous_iterator // P1612R1 Relocating endian To +// P1614R2 Adding Spaceship <=> To The Library // P1645R1 constexpr For Algorithms // P1651R0 bind_front() Should Not Unwrap reference_wrapper // P1690R1 Refining Heterogeneous Lookup For Unordered Containers @@ -503,7 +515,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 142 -#define _MSVC_STL_UPDATE 202101L +#define _MSVC_STL_UPDATE 202103L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #ifdef __CUDACC__ @@ -519,8 +531,8 @@ #error STL1000: Unexpected compiler version, expected Clang 11.0.0 or newer. #endif // ^^^ old Clang ^^^ #elif defined(_MSC_VER) -#if _MSC_VER < 1928 // Coarse-grained, not inspecting _MSC_FULL_VER -#error STL1001: Unexpected compiler version, expected MSVC 19.28 or newer. +#if _MSC_VER < 1929 // Coarse-grained, not inspecting _MSC_FULL_VER +#error STL1001: Unexpected compiler version, expected MSVC 19.29 or newer. #endif // ^^^ old MSVC ^^^ #else // vvv other compilers vvv // not attempting to detect other compilers @@ -1024,6 +1036,10 @@ #define _HAS_DEPRECATED_ADAPTOR_TYPEDEFS (_HAS_FEATURES_REMOVED_IN_CXX20) #endif // _HAS_DEPRECATED_ADAPTOR_TYPEDEFS +#ifndef _HAS_DEPRECATED_ALLOCATOR_MEMBERS +#define _HAS_DEPRECATED_ALLOCATOR_MEMBERS (_HAS_FEATURES_REMOVED_IN_CXX20) +#endif // _HAS_DEPRECATED_ALLOCATOR_MEMBERS + #ifndef _HAS_DEPRECATED_IS_LITERAL_TYPE #define _HAS_DEPRECATED_IS_LITERAL_TYPE (_HAS_FEATURES_REMOVED_IN_CXX20) #endif // _HAS_DEPRECATED_IS_LITERAL_TYPE @@ -1183,22 +1199,26 @@ #define __cpp_lib_constexpr_algorithms 201806L #define __cpp_lib_constexpr_complex 201711L -#if defined(__cpp_constexpr_dynamic_alloc) \ - && defined(__clang__) // TRANSITION, MSVC support for constexpr dynamic allocation +#ifdef __cpp_constexpr_dynamic_alloc #define __cpp_lib_constexpr_dynamic_alloc 201907L -#endif // defined(__cpp_constexpr_dynamic_alloc) && defined(__clang__) +#endif // __cpp_constexpr_dynamic_alloc + +#define __cpp_lib_constexpr_functional 201907L +#define __cpp_lib_constexpr_iterator 201811L +#define __cpp_lib_constexpr_memory 201811L +#define __cpp_lib_constexpr_numeric 201911L + +#if defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) // TRANSITION, LLVM-48606 +#define __cpp_lib_constexpr_string 201907L +#endif // defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) -#define __cpp_lib_constexpr_functional 201907L -#define __cpp_lib_constexpr_iterator 201811L -#define __cpp_lib_constexpr_memory 201811L -#define __cpp_lib_constexpr_numeric 201911L #define __cpp_lib_constexpr_string_view 201811L #define __cpp_lib_constexpr_tuple 201811L #define __cpp_lib_constexpr_utility 201811L -#ifdef __cpp_impl_coroutine // TRANSITION, Clang coroutine support -#define __cpp_lib_coroutine 201902L -#endif // __cpp_impl_coroutine +#if defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) // TRANSITION, LLVM-48606 +#define __cpp_lib_constexpr_vector 201907L +#endif // defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) #define __cpp_lib_destroying_delete 201806L #define __cpp_lib_endian 201907L @@ -1208,22 +1228,42 @@ #define __cpp_lib_integer_comparison_functions 202002L #define __cpp_lib_interpolate 201902L #define __cpp_lib_is_constant_evaluated 201811L -#define __cpp_lib_is_nothrow_convertible 201806L -#define __cpp_lib_jthread 201911L -#define __cpp_lib_latch 201907L -#define __cpp_lib_list_remove_return_type 201806L -#define __cpp_lib_math_constants 201907L -#define __cpp_lib_polymorphic_allocator 201902L -#define __cpp_lib_remove_cvref 201711L -#define __cpp_lib_semaphore 201907L -#define __cpp_lib_shift 201806L -#define __cpp_lib_smart_ptr_for_overwrite 202002L -#define __cpp_lib_span 202002L -#define __cpp_lib_ssize 201902L -#define __cpp_lib_starts_ends_with 201711L + +#ifndef __EDG__ // TRANSITION, VSO-1268984 +#ifndef __clang__ // TRANSITION, LLVM-48860 +#define __cpp_lib_is_layout_compatible 201907L +#endif // __clang__ +#endif // __EDG__ + +#define __cpp_lib_is_nothrow_convertible 201806L + +#ifndef __EDG__ // TRANSITION, VSO-1268984 +#ifndef __clang__ // TRANSITION, LLVM-48860 +#define __cpp_lib_is_pointer_interconvertible 201907L +#endif // __clang__ +#endif // __EDG__ + +#define __cpp_lib_jthread 201911L +#define __cpp_lib_latch 201907L +#define __cpp_lib_list_remove_return_type 201806L +#define __cpp_lib_math_constants 201907L +#define __cpp_lib_polymorphic_allocator 201902L +#define __cpp_lib_remove_cvref 201711L +#define __cpp_lib_semaphore 201907L +#define __cpp_lib_shift 201806L +#define __cpp_lib_smart_ptr_for_overwrite 202002L + +#ifdef __cpp_consteval +#define __cpp_lib_source_location 201907L +#endif // __cpp_consteval + +#define __cpp_lib_span 202002L +#define __cpp_lib_ssize 201902L +#define __cpp_lib_starts_ends_with 201711L +#define __cpp_lib_syncbuf 201803L #ifdef __cpp_lib_concepts // TRANSITION, GH-395 -#define __cpp_lib_three_way_comparison 201711L +#define __cpp_lib_three_way_comparison 201907L #endif // __cpp_lib_concepts #define __cpp_lib_to_address 201711L @@ -1252,6 +1292,10 @@ #define __cpp_lib_shared_ptr_arrays 201611L // P0497R0 Fixing shared_ptr For Arrays #endif // _HAS_CXX20 +#if defined(__cpp_impl_coroutine) || defined(_DOWNLEVEL_COROUTINES_SUPPORTED) // TRANSITION, Clang coroutine support +#define __cpp_lib_coroutine 201902L +#endif // __cpp_impl_coroutine + // EXPERIMENTAL #define __cpp_lib_experimental_erase_if 201411L #define __cpp_lib_experimental_filesystem 201406L @@ -1263,6 +1307,13 @@ #define _CONSTEXPR20_DYNALLOC inline #endif +// Functions that became constexpr in C++20 via P0980R1 or P1004R2 +#if defined(__cpp_lib_constexpr_dynamic_alloc) && !defined(__clang__) // TRANSITION, LLVM-48606 +#define _CONSTEXPR20_CONTAINER constexpr +#else +#define _CONSTEXPR20_CONTAINER inline +#endif + #ifdef _RTC_CONVERSION_CHECKS_ENABLED #ifndef _ALLOW_RTCc_IN_STL #error /RTCc rejects conformant code, so it is not supported by the C++ Standard Library. Either remove this \ @@ -1278,6 +1329,7 @@ compiler option, or define _ALLOW_RTCc_IN_STL to acknowledge that you have recei #define _STD_BEGIN namespace std { #define _STD_END } #define _STD ::std:: +#define _CHRONO ::std::chrono:: #define _RANGES ::std::ranges:: // We use the stdext (standard extension) namespace to contain extensions that are not part of the current standard diff --git a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets index e7cc52c398d..05ebeab8862 100644 --- a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets +++ b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets @@ -8,6 +8,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception nativecpp diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 5fd1b263103..d14a8bc528d 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -6,16 +6,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --> - - nativecpp @@ -60,6 +57,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\cthread.cpp; $(CrtRoot)\github\stl\src\mutex.cpp; $(CrtRoot)\github\stl\src\pplerror.cpp; + $(CrtRoot)\github\stl\src\ppltasks.cpp; $(CrtRoot)\github\stl\src\taskscheduler.cpp; $(CrtRoot)\github\stl\src\xnotify.cpp; $(CrtRoot)\github\stl\src\xtime.cpp; @@ -151,14 +149,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception nativecpp - - nativecpp - - true - - diff --git a/stl/src/memory_resource.cpp b/stl/src/memory_resource.cpp index 862b1e91f09..911ca06a2fd 100644 --- a/stl/src/memory_resource.cpp +++ b/stl/src/memory_resource.cpp @@ -59,7 +59,7 @@ namespace pmr { } // FUNCTION null_memory_resource - extern "C" _CRT_SATELLITE_1 _NODISCARD memory_resource* __cdecl null_memory_resource() noexcept { + extern "C" _NODISCARD _CRT_SATELLITE_1 memory_resource* __cdecl null_memory_resource() noexcept { class _Null_resource final : public _Identity_equal_resource { virtual void* do_allocate(size_t, size_t) override { // Sorry, OOM! _Xbad_alloc(); diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index d8f2d843b5a..240916023ea 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -6,6 +6,7 @@ LIBRARY LIBRARYNAME EXPORTS + __std_acquire_shared_mutex_for_instance __std_atomic_compare_exchange_128 __std_atomic_get_mutex __std_atomic_has_cmpxchg16b @@ -24,5 +25,6 @@ EXPORTS __std_execution_wait_on_uchar __std_execution_wake_by_address_all __std_parallel_algorithms_hw_threads + __std_release_shared_mutex_for_instance __std_submit_threadpool_work __std_wait_for_threadpool_work_callbacks diff --git a/stl/src/special_math.cpp b/stl/src/special_math.cpp index 8a4dd5e0836..425e5da2c48 100644 --- a/stl/src/special_math.cpp +++ b/stl/src/special_math.cpp @@ -66,7 +66,7 @@ namespace { } // unnamed namespace _EXTERN_C -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_laguerre( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_laguerre( const unsigned int _Pn, const unsigned int _Pm, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -75,7 +75,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_laguerre( return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Pm, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_laguerref( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_laguerref( const unsigned int _Pn, const unsigned int _Pm, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -84,7 +84,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_laguerref( return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Pm, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_legendre( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_legendre( const unsigned int _Pl, const unsigned int _Pm, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -100,7 +100,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_legendre( }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_legendref( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_legendref( const unsigned int _Pl, const unsigned int _Pm, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -116,23 +116,23 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_legendref( }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_beta(const double _Px, const double _Py) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_beta(const double _Px, const double _Py) noexcept { return _Boost_call([=] { return ::boost::math::beta(_Px, _Py); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_betaf(const float _Px, const float _Py) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_betaf(const float _Px, const float _Py) noexcept { return _Boost_call([=] { return ::boost::math::beta(_Px, _Py); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_1(const double _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_1(const double _Pk) noexcept { return _Boost_call([=] { return ::boost::math::ellint_1(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_1f(const float _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_1f(const float _Pk) noexcept { return _Boost_call([=] { return ::boost::math::ellint_1(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_2(const double _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_2(const double _Pk) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -140,7 +140,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_2(const doubl return _Boost_call([=] { return ::boost::math::ellint_2(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_2f(const float _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_2f(const float _Pk) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -148,7 +148,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_2f(const float return _Boost_call([=] { return ::boost::math::ellint_2(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_3(const double _Pk, const double _Pnu) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_3(const double _Pk, const double _Pnu) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -160,7 +160,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_3(const doubl return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_3f(const float _Pk, const float _Pnu) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_3f(const float _Pk, const float _Pnu) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -172,7 +172,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_3f(const float return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_i(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_i(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -184,7 +184,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_i(const double return _Boost_call([=] { return ::boost::math::cyl_bessel_i(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_if(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_if(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -196,7 +196,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_if(const float return _Boost_call([=] { return ::boost::math::cyl_bessel_i(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_j(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_j(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -208,7 +208,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_j(const double return _Boost_call([=] { return ::boost::math::cyl_bessel_j(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_jf(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_jf(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -220,7 +220,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_jf(const float return _Boost_call([=] { return ::boost::math::cyl_bessel_j(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_k(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_k(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -232,7 +232,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_k(const double return _Boost_call([=] { return ::boost::math::cyl_bessel_k(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_kf(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_kf(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -244,7 +244,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_kf(const float return _Boost_call([=] { return ::boost::math::cyl_bessel_k(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_neumann(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_neumann(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -256,7 +256,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_neumann(const double return _Boost_call([=] { return ::boost::math::cyl_neumann(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_neumannf(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_neumannf(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -268,7 +268,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_neumannf(const float _ return _Boost_call([=] { return ::boost::math::cyl_neumann(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_1(const double _Pk, const double _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_1(const double _Pk, const double _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -280,7 +280,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_1(const double _Pk return _Boost_call([=] { return ::boost::math::ellint_1(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_1f(const float _Pk, const float _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_1f(const float _Pk, const float _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -292,7 +292,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_1f(const float _Pk, return _Boost_call([=] { return ::boost::math::ellint_1(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_2(const double _Pk, const double _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_2(const double _Pk, const double _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -304,7 +304,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_2(const double _Pk return _Boost_call([=] { return ::boost::math::ellint_2(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_2f(const float _Pk, const float _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_2f(const float _Pk, const float _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -316,7 +316,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_2f(const float _Pk, return _Boost_call([=] { return ::boost::math::ellint_2(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_3( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_3( const double _Pk, const double _Pnu, const double _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; @@ -333,7 +333,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_3( return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_3f( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_3f( const float _Pk, const float _Pnu, const float _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; @@ -350,7 +350,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_3f( return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_expint(const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_expint(const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -358,7 +358,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_expint(const double _Px) return _Boost_call([=] { return ::boost::math::expint(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_expintf(const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_expintf(const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -366,7 +366,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_expintf(const float _Px) n return _Boost_call([=] { return ::boost::math::expint(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hermite(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hermite(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -374,7 +374,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hermite(const unsigned in return _Boost_call([=] { return ::boost::math::hermite(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hermitef(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hermitef(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -382,7 +382,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hermitef(const unsigned in return _Boost_call([=] { return ::boost::math::hermite(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_laguerre(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_laguerre(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -390,7 +390,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_laguerre(const unsigned i return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_laguerref(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_laguerref(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -398,7 +398,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_laguerref(const unsigned i return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_legendre(const unsigned int _Pl, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_legendre(const unsigned int _Pl, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -406,7 +406,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_legendre(const unsigned i return _Boost_call([=] { return ::boost::math::legendre_p(_Pl, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_legendref(const unsigned int _Pl, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_legendref(const unsigned int _Pl, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -414,7 +414,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_legendref(const unsigned i return _Boost_call([=] { return ::boost::math::legendre_p(_Pl, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_riemann_zeta(const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_riemann_zeta(const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -422,7 +422,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_riemann_zeta(const double return _Boost_call([=] { return ::boost::math::zeta(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_riemann_zetaf(const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_riemann_zetaf(const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -430,7 +430,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_riemann_zetaf(const float return _Boost_call([=] { return ::boost::math::zeta(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_bessel(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_bessel(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -438,7 +438,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_bessel(const unsigned return _Boost_call([=] { return ::boost::math::sph_bessel(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_besself(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_besself(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -446,7 +446,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_besself(const unsigned return _Boost_call([=] { return ::boost::math::sph_bessel(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_legendre( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_legendre( const unsigned int _Pl, const unsigned int _Pm, const double _Ptheta) noexcept { if (_STD isnan(_Ptheta)) { return _Ptheta; @@ -455,7 +455,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_legendre( return _Boost_call([=] { return ::boost::math::spherical_harmonic_r(_Pl, _Pm, _Ptheta, 0.0); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_legendref( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_legendref( const unsigned int _Pl, const unsigned int _Pm, const float _Ptheta) noexcept { if (_STD isnan(_Ptheta)) { return _Ptheta; @@ -464,7 +464,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_legendref( return _Boost_call([=] { return ::boost::math::spherical_harmonic_r(_Pl, _Pm, _Ptheta, 0.0f); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_neumann(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_neumann(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -472,7 +472,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_neumann(const unsigne return _Boost_call([=] { return ::boost::math::sph_neumann(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_neumannf(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_neumannf(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -515,12 +515,12 @@ namespace { } // unnamed namespace _EXTERN_C -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hypot3( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hypot3( const double _Dx, const double _Dy, const double _Dz) noexcept { return _Hypot3(_Dx, _Dy, _Dz); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hypot3f( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hypot3f( const float _Dx, const float _Dy, const float _Dz) noexcept { return _Hypot3(_Dx, _Dy, _Dz); } diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp new file mode 100644 index 00000000000..77e7df373e9 --- /dev/null +++ b/stl/src/syncstream.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// initialize syncstream mutex map + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4074) +#pragma init_seg(compiler) +static std::_Init_locks initlocks; + +namespace { + // OBJECT DECLARATIONS + struct _Mutex_count_pair { + _STD shared_mutex _Mutex; + size_t _Ref_count = 0; + }; + + template + class _Crt_allocator { + public: + using value_type = _Ty; + using propagate_on_container_move_assignment = _STD true_type; + using is_always_equal = _STD true_type; + + constexpr _Crt_allocator() noexcept = default; + + constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; + template + constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} + + _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + const auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); + if (!_Ptr) { + throw _STD bad_alloc{}; + } + return static_cast<_Ty*>(_Ptr); + } + + void deallocate(_Ty* const _Ptr, size_t) noexcept { + _free_crt(_Ptr); + } + }; + + using _Map_alloc = _Crt_allocator<_STD pair>; + using _Map_type = _STD map, _Map_alloc>; + + _Map_type _Lookup_map; + _STD shared_mutex _Lookup_mutex; +} // unnamed namespace + +_EXTERN_C + +// TRANSITION, ABI: This returns a pointer to a C++ type. +// A flat C interface would return an opaque handle and would provide separate functions for locking and unlocking. +_NODISCARD _STD shared_mutex* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept { + try { + _STD scoped_lock _Guard(_Lookup_mutex); + auto& [_Mutex, _Refs] = _Lookup_map.try_emplace(_Ptr).first->second; + ++_Refs; + return &_Mutex; + } catch (...) { + return nullptr; + } +} + +void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept { + _STD scoped_lock _Guard(_Lookup_mutex); + const auto _Instance_mutex_iter = _Lookup_map.find(_Ptr); + _ASSERT_EXPR(_Instance_mutex_iter != _Lookup_map.end(), "No mutex exists for given instance!"); + auto& _Refs = _Instance_mutex_iter->second._Ref_count; + if (--_Refs == 0) { + _Lookup_map.erase(_Instance_mutex_iter); + } +} + +_END_EXTERN_C diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fab5573e6b1..76a64ab60d2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,8 @@ set(LLVM_PROJECT_SOURCE_DIR "${STL_SOURCE_DIR}/llvm-project" CACHE PATH set(LIBCXX_SOURCE_DIR "${LLVM_PROJECT_SOURCE_DIR}/libcxx" CACHE PATH "Location of the libcxx source tree") +option(TESTS_BUILD_ONLY "Only run the build steps of tests" OFF) + add_subdirectory(libcxx) add_subdirectory(std) add_subdirectory(tr1) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 8961a1cc341..229c4d2141e 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -57,21 +57,12 @@ std/language.support/support.limits/support.limits.general/locale.version.pass.c std/language.support/support.limits/support.limits.general/ostream.version.pass.cpp FAIL std/language.support/support.limits/support.limits.general/string_view.version.pass.cpp FAIL -# libc++ doesn't yet implement P1956R1, so it expects the old names `ispow2`, `ceil2`, `floor2`, and `log2p1` -std/numerics/bit/bit.pow.two/ceil2.pass.cpp FAIL -std/numerics/bit/bit.pow.two/floor2.pass.cpp FAIL -std/numerics/bit/bit.pow.two/ispow2.pass.cpp FAIL -std/numerics/bit/bit.pow.two/log2p1.pass.cpp FAIL - # test emits warning C4310: cast truncates constant value std/numerics/bit/bitops.rot/rotl.pass.cpp:0 FAIL # libc++ doesn't yet implement P1754R1 or P1964R2, so it expects an old value for `__cpp_lib_concepts` std/language.support/support.limits/support.limits.general/concepts.version.pass.cpp FAIL -# libc++ doesn't yet implement P1001R2, so it expects an old value for `__cpp_lib_execution` -std/language.support/support.limits/support.limits.general/execution.version.pass.cpp FAIL - # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" @@ -197,6 +188,8 @@ std/language.support/support.limits/support.limits.general/type_traits.version.p std/language.support/support.limits/support.limits.general/version.version.pass.cpp FAIL # Contest does not understand .sh tests, which must be run specially +std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp SKIPPED +std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp SKIPPED std/namespace/addressable_functions.sh.cpp SKIPPED std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp SKIPPED @@ -280,211 +273,31 @@ std/utilities/memory/default.allocator/allocator_void.deprecated_in_cxx17.verify std/utilities/memory/default.allocator/allocator.members/allocate.constexpr.size.verify.cpp SKIPPED std/utilities/memory/default.allocator/allocator.members/allocate.verify.cpp SKIPPED -# GH-1382: Our machinery doesn't understand compile-only `.compile.pass.cpp` tests -std/strings/string.view/string.view.io/stream_insert_decl_present.compile.pass.cpp SKIPPED - # *** MISSING STL FEATURES *** # C++20 P0355R7 " Calendars And Time Zones" -std/utilities/time/days.pass.cpp FAIL -std/utilities/time/months.pass.cpp FAIL -std/utilities/time/weeks.pass.cpp FAIL -std/utilities/time/years.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.members/decrement.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.members/increment.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.members/plus_minus_equal.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.nonmembers/literals.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.day/time.cal.day.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.day/time.cal.day.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.last/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.md/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.md/time.cal.md.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.md/time.cal.md.members/day.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.md/time.cal.md.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.md/time.cal.md.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.md/time.cal.md.nonmembers/comparisons.pass.cpp FAIL std/utilities/time/time.cal/time.cal.md/time.cal.md.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mdlast/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mdlast/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mdlast/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mdlast/ok.pass.cpp FAIL std/utilities/time/time.cal/time.cal.mdlast/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mdlast/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.members/decrement.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.members/increment.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.members/plus_minus_equal.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.nonmembers/literals.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.month/time.cal.month.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.month/time.cal.month.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwd/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwd/time.cal.mwd.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwd/time.cal.mwd.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwd/time.cal.mwd.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwd/time.cal.mwd.members/weekday_indexed.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwd/time.cal.mwd.nonmembers/comparisons.pass.cpp FAIL std/utilities/time/time.cal/time.cal.mwd/time.cal.mwd.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwdlast/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwdlast/time.cal.mwdlast.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwdlast/time.cal.mwdlast.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwdlast/time.cal.mwdlast.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwdlast/time.cal.mwdlast.members/weekday_last.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.mwdlast/time.cal.mwdlast.nonmembers/comparisons.pass.cpp FAIL std/utilities/time/time.cal/time.cal.mwdlast/time.cal.mwdlast.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/month_day.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/month_day_last.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/month_weekday.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/month_weekday_last.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/year_month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/year_month_day.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/year_month_day_last.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/year_month_weekday.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.operators/year_month_weekday_last.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdidx/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdidx/time.cal.wdidx.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdidx/time.cal.wdidx.members/index.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdidx/time.cal.wdidx.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdidx/time.cal.wdidx.members/weekday.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdidx/time.cal.wdidx.nonmembers/comparisons.pass.cpp FAIL std/utilities/time/time.cal/time.cal.wdidx/time.cal.wdidx.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdlast/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdlast/time.cal.wdlast.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdlast/time.cal.wdlast.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdlast/time.cal.wdlast.members/weekday.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.wdlast/time.cal.wdlast.nonmembers/comparisons.pass.cpp FAIL std/utilities/time/time.cal/time.cal.wdlast/time.cal.wdlast.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/c_encoding.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/ctor.local_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/ctor.sys_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/decrement.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/increment.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/iso_encoding.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/operator[].pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.members/plus_minus_equal.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.nonmembers/literals.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.members/decrement.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.members/increment.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.members/is_leap.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.members/plus_minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.members/plus_minus_equal.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.nonmembers/literals.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.year/time.cal.year.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.year/time.cal.year.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.members/plus_minus_equal_month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.members/plus_minus_equal_year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.members/year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/ctor.local_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/ctor.sys_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/ctor.year_month_day_last.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/day.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/op.local_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/op.sys_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/plus_minus_equal_month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/plus_minus_equal_year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.members/year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/day.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/month_day_last.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/op_local_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/op_sys_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/plus_minus_equal_month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/plus_minus_equal_year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/ctor.local_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/ctor.sys_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/index.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/op.local_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/op.sys_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/plus_minus_equal_month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/plus_minus_equal_year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/weekday.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/weekday_indexed.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/types.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/ctor.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/ok.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/op_local_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/op_sys_days.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/plus_minus_equal_month.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/plus_minus_equal_year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/weekday.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/year.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/comparisons.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/minus.pass.cpp FAIL -std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/plus.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/streaming.pass.cpp FAIL std/utilities/time/time.clock/time.clock.file/consistency.pass.cpp FAIL std/utilities/time/time.clock/time.clock.file/file_time.pass.cpp FAIL std/utilities/time/time.clock/time.clock.file/now.pass.cpp FAIL std/utilities/time/time.clock/time.clock.file/rep_signed.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.system/local_time.types.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.system/sys.time.types.pass.cpp FAIL -std/utilities/time/time.duration/time.duration.literals/literals1.pass.cpp FAIL -std/utilities/time/time.hms/time.12/is_am.pass.cpp FAIL -std/utilities/time/time.hms/time.12/is_pm.pass.cpp FAIL -std/utilities/time/time.hms/time.12/make12.pass.cpp FAIL -std/utilities/time/time.hms/time.12/make24.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/hours.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/is_negative.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/minutes.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/precision.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/precision_type.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/seconds.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/subseconds.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/to_duration.pass.cpp FAIL -std/utilities/time/time.hms/time.hms.members/width.pass.cpp FAIL + +# C++20 P0466R5 "Layout-Compatibility And Pointer-Interconvertibility Traits" +std/language.support/support.limits/support.limits.general/type_traits.version.pass.cpp:1 FAIL # C++20 P0608R3 "Improving variant's Converting Constructor/Assignment" std/utilities/variant/variant.variant/variant.assign/conv.pass.cpp FAIL @@ -492,19 +305,14 @@ std/utilities/variant/variant.variant/variant.assign/T.pass.cpp FAIL std/utilities/variant/variant.variant/variant.ctor/conv.pass.cpp FAIL std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp FAIL +# C++20 P0645R10 " Text Formatting" +std/language.support/support.limits/support.limits.general/format.version.pass.cpp FAIL +std/utilities/format/format.error/format.error.pass.cpp FAIL + # C++20 P0784R7 "More constexpr containers" -std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp:0 FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp:0 FAIL std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp:0 FAIL std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp:0 FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp:0 FAIL -std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp:0 FAIL std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp FAIL -std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp:0 FAIL -std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp:0 FAIL -std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp:0 FAIL # C++20 P0896R4 "" std/language.support/support.limits/support.limits.general/algorithm.version.pass.cpp FAIL @@ -512,12 +320,26 @@ 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++23 P1048R1 "is_scoped_enum" +std/utilities/meta/meta.unary/meta.unary.prop/is_scoped_enum.pass.cpp FAIL + +# C++23 P1679R3 "contains() For basic_string/basic_string_view" +std/strings/basic.string/string.contains/contains.char.pass.cpp FAIL +std/strings/basic.string/string.contains/contains.ptr.pass.cpp FAIL +std/strings/basic.string/string.contains/contains.string_view.pass.cpp FAIL +std/strings/string.view/string.view.template/contains.char.pass.cpp FAIL +std/strings/string.view/string.view.template/contains.ptr.pass.cpp FAIL +std/strings/string.view/string.view.template/contains.string_view.pass.cpp FAIL + # *** MISSING COMPILER FEATURES *** # Nothing here! :-) # *** MISSING LWG ISSUE RESOLUTIONS *** +# LWG-2503 "multiline option should be added to syntax_option_type" +std/re/re.const/re.matchflag/match_multiline.pass.cpp FAIL + # LWG-2532 "Satisfying a promise at thread exit" (Open) # WCFB02 implements the proposed resolution for this issue std/thread/futures/futures.promise/set_exception_at_thread_exit.pass.cpp FAIL @@ -541,6 +363,10 @@ std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp:0 FAIL std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp:0 FAIL std/utilities/utility/pairs/pairs.pair/assign_rv_pair_U_V.pass.cpp:0 FAIL +# VSO-1271673 "static analyzer doesn't know about short-circuiting" +std/algorithms/alg.sorting/alg.sort/partial.sort/partial_sort.pass.cpp:0 FAIL +std/algorithms/alg.sorting/alg.sort/partial.sort/partial_sort_comp.pass.cpp:0 FAIL + # *** CLANG COMPILER BUGS *** # LLVM-33230 "Clang on Windows should define __STDCPP_THREADS__ to be 1" @@ -549,6 +375,9 @@ std/thread/macro.pass.cpp:1 FAIL # LLVM-46207 Clang's tgmath.h interferes with the UCRT's tgmath.h std/depr/depr.c.headers/tgmath_h.pass.cpp:1 FAIL +# LLVM-48606 "Clang rejects creation of struct with mutable member during constant evaluation" +std/language.support/support.limits/support.limits.general/vector.version.pass.cpp:1 FAIL + # *** CLANG ISSUES, NOT YET ANALYZED *** # Clang doesn't enable sized deallocation by default. Should we add -fsized-deallocation or do something else? @@ -581,7 +410,6 @@ std/containers/sequences/array/array.data/data.pass.cpp FAIL std/containers/sequences/array/iterators.pass.cpp FAIL # GH-1006 : debug checks for predicates are observable -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 std/algorithms/alg.sorting/alg.min.max/minmax_init_list_comp.pass.cpp FAIL @@ -646,6 +474,12 @@ std/utilities/meta/meta.trans/meta.trans.other/aligned_storage.pass.cpp FAIL std/depr/depr.c.headers/math_h.pass.cpp FAIL std/numerics/c.math/cmath.pass.cpp FAIL +# GH-1596: : unqualified calls to _Adl_verify_range incorrectly cause instantiation +std/algorithms/robust_against_adl.pass.cpp FAIL + +# GH-1595: : bit_ceil(T(-1)) should not be a constant expression +std/numerics/bit/bit.pow.two/bit_ceil.fail.cpp:0 FAIL + # *** CRT BUGS *** # We're permanently missing aligned_alloc(). @@ -662,15 +496,6 @@ std/language.support/support.limits/c.limits/cfloat.pass.cpp:0 FAIL # *** 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 @@ -684,15 +509,6 @@ std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.asgn/move.pass.cpp # Test bug after LWG-3257 "Missing feature testing macro update from P0858" was accepted. std/language.support/support.limits/support.limits.general/string.version.pass.cpp FAIL -# libc++ expects an old value for `__cpp_lib_array_constexpr` (`201603L`). We've implemented P0858R0, P1023R0, and P1032R1, increasing the value to `201811L`. -std/language.support/support.limits/support.limits.general/array.version.pass.cpp FAIL - -# libc++ expects the old macro `__cpp_lib_constexpr_misc`. After P1902R1, it should expect `__cpp_lib_constexpr_tuple` to have the value `201811L`. -std/language.support/support.limits/support.limits.general/tuple.version.pass.cpp FAIL - -# libc++ expects the old macro `__cpp_lib_constexpr_misc`. After P1902R1, it should expect `__cpp_lib_constexpr_utility` to have the value `201811L`. -std/language.support/support.limits/support.limits.general/utility.version.pass.cpp FAIL - # Not yet analyzed, likely bogus tests. Appears to be timing assumptions. std/thread/futures/futures.async/async.pass.cpp SKIPPED std/thread/futures/futures.shared_future/get.pass.cpp SKIPPED @@ -809,43 +625,71 @@ std/numerics/rand/rand.dis/rand.dist.samp/rand.dist.samp.plinear/eval.pass.cpp F # They shouldn't behave differently. Both of them should probably return NaN. std/numerics/c.math/c.math.lerp/c.math.lerp.pass.cpp FAIL +# --month{14} should be 1, not 13 as the test expects +std/utilities/time/time.cal/time.cal.month/time.cal.month.members/decrement.pass.cpp FAIL + +# test is broken due to month_weekday not being default constructible +std/utilities/time/time.cal/time.cal.mwd/time.cal.mwd.members/month.pass.cpp FAIL + +# conversion from '__int64' to 'long', possible loss of data +std/utilities/time/time.hms/time.hms.members/seconds.pass.cpp:0 FAIL +std/utilities/time/time.hms/time.hms.members/subseconds.pass.cpp:0 FAIL + +# Code: `for (int i = 1000; i < 20; ++i)` +# warning C6294: Ill-defined for-loop: initial condition does not satisfy test. Loop body not executed. +std/utilities/time/time.cal/time.cal.month/time.cal.month.nonmembers/comparisons.pass.cpp:0 FAIL +std/utilities/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/comparisons.pass.cpp:0 FAIL +std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/comparisons.pass.cpp:0 FAIL +std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/comparisons.pass.cpp:0 FAIL +std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/comparisons.pass.cpp:0 FAIL +std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/comparisons.pass.cpp:0 FAIL + +# Tests are manually declaring printf, which appears to be unused. +# warning C28301: No annotations for first declaration of 'printf'. +std/utilities/time/time.cal/time.cal.weekday/time.cal.weekday.nonmembers/minus.pass.cpp:0 FAIL +std/utilities/time/time.cal/time.cal.year/time.cal.year.nonmembers/minus.pass.cpp:0 FAIL + +# Bogus test passes a class type as the second argument to std::advance +std/iterators/iterator.primitives/iterator.operations/robust_against_adl.pass.cpp FAIL + +# Non-Standard test should be moved from libcxx/test/std to libcxx/test/libcxx +std/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_layout.pass.cpp FAIL + +# Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano +std/input.output/filesystems/fs.filesystem.synopsis/file_time_type_resolution.compile.pass.cpp FAIL + +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +std/iterators/stream.iterators/istream.iterator/istream.iterator.ops/equal.pass.cpp FAIL + +# Uses-allocator class constructor wants non-const allocator ref and mismatched piecewise_construct's value category +std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp FAIL + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. std/numerics/rand/rand.dis/rand.dist.bern/rand.dist.bern.bin/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.bern/rand.dist.bern.bin/eval.pass.cpp FAIL +std/numerics/rand/rand.dis/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.bern/rand.dist.bern.geo/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.bern/rand.dist.bern.geo/eval.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.bern/rand.dist.bern.negbin/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.bern/rand.dist.bern.negbin/eval.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.cauchy/max.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.cauchy/min.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.chisq/max.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.chisq/min.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.f/max.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.lognormal/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.lognormal/eval.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.lognormal/max.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.lognormal/min.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.normal/max.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.normal/min.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.t/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.t/eval.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.t/max.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.norm/rand.dist.norm.t/min.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.exp/max.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.extreme/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.extreme/eval.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.extreme/max.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.extreme/min.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.gamma/eq.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.gamma/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.gamma/eval.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.gamma/max.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.gamma/min.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.poisson/eval_param.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.poisson/eval.pass.cpp FAIL -std/numerics/rand/rand.dis/rand.dist.pois/rand.dist.pois.weibull/max.pass.cpp FAIL std/numerics/rand/rand.dis/rand.dist.uni/rand.dist.uni.real/param_ctor.pass.cpp FAIL # Not yet analyzed, likely STL bugs. Various assertions. @@ -1007,6 +851,31 @@ std/containers/sequences/deque/deque.modifiers/insert_iter_iter.pass.cpp SKIPPED # Not yet analyzed. Failing after https://reviews.llvm.org/D75622. std/re/re.const/re.matchflag/match_prev_avail.pass.cpp FAIL +# Not yet analyzed. Many diagnostics. +std/input.output/filesystems/class.path/path.member/path.charconv.pass.cpp FAIL + +# Not yet analyzed. Probably ADL shenanigans. +std/thread/thread.threads/thread.thread.class/thread.thread.constr/robust_against_adl.pass.cpp FAIL +std/utilities/function.objects/func.wrap/func.wrap.func/robust_against_adl.pass.cpp FAIL +std/utilities/function.objects/refwrap/refwrap.invoke/robust_against_adl.pass.cpp FAIL +std/utilities/variant/variant.visit/robust_against_adl.pass.cpp FAIL + +# Not yet analyzed. Probably MSVC bug. +std/utilities/function.objects/func.invoke/invoke_constexpr.pass.cpp:0 FAIL + +# Not yet analyzed. Probably name mangling bug. +std/utilities/function.objects/func.wrap/func.wrap.func/noncopyable_return_type.pass.cpp FAIL + +# Not yet analyzed. Failing for "[a[.ch.]z]". +std/re/re.alg/re.alg.match/awk.locale.pass.cpp FAIL +std/re/re.alg/re.alg.match/basic.locale.pass.cpp FAIL +std/re/re.alg/re.alg.match/ecma.locale.pass.cpp FAIL +std/re/re.alg/re.alg.match/extended.locale.pass.cpp FAIL +std/re/re.alg/re.alg.search/awk.locale.pass.cpp FAIL +std/re/re.alg/re.alg.search/basic.locale.pass.cpp FAIL +std/re/re.alg/re.alg.search/ecma.locale.pass.cpp FAIL +std/re/re.alg/re.alg.search/extended.locale.pass.cpp FAIL + # *** XFAILs WHICH PASS *** # Not yet implemented in libcxx and marked as XFAIL diff --git a/tests/libcxx/lit.site.cfg.in b/tests/libcxx/lit.site.cfg.in index fb530312e8a..0557736b467 100644 --- a/tests/libcxx/lit.site.cfg.in +++ b/tests/libcxx/lit.site.cfg.in @@ -30,6 +30,7 @@ lit_config.test_subdirs[config.name] = ['@LIBCXX_SOURCE_DIR@/test/std'] lit_config.cxx_runtime = '@CMAKE_RUNTIME_OUTPUT_DIRECTORY@' lit_config.target_arch = '@VCLIBS_TARGET_ARCHITECTURE@' +lit_config.build_only = '@TESTS_BUILD_ONLY@'.lower() in ['1', 'true', 'on'] # Add parameters and features to the config stl.test.config.configure( diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 61b46acc242..a8fe00a48f7 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -57,21 +57,12 @@ language.support\support.limits\support.limits.general\locale.version.pass.cpp language.support\support.limits\support.limits.general\ostream.version.pass.cpp language.support\support.limits\support.limits.general\string_view.version.pass.cpp -# libc++ doesn't yet implement P1956R1, so it expects the old names `ispow2`, `ceil2`, `floor2`, and `log2p1` -numerics\bit\bit.pow.two\ceil2.pass.cpp -numerics\bit\bit.pow.two\floor2.pass.cpp -numerics\bit\bit.pow.two\ispow2.pass.cpp -numerics\bit\bit.pow.two\log2p1.pass.cpp - # test emits warning C4310: cast truncates constant value numerics\bit\bitops.rot\rotl.pass.cpp # libc++ doesn't yet implement P1754R1 or P1964R2, so it expects an old value for `__cpp_lib_concepts` language.support\support.limits\support.limits.general\concepts.version.pass.cpp -# libc++ doesn't yet implement P1001R2, so it expects an old value for `__cpp_lib_execution` -language.support\support.limits\support.limits.general\execution.version.pass.cpp - # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" @@ -197,6 +188,8 @@ language.support\support.limits\support.limits.general\type_traits.version.pass. language.support\support.limits\support.limits.general\version.version.pass.cpp # Contest does not understand .sh tests, which must be run specially +input.output\iostream.objects\narrow.stream.objects\cin.sh.cpp +input.output\iostream.objects\wide.stream.objects\wcin.sh.cpp namespace\addressable_functions.sh.cpp thread\thread.condition\thread.condition.condvarany\wait_terminates.sh.cpp @@ -280,211 +273,31 @@ utilities\memory\default.allocator\allocator_void.deprecated_in_cxx17.verify.cpp utilities\memory\default.allocator\allocator.members\allocate.constexpr.size.verify.cpp utilities\memory\default.allocator\allocator.members\allocate.verify.cpp -# GH-1382: Our machinery doesn't understand compile-only `.compile.pass.cpp` tests -strings\string.view\string.view.io\stream_insert_decl_present.compile.pass.cpp - # *** MISSING STL FEATURES *** # C++20 P0355R7 " Calendars And Time Zones" -utilities\time\days.pass.cpp -utilities\time\months.pass.cpp -utilities\time\weeks.pass.cpp -utilities\time\years.pass.cpp -utilities\time\time.cal\time.cal.day\types.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.members\decrement.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.members\increment.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.members\ok.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.members\plus_minus_equal.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.nonmembers\literals.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.day\time.cal.day.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.day\time.cal.day.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.last\types.pass.cpp -utilities\time\time.cal\time.cal.md\types.pass.cpp -utilities\time\time.cal\time.cal.md\time.cal.md.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.md\time.cal.md.members\day.pass.cpp -utilities\time\time.cal\time.cal.md\time.cal.md.members\month.pass.cpp -utilities\time\time.cal\time.cal.md\time.cal.md.members\ok.pass.cpp -utilities\time\time.cal\time.cal.md\time.cal.md.nonmembers\comparisons.pass.cpp utilities\time\time.cal\time.cal.md\time.cal.md.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.mdlast\comparisons.pass.cpp -utilities\time\time.cal\time.cal.mdlast\ctor.pass.cpp -utilities\time\time.cal\time.cal.mdlast\month.pass.cpp -utilities\time\time.cal\time.cal.mdlast\ok.pass.cpp utilities\time\time.cal\time.cal.mdlast\streaming.pass.cpp -utilities\time\time.cal\time.cal.mdlast\types.pass.cpp -utilities\time\time.cal\time.cal.month\types.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.members\decrement.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.members\increment.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.members\ok.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.members\plus_minus_equal.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.nonmembers\literals.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.month\time.cal.month.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.month\time.cal.month.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.mwd\types.pass.cpp -utilities\time\time.cal\time.cal.mwd\time.cal.mwd.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.mwd\time.cal.mwd.members\month.pass.cpp -utilities\time\time.cal\time.cal.mwd\time.cal.mwd.members\ok.pass.cpp -utilities\time\time.cal\time.cal.mwd\time.cal.mwd.members\weekday_indexed.pass.cpp -utilities\time\time.cal\time.cal.mwd\time.cal.mwd.nonmembers\comparisons.pass.cpp utilities\time\time.cal\time.cal.mwd\time.cal.mwd.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.mwdlast\types.pass.cpp -utilities\time\time.cal\time.cal.mwdlast\time.cal.mwdlast.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.mwdlast\time.cal.mwdlast.members\month.pass.cpp -utilities\time\time.cal\time.cal.mwdlast\time.cal.mwdlast.members\ok.pass.cpp -utilities\time\time.cal\time.cal.mwdlast\time.cal.mwdlast.members\weekday_last.pass.cpp -utilities\time\time.cal\time.cal.mwdlast\time.cal.mwdlast.nonmembers\comparisons.pass.cpp utilities\time\time.cal\time.cal.mwdlast\time.cal.mwdlast.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.operators\month_day.pass.cpp -utilities\time\time.cal\time.cal.operators\month_day_last.pass.cpp -utilities\time\time.cal\time.cal.operators\month_weekday.pass.cpp -utilities\time\time.cal\time.cal.operators\month_weekday_last.pass.cpp -utilities\time\time.cal\time.cal.operators\year_month.pass.cpp -utilities\time\time.cal\time.cal.operators\year_month_day.pass.cpp -utilities\time\time.cal\time.cal.operators\year_month_day_last.pass.cpp -utilities\time\time.cal\time.cal.operators\year_month_weekday.pass.cpp -utilities\time\time.cal\time.cal.operators\year_month_weekday_last.pass.cpp -utilities\time\time.cal\time.cal.wdidx\types.pass.cpp -utilities\time\time.cal\time.cal.wdidx\time.cal.wdidx.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.wdidx\time.cal.wdidx.members\index.pass.cpp -utilities\time\time.cal\time.cal.wdidx\time.cal.wdidx.members\ok.pass.cpp -utilities\time\time.cal\time.cal.wdidx\time.cal.wdidx.members\weekday.pass.cpp -utilities\time\time.cal\time.cal.wdidx\time.cal.wdidx.nonmembers\comparisons.pass.cpp utilities\time\time.cal\time.cal.wdidx\time.cal.wdidx.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.wdlast\types.pass.cpp -utilities\time\time.cal\time.cal.wdlast\time.cal.wdlast.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.wdlast\time.cal.wdlast.members\ok.pass.cpp -utilities\time\time.cal\time.cal.wdlast\time.cal.wdlast.members\weekday.pass.cpp -utilities\time\time.cal\time.cal.wdlast\time.cal.wdlast.nonmembers\comparisons.pass.cpp utilities\time\time.cal\time.cal.wdlast\time.cal.wdlast.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.weekday\types.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\c_encoding.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\ctor.local_days.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\ctor.sys_days.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\decrement.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\increment.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\iso_encoding.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\ok.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\operator[].pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.members\plus_minus_equal.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.nonmembers\literals.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.weekday\time.cal.weekday.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.weekday\time.cal.weekday.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.year\types.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.members\decrement.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.members\increment.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.members\is_leap.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.members\ok.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.members\plus_minus.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.members\plus_minus_equal.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.nonmembers\literals.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.year\time.cal.year.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.year\time.cal.year.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.ym\types.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.members\month.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.members\ok.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.members\plus_minus_equal_month.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.members\plus_minus_equal_year.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.members\year.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.ym\time.cal.ym.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.ym\time.cal.ym.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.ymd\types.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\ctor.local_days.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\ctor.sys_days.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\ctor.year_month_day_last.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\day.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\month.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\ok.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\op.local_days.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\op.sys_days.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\plus_minus_equal_month.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\plus_minus_equal_year.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.members\year.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.ymd\time.cal.ymd.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.ymd\time.cal.ymd.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\day.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\month.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\month_day_last.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\ok.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\op_local_days.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\op_sys_days.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\plus_minus_equal_month.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\plus_minus_equal_year.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.members\year.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.ymwd\types.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\ctor.local_days.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\ctor.sys_days.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\index.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\month.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\ok.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\op.local_days.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\op.sys_days.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\plus_minus_equal_month.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\plus_minus_equal_year.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\weekday.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\weekday_indexed.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.members\year.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.nonmembers\streaming.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\types.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\ctor.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\month.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\ok.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\op_local_days.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\op_sys_days.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\plus_minus_equal_month.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\plus_minus_equal_year.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\weekday.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.members\year.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.nonmembers\comparisons.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.nonmembers\minus.pass.cpp -utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.nonmembers\plus.pass.cpp utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.nonmembers\streaming.pass.cpp utilities\time\time.clock\time.clock.file\consistency.pass.cpp utilities\time\time.clock\time.clock.file\file_time.pass.cpp utilities\time\time.clock\time.clock.file\now.pass.cpp utilities\time\time.clock\time.clock.file\rep_signed.pass.cpp -utilities\time\time.clock\time.clock.system\local_time.types.pass.cpp -utilities\time\time.clock\time.clock.system\sys.time.types.pass.cpp -utilities\time\time.duration\time.duration.literals\literals1.pass.cpp -utilities\time\time.hms\time.12\is_am.pass.cpp -utilities\time\time.hms\time.12\is_pm.pass.cpp -utilities\time\time.hms\time.12\make12.pass.cpp -utilities\time\time.hms\time.12\make24.pass.cpp -utilities\time\time.hms\time.hms.members\hours.pass.cpp -utilities\time\time.hms\time.hms.members\is_negative.pass.cpp -utilities\time\time.hms\time.hms.members\minutes.pass.cpp -utilities\time\time.hms\time.hms.members\precision.pass.cpp -utilities\time\time.hms\time.hms.members\precision_type.pass.cpp -utilities\time\time.hms\time.hms.members\seconds.pass.cpp -utilities\time\time.hms\time.hms.members\subseconds.pass.cpp -utilities\time\time.hms\time.hms.members\to_duration.pass.cpp -utilities\time\time.hms\time.hms.members\width.pass.cpp + +# C++20 P0466R5 "Layout-Compatibility And Pointer-Interconvertibility Traits" +language.support\support.limits\support.limits.general\type_traits.version.pass.cpp # C++20 P0608R3 "Improving variant's Converting Constructor/Assignment" utilities\variant\variant.variant\variant.assign\conv.pass.cpp @@ -492,19 +305,14 @@ utilities\variant\variant.variant\variant.assign\T.pass.cpp utilities\variant\variant.variant\variant.ctor\conv.pass.cpp utilities\variant\variant.variant\variant.ctor\T.pass.cpp +# C++20 P0645R10 " Text Formatting" +language.support\support.limits\support.limits.general\format.version.pass.cpp +utilities\format\format.error\format.error.pass.cpp + # C++20 P0784R7 "More constexpr containers" -utilities\memory\allocator.traits\allocator.traits.members\allocate.pass.cpp -utilities\memory\allocator.traits\allocator.traits.members\allocate_hint.pass.cpp utilities\memory\allocator.traits\allocator.traits.members\construct.pass.cpp -utilities\memory\allocator.traits\allocator.traits.members\deallocate.pass.cpp utilities\memory\allocator.traits\allocator.traits.members\destroy.pass.cpp -utilities\memory\allocator.traits\allocator.traits.members\max_size.pass.cpp -utilities\memory\allocator.traits\allocator.traits.members\select_on_container_copy_construction.pass.cpp -utilities\memory\default.allocator\allocator.globals\eq.pass.cpp utilities\memory\specialized.algorithms\specialized.construct\construct_at.pass.cpp -utilities\memory\specialized.algorithms\specialized.destroy\destroy.pass.cpp -utilities\memory\specialized.algorithms\specialized.destroy\destroy_at.pass.cpp -utilities\memory\specialized.algorithms\specialized.destroy\destroy_n.pass.cpp # C++20 P0896R4 "" language.support\support.limits\support.limits.general\algorithm.version.pass.cpp @@ -512,12 +320,26 @@ 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++23 P1048R1 "is_scoped_enum" +utilities\meta\meta.unary\meta.unary.prop\is_scoped_enum.pass.cpp + +# C++23 P1679R3 "contains() For basic_string/basic_string_view" +strings\basic.string\string.contains\contains.char.pass.cpp +strings\basic.string\string.contains\contains.ptr.pass.cpp +strings\basic.string\string.contains\contains.string_view.pass.cpp +strings\string.view\string.view.template\contains.char.pass.cpp +strings\string.view\string.view.template\contains.ptr.pass.cpp +strings\string.view\string.view.template\contains.string_view.pass.cpp + # *** MISSING COMPILER FEATURES *** # Nothing here! :-) # *** MISSING LWG ISSUE RESOLUTIONS *** +# LWG-2503 "multiline option should be added to syntax_option_type" +re\re.const\re.matchflag\match_multiline.pass.cpp + # LWG-2532 "Satisfying a promise at thread exit" (Open) # WCFB02 implements the proposed resolution for this issue thread\futures\futures.promise\set_exception_at_thread_exit.pass.cpp @@ -541,6 +363,10 @@ utilities\utility\pairs\pairs.pair\assign_pair.pass.cpp utilities\utility\pairs\pairs.pair\assign_rv_pair.pass.cpp utilities\utility\pairs\pairs.pair\assign_rv_pair_U_V.pass.cpp +# VSO-1271673 "static analyzer doesn't know about short-circuiting" +algorithms\alg.sorting\alg.sort\partial.sort\partial_sort.pass.cpp +algorithms\alg.sorting\alg.sort\partial.sort\partial_sort_comp.pass.cpp + # *** CLANG COMPILER BUGS *** # LLVM-33230 "Clang on Windows should define __STDCPP_THREADS__ to be 1" @@ -549,6 +375,9 @@ thread\macro.pass.cpp # LLVM-46207 Clang's tgmath.h interferes with the UCRT's tgmath.h depr\depr.c.headers\tgmath_h.pass.cpp +# LLVM-48606 "Clang rejects creation of struct with mutable member during constant evaluation" +language.support\support.limits\support.limits.general\vector.version.pass.cpp + # *** CLANG ISSUES, NOT YET ANALYZED *** # Clang doesn't enable sized deallocation by default. Should we add -fsized-deallocation or do something else? @@ -581,7 +410,6 @@ containers\sequences\array\array.data\data.pass.cpp containers\sequences\array\iterators.pass.cpp # GH-1006 : debug checks for predicates are observable -algorithms\alg.sorting\alg.heap.operations\make.heap\make_heap_comp.pass.cpp algorithms\alg.sorting\alg.merge\inplace_merge_comp.pass.cpp algorithms\alg.sorting\alg.min.max\minmax_init_list_comp.pass.cpp @@ -646,6 +474,12 @@ utilities\meta\meta.trans\meta.trans.other\aligned_storage.pass.cpp depr\depr.c.headers\math_h.pass.cpp numerics\c.math\cmath.pass.cpp +# GH-1596: : unqualified calls to _Adl_verify_range incorrectly cause instantiation +algorithms\robust_against_adl.pass.cpp + +# GH-1595: : bit_ceil(T(-1)) should not be a constant expression +numerics\bit\bit.pow.two\bit_ceil.fail.cpp + # *** CRT BUGS *** # We're permanently missing aligned_alloc(). @@ -662,15 +496,6 @@ language.support\support.limits\c.limits\cfloat.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 @@ -684,15 +509,6 @@ utilities\smartptr\unique.ptr\unique.ptr.class\unique.ptr.asgn\move.pass.cpp # Test bug after LWG-3257 "Missing feature testing macro update from P0858" was accepted. language.support\support.limits\support.limits.general\string.version.pass.cpp -# libc++ expects an old value for `__cpp_lib_array_constexpr` (`201603L`). We've implemented P0858R0, P1023R0, and P1032R1, increasing the value to `201811L`. -language.support\support.limits\support.limits.general\array.version.pass.cpp - -# libc++ expects the old macro `__cpp_lib_constexpr_misc`. After P1902R1, it should expect `__cpp_lib_constexpr_tuple` to have the value `201811L`. -language.support\support.limits\support.limits.general\tuple.version.pass.cpp - -# libc++ expects the old macro `__cpp_lib_constexpr_misc`. After P1902R1, it should expect `__cpp_lib_constexpr_utility` to have the value `201811L`. -language.support\support.limits\support.limits.general\utility.version.pass.cpp - # Not yet analyzed, likely bogus tests. Appears to be timing assumptions. thread\futures\futures.async\async.pass.cpp thread\futures\futures.shared_future\get.pass.cpp @@ -809,43 +625,71 @@ numerics\rand\rand.dis\rand.dist.samp\rand.dist.samp.plinear\eval.pass.cpp # They shouldn't behave differently. Both of them should probably return NaN. numerics\c.math\c.math.lerp\c.math.lerp.pass.cpp +# --month{14} should be 1, not 13 as the test expects +utilities\time\time.cal\time.cal.month\time.cal.month.members\decrement.pass.cpp + +# test is broken due to month_weekday not being default constructible +utilities\time\time.cal\time.cal.mwd\time.cal.mwd.members\month.pass.cpp + +# conversion from '__int64' to 'long', possible loss of data +utilities\time\time.hms\time.hms.members\seconds.pass.cpp +utilities\time\time.hms\time.hms.members\subseconds.pass.cpp + +# Code: `for (int i = 1000; i < 20; ++i)` +# warning C6294: Ill-defined for-loop: initial condition does not satisfy test. Loop body not executed. +utilities\time\time.cal\time.cal.month\time.cal.month.nonmembers\comparisons.pass.cpp +utilities\time\time.cal\time.cal.ym\time.cal.ym.nonmembers\comparisons.pass.cpp +utilities\time\time.cal\time.cal.ymd\time.cal.ymd.nonmembers\comparisons.pass.cpp +utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.nonmembers\comparisons.pass.cpp +utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.nonmembers\comparisons.pass.cpp +utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.nonmembers\comparisons.pass.cpp + +# Tests are manually declaring printf, which appears to be unused. +# warning C28301: No annotations for first declaration of 'printf'. +utilities\time\time.cal\time.cal.weekday\time.cal.weekday.nonmembers\minus.pass.cpp +utilities\time\time.cal\time.cal.year\time.cal.year.nonmembers\minus.pass.cpp + +# Bogus test passes a class type as the second argument to std::advance +iterators\iterator.primitives\iterator.operations\robust_against_adl.pass.cpp + +# Non-Standard test should be moved from libcxx/test/std to libcxx/test/libcxx +utilities\memory\util.smartptr\util.smartptr.shared\libcxx.control_block_layout.pass.cpp + +# Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano +input.output\filesystems\fs.filesystem.synopsis\file_time_type_resolution.compile.pass.cpp + +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +iterators\stream.iterators\istream.iterator\istream.iterator.ops\equal.pass.cpp + +# Uses-allocator class constructor wants non-const allocator ref and mismatched piecewise_construct's value category +utilities\allocator.adaptor\allocator.adaptor.members\construct.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_const_lvalue_pair.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_piecewise.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_rvalue.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_values.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_type.pass.cpp + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. numerics\rand\rand.dis\rand.dist.bern\rand.dist.bern.bin\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.bern\rand.dist.bern.bin\eval.pass.cpp +numerics\rand\rand.dis\rand.dist.bern\rand.dist.bern.bin\eval.PR44847.pass.cpp numerics\rand\rand.dis\rand.dist.bern\rand.dist.bern.geo\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.bern\rand.dist.bern.geo\eval.pass.cpp numerics\rand\rand.dis\rand.dist.bern\rand.dist.bern.negbin\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.bern\rand.dist.bern.negbin\eval.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.cauchy\max.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.cauchy\min.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.chisq\max.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.chisq\min.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.f\max.pass.cpp numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.lognormal\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.lognormal\eval.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.lognormal\max.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.lognormal\min.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.normal\max.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.normal\min.pass.cpp numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.t\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.t\eval.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.t\max.pass.cpp -numerics\rand\rand.dis\rand.dist.norm\rand.dist.norm.t\min.pass.cpp -numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.exp\max.pass.cpp numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.extreme\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.extreme\eval.pass.cpp -numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.extreme\max.pass.cpp -numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.extreme\min.pass.cpp -numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.gamma\eq.pass.cpp numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.gamma\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.gamma\eval.pass.cpp -numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.gamma\max.pass.cpp -numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.gamma\min.pass.cpp numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.poisson\eval_param.pass.cpp numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.poisson\eval.pass.cpp -numerics\rand\rand.dis\rand.dist.pois\rand.dist.pois.weibull\max.pass.cpp numerics\rand\rand.dis\rand.dist.uni\rand.dist.uni.real\param_ctor.pass.cpp # Not yet analyzed, likely STL bugs. Various assertions. @@ -1006,3 +850,35 @@ containers\sequences\deque\deque.modifiers\insert_iter_iter.pass.cpp # Not yet analyzed. Failing after https://reviews.llvm.org/D75622. re\re.const\re.matchflag\match_prev_avail.pass.cpp + +# Not yet analyzed. Many diagnostics. +input.output\filesystems\class.path\path.member\path.charconv.pass.cpp + +# Not yet analyzed. Probably ADL shenanigans. +thread\thread.threads\thread.thread.class\thread.thread.constr\robust_against_adl.pass.cpp +utilities\function.objects\func.wrap\func.wrap.func\robust_against_adl.pass.cpp +utilities\function.objects\refwrap\refwrap.invoke\robust_against_adl.pass.cpp +utilities\variant\variant.visit\robust_against_adl.pass.cpp + +# Not yet analyzed. Probably MSVC bug. +utilities\function.objects\func.invoke\invoke_constexpr.pass.cpp + +# Not yet analyzed. Probably name mangling bug. +utilities\function.objects\func.wrap\func.wrap.func\noncopyable_return_type.pass.cpp + +# Not yet analyzed. Failing for "[a[.ch.]z]". +re\re.alg\re.alg.match\awk.locale.pass.cpp +re\re.alg\re.alg.match\basic.locale.pass.cpp +re\re.alg\re.alg.match\ecma.locale.pass.cpp +re\re.alg\re.alg.match\extended.locale.pass.cpp +re\re.alg\re.alg.search\awk.locale.pass.cpp +re\re.alg\re.alg.search\basic.locale.pass.cpp +re\re.alg\re.alg.search\ecma.locale.pass.cpp +re\re.alg\re.alg.search\extended.locale.pass.cpp + + +# *** SKIPPED FOR MSVC-INTERNAL CONTEST ONLY *** +# Our machinery doesn't understand compile-only `.compile.pass.cpp` tests. +# (Implemented for GitHub, see GH-1382.) +concepts\concept.constructible\constructible_from.compile.pass.cpp +strings\string.view\string.view.io\stream_insert_decl_present.compile.pass.cpp diff --git a/tests/std/include/test_atomic_wait.hpp b/tests/std/include/test_atomic_wait.hpp index a3d9b4471c2..cd061882d2d 100644 --- a/tests/std/include/test_atomic_wait.hpp +++ b/tests/std/include/test_atomic_wait.hpp @@ -43,7 +43,7 @@ void test_atomic_wait_func_impl(UnderlyingType& old_value, const UnderlyingType // timing assumption that the main thread evaluates the `wait(old_value)` before this timeout expires std::this_thread::sleep_for(waiting_duration); add_seq('6'); -#endif // CAN_FAIL_ON_TIMING_ASSUMPTION +#endif }); a.wait(old_value); @@ -187,6 +187,8 @@ struct big_char_like { friend bool operator==(big_char_like, big_char_like) = delete; }; +#pragma warning(push) +#pragma warning(disable : 4324) // structure was padded due to alignment specifier template struct with_padding_bits { alignas(size) char value; @@ -197,6 +199,7 @@ struct with_padding_bits { friend bool operator==(with_padding_bits, with_padding_bits) = delete; }; +#pragma warning(pop) inline void test_atomic_wait() { // wait for all the threads to be waiting; if this value is too small the test might be ineffective but should not @@ -249,7 +252,9 @@ inline void test_atomic_wait() { test_pad_bits>(waiting_duration); test_pad_bits>(waiting_duration); test_pad_bits>(waiting_duration); +#ifndef _M_ARM test_pad_bits>(waiting_duration); test_pad_bits>(waiting_duration); +#endif // ^^^ !ARM ^^^ #endif // __clang__, TRANSITION, LLVM-46685 } diff --git a/tests/std/include/test_death.hpp b/tests/std/include/test_death.hpp index 943b0f28a02..296a0f69012 100644 --- a/tests/std/include/test_death.hpp +++ b/tests/std/include/test_death.hpp @@ -79,9 +79,16 @@ namespace std_testing { // buffer was not big enough const size_t str_max_size = result.max_size(); const size_t result_max_size = str_max_size - str_max_size / 2; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif // __clang__ if (result_size >= result_max_size) { api_unexpected("GetModuleFileNameW"); } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ result.resize(result_size + result_size / 2); } else if (result_size == 0) { diff --git a/tests/std/lit.site.cfg.in b/tests/std/lit.site.cfg.in index 6455f31677a..653f76d16a3 100644 --- a/tests/std/lit.site.cfg.in +++ b/tests/std/lit.site.cfg.in @@ -30,6 +30,7 @@ lit_config.test_subdirs[config.name] = ['@CMAKE_CURRENT_SOURCE_DIR@/tests'] lit_config.cxx_headers = '@STL_TESTED_HEADERS_DIR@' lit_config.cxx_runtime = '@CMAKE_RUNTIME_OUTPUT_DIRECTORY@' lit_config.target_arch = '@VCLIBS_TARGET_ARCHITECTURE@' +lit_config.build_only = '@TESTS_BUILD_ONLY@'.lower() in ['1', 'true', 'on'] # Add parameters and features to the config stl.test.config.configure( diff --git a/tests/std/test.lst b/tests/std/test.lst index 014d2c798d4..e5866c93af1 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -99,7 +99,7 @@ tests\Dev11_0000000_user_defined_literals tests\Dev11_0019127_singular_iterators tests\Dev11_0091392_string_erase_resize_perf tests\Dev11_0133625_locale0_implib_cpp -tests\Dev11_0135139_vector_bool_equality_perf +tests\Dev11_0135139_vector_bool_comparisons tests\Dev11_0235721_async_and_packaged_task tests\Dev11_0253803_debug_pointer tests\Dev11_0272959_make_signed @@ -172,7 +172,9 @@ tests\GH_001103_countl_zero_correctness tests\GH_001105_custom_streambuf_throws tests\GH_001123_random_cast_out_of_range tests\GH_001411_core_headers +tests\GH_001530_binomial_accuracy tests\GH_001541_case_sensitive_boolalpha +tests\GH_001638_dllexport_derived_classes tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\P0019R8_atomic_ref @@ -207,6 +209,7 @@ tests\P0024R2_parallel_algorithms_transform_inclusive_scan tests\P0024R2_parallel_algorithms_transform_reduce tests\P0035R4_over_aligned_allocation tests\P0040R3_extending_memory_management_tools +tests\P0053R7_cpp_synchronized_buffered_ostream tests\P0067R5_charconv tests\P0083R3_splicing_maps_and_sets tests\P0088R3_variant @@ -226,7 +229,12 @@ tests\P0220R1_searchers tests\P0220R1_string_view tests\P0325R4_to_array tests\P0339R6_polymorphic_allocator +tests\P0355R7_calendars_and_time_zones_clocks +tests\P0355R7_calendars_and_time_zones_dates +tests\P0355R7_calendars_and_time_zones_dates_literals +tests\P0355R7_calendars_and_time_zones_hms tests\P0355R7_calendars_and_time_zones_io +tests\P0355R7_calendars_and_time_zones_time_point_and_durations tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper tests\P0408R7_efficient_access_to_stringbuf_buffer @@ -234,6 +242,8 @@ tests\P0414R2_shared_ptr_for_arrays tests\P0415R1_constexpr_complex tests\P0426R1_constexpr_char_traits tests\P0433R2_deduction_guides +tests\P0466R5_layout_compatibility_and_pointer_interconvertibility_traits +tests\P0475R1_P0591R4_uses_allocator_construction tests\P0476R2_bit_cast tests\P0487R1_fixing_operator_shl_basic_istream_char_pointer tests\P0513R0_poisoning_the_hash @@ -243,6 +253,7 @@ tests\P0556R3_bit_integral_power_of_two_operations tests\P0586R2_integer_comparison tests\P0595R2_is_constant_evaluated tests\P0607R0_inline_variables +tests\P0608R3_improved_variant_converting_constructor tests\P0616R0_using_move_in_numeric tests\P0631R8_numbers_math_constants tests\P0660R10_jthread_and_cv_any @@ -250,10 +261,12 @@ tests\P0660R10_stop_token tests\P0660R10_stop_token_death tests\P0674R1_make_shared_for_arrays tests\P0718R2_atomic_smart_ptrs +tests\P0753R2_manipulators_for_cpp_synchronized_buffered_ostream tests\P0758R1_is_nothrow_convertible tests\P0768R1_spaceship_cpos tests\P0768R1_spaceship_operator tests\P0769R2_shift_left_shift_right +tests\P0784R7_library_machinery tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp tests\P0896R4_common_iterator @@ -287,6 +300,7 @@ tests\P0896R4_ranges_alg_generate tests\P0896R4_ranges_alg_generate_n tests\P0896R4_ranges_alg_heap tests\P0896R4_ranges_alg_includes +tests\P0896R4_ranges_alg_inplace_merge tests\P0896R4_ranges_alg_is_permutation tests\P0896R4_ranges_alg_is_sorted tests\P0896R4_ranges_alg_lexicographical_compare @@ -324,6 +338,8 @@ tests\P0896R4_ranges_alg_set_symmetric_difference tests\P0896R4_ranges_alg_set_union tests\P0896R4_ranges_alg_shuffle tests\P0896R4_ranges_alg_sort +tests\P0896R4_ranges_alg_stable_partition +tests\P0896R4_ranges_alg_stable_sort tests\P0896R4_ranges_alg_swap_ranges tests\P0896R4_ranges_alg_transform_binary tests\P0896R4_ranges_alg_transform_unary @@ -358,6 +374,7 @@ tests\P0896R4_views_elements tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death +tests\P0896R4_views_iota tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take @@ -370,6 +387,9 @@ tests\P0898R3_identity tests\P0912R5_coroutine tests\P0919R3_heterogeneous_unordered_lookup tests\P0966R1_string_reserve_should_not_shrink +tests\P0980R1_constexpr_strings +tests\P1004R2_constexpr_vector +tests\P1004R2_constexpr_vector_bool tests\P1007R3_assume_aligned tests\P1020R1_smart_pointer_for_overwrite tests\P1023R0_constexpr_for_array_comparisons @@ -381,8 +401,10 @@ tests\P1135R6_barrier tests\P1135R6_latch tests\P1135R6_semaphore tests\P1165R1_consistently_propagating_stateful_allocators +tests\P1208R6_source_location tests\P1423R3_char8_t_remediation tests\P1502R1_standard_library_header_units +tests\P1614R2_spaceship tests\P1645R1_constexpr_numeric tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions diff --git a/tests/std/tests/Dev10_816787_swap_vector_bool_elements/test.cpp b/tests/std/tests/Dev10_816787_swap_vector_bool_elements/test.cpp index 49726a50d6c..619ccd9a0f0 100644 --- a/tests/std/tests/Dev10_816787_swap_vector_bool_elements/test.cpp +++ b/tests/std/tests/Dev10_816787_swap_vector_bool_elements/test.cpp @@ -17,6 +17,7 @@ int main() { assert(all_of(x.begin(), x.end(), is_false)); assert(all_of(y.begin(), y.end(), is_true)); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 swap(x[12], y[34]); assert(all_of(x.begin(), x.begin() + 12, is_false)); @@ -26,4 +27,5 @@ int main() { assert(all_of(y.begin(), y.begin() + 34, is_true)); assert(!y[34]); assert(all_of(y.begin() + 35, y.end(), is_true)); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 } diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst similarity index 100% rename from tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst rename to tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst diff --git a/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp new file mode 100644 index 00000000000..43dfd67165b --- /dev/null +++ b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +vector vb_from_str(const char* str) { + vector vb; + + for (; *str != '\0'; ++str) { + assert(*str == '0' || *str == '1'); + + vb.push_back(*str == '1'); + } + + return vb; +} + +enum class Ordering : int { Less = -1, Equal = 0, Greater = 1 }; + +constexpr Ordering Lt = Ordering::Less; +constexpr Ordering Eq = Ordering::Equal; +constexpr Ordering Gt = Ordering::Greater; + +void test_comparison(const char* const left_str, const char* const right_str, const Ordering order) { + const auto left = vb_from_str(left_str); + const auto right = vb_from_str(right_str); + +#ifdef __cpp_lib_concepts + assert((left <=> right) == (static_cast(order) <=> 0)); + assert((right <=> left) == (0 <=> static_cast(order))); +#endif // __cpp_lib_concepts + + switch (order) { + case Lt: + assert(!(left == right)); + assert(!(right == left)); + assert(left < right); + assert(!(right < left)); + break; + case Eq: + assert(left == right); + assert(right == left); + assert(!(left < right)); + assert(!(right < left)); + break; + case Gt: + assert(!(left == right)); + assert(!(right == left)); + assert(!(left < right)); + assert(right < left); + break; + default: + assert(false); + break; + } +} + +int main() { + + { + mt19937 eng(1729); + + uniform_int_distribution dist(0, 1); + + const size_t N = 137; + + vector x(N); + vector y(N); + + for (size_t i = 0; i < N; ++i) { + const bool b = dist(eng) != 0; + + x[i] = b; + y[i] = b; + } + + assert(x == y); + + y.push_back(0); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + y.push_back(1); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + x.back().flip(); + + assert(x != y); + + y.back().flip(); + + assert(x == y); + } + + { + // Also test DevDiv-850453 ": Missing emplace methods in std::vector container". + + vector v(47, allocator()); + + v.emplace_back(make_shared(123)); + v.emplace_back(shared_ptr()); + + v.emplace(v.cbegin(), make_shared(3.14)); + v.emplace(v.cbegin(), make_unique(456)); + v.emplace(v.cbegin(), shared_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + + + vector correct; + + correct.insert(correct.cend(), 3, false); + correct.insert(correct.cend(), 2, true); + correct.insert(correct.cend(), 47, false); + correct.insert(correct.cend(), 1, true); + correct.insert(correct.cend(), 1, false); + + assert(v == correct); + } + + // Also test GH-1046 optimizing vector spaceship and less-than comparisons. + test_comparison("", "", Eq); // both empty + test_comparison("", "00", Lt); // empty vs. partial word + test_comparison("", "01", Lt); + test_comparison("", "00000000000000000000000000000000", Lt); // empty vs. full word + test_comparison("", "01000000000000000000000000000000", Lt); + test_comparison("", "0000000000000000000000000000000000", Lt); // empty vs. full and partial words + test_comparison("", "0100000000000000000000000000000001", Lt); + + test_comparison("010111010", "010111010", Eq); + test_comparison("010111010", "011110010", Lt); // test that bits are compared in the correct direction + + // same test, after an initial matching word + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111010111010", Eq); + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111011110010", Lt); + + test_comparison("00001111", "00001111", Eq); + test_comparison("00001111", "000011110", Lt); // matching prefixes, test size comparison + test_comparison("00001111", "000011111", Lt); + test_comparison("00001111", "00001111000000000000000000000000", Lt); // full word + test_comparison("00001111", "00001111000000000000000000000001", Lt); + test_comparison("00001111", "0000111100000000000000000000000000", Lt); // full and partial words + test_comparison("00001111", "0000111100000000000000000000000001", Lt); + + test_comparison("10", "01111", Gt); // shorter but greater + test_comparison("10", "01111111111111111111111111111111", Gt); // full word + test_comparison("10", "0111111111111111111111111111111111", Gt); // full and partial words +} diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp deleted file mode 100644 index 2aa72a7adaf..00000000000 --- a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include -#include -#include - -using namespace std; - -int main() { - - { - mt19937 eng(1729); - - uniform_int_distribution dist(0, 1); - - const size_t N = 137; - - vector x(N); - vector y(N); - - for (size_t i = 0; i < N; ++i) { - const bool b = dist(eng) != 0; - - x[i] = b; - y[i] = b; - } - - assert(x == y); - - y.push_back(0); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - y.push_back(1); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - x.back().flip(); - - assert(x != y); - - y.back().flip(); - - assert(x == y); - } - - { - // Also test DevDiv-850453 ": Missing emplace methods in std::vector container". - - vector v(47, allocator()); - - v.emplace_back(make_shared(123)); - v.emplace_back(shared_ptr()); - - v.emplace(v.cbegin(), make_shared(3.14)); - v.emplace(v.cbegin(), make_unique(456)); - v.emplace(v.cbegin(), shared_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - - - vector correct; - - correct.insert(correct.cend(), 3, false); - correct.insert(correct.cend(), 2, true); - correct.insert(correct.cend(), 47, false); - correct.insert(correct.cend(), 1, true); - correct.insert(correct.cend(), 1, false); - - assert(v == correct); - } -} diff --git a/tests/std/tests/Dev11_0836436_get_time/test.cpp b/tests/std/tests/Dev11_0836436_get_time/test.cpp index 2d704e204dd..d28890d0014 100644 --- a/tests/std/tests/Dev11_0836436_get_time/test.cpp +++ b/tests/std/tests/Dev11_0836436_get_time/test.cpp @@ -494,6 +494,64 @@ void test_990695() { assert(t.tm_min == 8); assert(t.tm_sec == 0); } + + { + // Should fail if EOF while not parsing specifier (N4878 [locale.time.get.members]/8.3). + tm t{}; + istringstream iss("4"); + iss >> get_time(&t, "42"); + assert(iss.rdstate() == (ios_base::eofbit | ios_base::failbit)); + } + + { + // Trailing % should not be treated as a literal (N4878 [locale.time.get.members]/8.4). + tm t{}; + istringstream iss("%"); + iss >> get_time(&t, "%"); + assert(iss.fail()); + } + + { + // % with modifier but no specifier is also incomplete. + tm t{}; + istringstream iss("%E"); + iss >> get_time(&t, "%E"); + assert(iss.fail()); + } + + { + // Literal match is case-insensitive (N4878 [locale.time.get.members]/8.6). + tm t{}; + istringstream iss("aBc"); + iss >> get_time(&t, "AbC"); + assert(iss); + } + + { + // GH-1606: reads too many leading zeros + istringstream iss("19700405T000006"); + tm t{}; + iss >> get_time(&t, "%Y%m%dT%H%M%S"); + assert(iss); + + printf("Expected hour 0, min 0, sec 6\n"); + printf(" Got hour %d, min %d, sec %d\n", t.tm_hour, t.tm_min, t.tm_sec); + + assert(t.tm_year == 70); + assert(t.tm_mon == 3); + assert(t.tm_mday == 5); + assert(t.tm_hour == 0); + assert(t.tm_min == 0); + assert(t.tm_sec == 6); + } + + { + // strptime specification: "leading zeros are permitted but not required" + tm t{}; + istringstream{" 7 4"} >> get_time(&t, "%m%d"); + assert(t.tm_mon == 6); + assert(t.tm_mday == 4); + } } } diff --git a/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp b/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp index aa38f7b4c6a..d1c74c1e5ad 100644 --- a/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp +++ b/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp @@ -3,6 +3,7 @@ // Test DevDiv-920385 ": list::sort shouldn't default-construct allocators". +#define _HAS_DEPRECATED_ALLOCATOR_MEMBERS 1 #define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING #include diff --git a/tests/std/tests/GH_000690_overaligned_function/test.cpp b/tests/std/tests/GH_000690_overaligned_function/test.cpp index effd03765af..0e5623f2054 100644 --- a/tests/std/tests/GH_000690_overaligned_function/test.cpp +++ b/tests/std/tests/GH_000690_overaligned_function/test.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// UNSUPPORTED: arm + #include #include #include diff --git a/tests/std/tests/GH_001530_binomial_accuracy/env.lst b/tests/std/tests/GH_001530_binomial_accuracy/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_001530_binomial_accuracy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_001530_binomial_accuracy/test.cpp b/tests/std/tests/GH_001530_binomial_accuracy/test.cpp new file mode 100644 index 00000000000..4c3d704e7e4 --- /dev/null +++ b/tests/std/tests/GH_001530_binomial_accuracy/test.cpp @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; + +template +void test_binomial(const int n, const double mean, Generator& gen) { + const double p = mean / n; + const double var = n * p * (1.0 - p); + constexpr double tol = 0.01; + constexpr double x = 4.0; // Standard deviation of sample variance should be less than tol / x. + const size_t it_max = 2 * static_cast(pow(var / (tol / x), 2.0)); + binomial_distribution<> dist(n, p); + + vector counts(static_cast(n) + 1); + for (size_t i = 0; i < it_max; ++i) { + ++counts[static_cast(dist(gen))]; + } + + double sample_mean = 0.0; + for (size_t i = 1; i < counts.size(); ++i) { + sample_mean += static_cast(i) * counts[i]; + } + sample_mean /= it_max; + + double sample_var = 0.0; + for (size_t i = 0; i < counts.size(); ++i) { + sample_var += counts[i] * pow(i - sample_mean, 2); + } + sample_var /= it_max - 1; + + assert(abs(sample_mean / mean - 1.0) < tol); + assert(abs(sample_var / var - 1.0) < tol); +} + +int main() { + mt19937 gen; + constexpr int n = 25; + test_binomial(n, 0.99, gen); + test_binomial(n, n - 0.99, gen); + return 0; +} diff --git a/tests/std/tests/GH_001638_dllexport_derived_classes/env.lst b/tests/std/tests/GH_001638_dllexport_derived_classes/env.lst new file mode 100644 index 00000000000..f141421b292 --- /dev/null +++ b/tests/std/tests/GH_001638_dllexport_derived_classes/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\impure_matrix.lst diff --git a/tests/std/tests/GH_001638_dllexport_derived_classes/test.compile.pass.cpp b/tests/std/tests/GH_001638_dllexport_derived_classes/test.compile.pass.cpp new file mode 100644 index 00000000000..3989d13f884 --- /dev/null +++ b/tests/std/tests/GH_001638_dllexport_derived_classes/test.compile.pass.cpp @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + +using namespace std; + +int main() {} // COMPILE-ONLY + +#pragma warning(disable : 4251) // class 'A' needs to have dll-interface to be used by clients of class 'B' +#pragma warning(disable : 4275) // non dll-interface struct 'A' used as base for dll-interface class 'B' + +struct __declspec(dllexport) ExportedArray : array {}; +struct __declspec(dllexport) ExportedArrayZero : array {}; +struct __declspec(dllexport) ExportedDeque : deque {}; +struct __declspec(dllexport) ExportedForwardList : forward_list {}; +struct __declspec(dllexport) ExportedList : list {}; +struct __declspec(dllexport) ExportedVector : vector {}; +struct __declspec(dllexport) ExportedVectorBool : vector {}; + +struct __declspec(dllexport) ExportedMap : map {}; +struct __declspec(dllexport) ExportedMultimap : multimap {}; +struct __declspec(dllexport) ExportedSet : set {}; +struct __declspec(dllexport) ExportedMultiset : multiset {}; + +struct __declspec(dllexport) ExportedUnorderedMap : unordered_map {}; +struct __declspec(dllexport) ExportedUnorderedMultimap : unordered_multimap {}; +struct __declspec(dllexport) ExportedUnorderedSet : unordered_set {}; +struct __declspec(dllexport) ExportedUnorderedMultiset : unordered_multiset {}; + +struct __declspec(dllexport) ExportedQueue : queue {}; +struct __declspec(dllexport) ExportedPriorityQueue : priority_queue {}; +struct __declspec(dllexport) ExportedStack : stack {}; + +#if _HAS_CXX20 +struct __declspec(dllexport) ExportedSpan : span {}; +struct __declspec(dllexport) ExportedSpanThree : span {}; +#endif // _HAS_CXX20 diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/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/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp new file mode 100644 index 00000000000..06da2ce3a88 --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -0,0 +1,385 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "test.hpp" + +using namespace std; + +static_assert(is_default_constructible_v); +static_assert(is_constructible_v); +static_assert(is_constructible_v); +static_assert(is_move_constructible_v); +static_assert(is_move_assignable_v); +static_assert(is_swappable_v); +static_assert(is_destructible_v); + +template +class test_syncbuf : public basic_syncbuf { +public: + using size_type = typename Alloc::size_type; + using value_type = typename Alloc::value_type; + using Mybase = basic_syncbuf; + using streambuf_type = typename Mybase::streambuf_type; + + using Mybase::epptr; + using Mybase::pbase; + using Mybase::pptr; + + test_syncbuf() = default; + + explicit test_syncbuf(streambuf_type* strbuf) : Mybase(strbuf) {} + + test_syncbuf(streambuf_type* strbuf, const Alloc& al) : Mybase(strbuf, al) {} + + test_syncbuf(test_syncbuf&&) = default; + test_syncbuf& operator=(test_syncbuf&&) = default; + ~test_syncbuf() = default; + + auto Test_get_buffer_size() const noexcept { + return static_cast(epptr() - pbase()); + } + auto Test_get_data_size() const noexcept { + return static_cast(pptr() - pbase()); + } + + test_syncbuf(const test_syncbuf&) = delete; + test_syncbuf& operator=(const test_syncbuf&) = delete; +}; + +template +void test_syncbuf_member_functions(string_buffer* buf = nullptr) { + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + Alloc alloc{}; + + Syncbuf aSyncbuf{buf, alloc}; + + // test construction post-conditions + assert(aSyncbuf.get_wrapped() == buf); + assert(aSyncbuf.get_allocator() == alloc); + assert(aSyncbuf.Test_get_data_size() == 0); + assert(aSyncbuf.Test_get_buffer_size() == Min_syncbuf_size); + + // check emit post-conditions with no input + if (buf) { + assert(aSyncbuf.emit() == true); + } else { + assert(aSyncbuf.emit() == false); + } + + OStream os{&aSyncbuf}; + + os << "A small string\n"; + assert(aSyncbuf.Test_get_data_size() == 15); + if (buf) { + assert(aSyncbuf.emit() == true); + assert(buf->str == "A small string\n"); + buf->str.clear(); + } else { + assert(os.rdstate() == ios::goodbit); + assert(aSyncbuf.emit() == false); + } + + os << "A string holds more than 32 characters"; // requires one re-allocation + if (buf) { + assert(aSyncbuf.Test_get_data_size() == 38); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string holds more than 32 characters"); + buf->str.clear(); + } else { + assert(aSyncbuf.Test_get_data_size() == Min_syncbuf_size); // if _Wrapped is nullptr, re-allocation will not + // occur and will return a _Traits::eof bit + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == false); + } + + os << "A string that will definitely overflow the small_size_allocator"; // requires more than one re-allocation + if (buf) { + if constexpr (is_base_of_v) { // fail to allocate enough memory + assert(aSyncbuf.Test_get_data_size() == Min_size_allocation); + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string that will definitely overflow the small_s"); + } else { + assert(aSyncbuf.Test_get_data_size() == 63); + assert(os.rdstate() == ios::goodbit); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string that will definitely overflow the small_size_allocator"); + } + buf->str.clear(); + } else { + assert(aSyncbuf.Test_get_data_size() == Min_syncbuf_size); + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == false); + } +} + +template +void test_syncbuf_synchronization(string_buffer* buf) { + assert(buf); // meaningless to run with nullptr + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + { + Syncbuf buf1{buf}; + OStream os1{&buf1}; + os1 << "Last element "; + { + Syncbuf buf2{buf}; + buf2.set_emit_on_sync(false); + OStream os2{&buf2}; + os2 << "Second element "; + int syncResult = buf2.pubsync(); // trigger a sync + assert(syncResult == 0); + { + Syncbuf buf3{buf}; + OStream{&buf3} << "First element to be presented!\n"; + } + os2 << "to be presented!\n"; + } + os1 << "to be presented!\n"; + } + assert( + buf->str == "First element to be presented!\nSecond element to be presented!\nLast element to be presented!\n"); + buf->str.clear(); + { + Syncbuf buf1{buf}; + OStream os1{&buf1}; + os1 << "Last element "; + { + Syncbuf buf2{buf}; + buf2.set_emit_on_sync(true); + OStream os2{&buf2}; + os2 << "First element to be emitted by sync!\n"; + int syncResult = buf2.pubsync(); // trigger a sync + if constexpr (ThrowOnSync) { + assert(syncResult == -1); + } else { + assert(syncResult == 0); + } + { + Syncbuf buf3{buf}; + OStream{&buf3} << "Second element to be presented!\n"; + } + os2 << "Third element to be presented!\n"; + } + os1 << "to be presented!\n"; + } + assert(buf->str + == "First element to be emitted by sync!\nSecond element to be presented!\nThird element to be " + "presented!\nLast element to be presented!\n"); + buf->str.clear(); +} + +template +void test_syncbuf_move_swap_operations(string_buffer* buf) { + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + { // test move constructor + Syncbuf buf1{buf}; + OStream(&buf1) << "Some input"; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + Syncbuf buf2{move(buf1)}; + assert(buf->str == ""); + + // move constructor post-conditions + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == nullptr); + assert(buf1.pbase() == buf1.pptr()); + assert(buf1.Test_get_buffer_size() == 0); + assert(buf1.Test_get_data_size() == 0); + } + assert(buf->str == "Some input"); + buf->str.clear(); + { // test move assignment + Syncbuf buf1{buf}; + OStream{&buf1} << "Some input"; + Syncbuf buf2{nullptr}; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + buf2 = move(buf1); + + // move assignment post-conditions + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == nullptr); + assert(buf1.Test_get_buffer_size() == 0); + assert(buf1.Test_get_data_size() == 0); + + if constexpr (allocator_traits::propagate_on_container_move_assignment::value + && allocator_traits::is_always_equal::value) { + assert(buf1.get_allocator() == buf2.get_allocator()); + } else if constexpr (allocator_traits::is_always_equal::value) { + assert(buf1.get_allocator() == buf2.get_allocator()); + } else { + assert(buf1.get_allocator() != buf2.get_allocator()); + } + } + assert(buf->str == "Some input"); + buf->str.clear(); + { // test swap + Syncbuf buf1{buf}; + OStream{&buf1} << "Some input that requires re-allocation"; + Syncbuf buf2{nullptr}; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + auto buf2WrappedObject = buf2.get_wrapped(); + auto buf2BufferSize = buf2.Test_get_buffer_size(); + auto buf2DataSize = buf2.Test_get_data_size(); + if constexpr (allocator_traits::propagate_on_container_swap::value + || allocator_traits::is_always_equal::value) { + buf1.swap(buf2); + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == buf2WrappedObject); + assert(buf1.Test_get_buffer_size() == buf2BufferSize); + assert(buf1.Test_get_data_size() == buf2DataSize); + } + } + assert(buf->str == "Some input that requires re-allocation"); + buf->str.clear(); +} + +template +void test_osyncstream(string_buffer* buf = nullptr) { + using value_type = typename Alloc::value_type; + using OSyncStream = basic_osyncstream, Alloc>; + + { + OSyncStream oss{buf}; + + // test construction post-conditions + assert(oss.get_wrapped() == buf); + assert(oss.rdbuf()->get_wrapped() == buf); + + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + + oss << "A small string\n"; + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + if (buf) { + assert(buf->str == "A small string\n"); + buf->str.clear(); + } + + oss << "A string holds more than 32 characters"; // requires one re-allocation + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + if (buf) { + assert(buf->str == "A string holds more than 32 characters"); + buf->str.clear(); + } + + oss << "A string that will definitely overflow the small_size_allocator"; // requires more than one + // re-allocation + if constexpr (is_base_of_v) { // fail to allocate enough memory + assert(oss.rdstate() == ios::badbit); + oss.clear(); + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + assert(buf ? buf->str == "A string that will definitely overflow the small_s" : true); + } else { + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + assert(buf ? buf->str == "A string that will definitely overflow the small_size_allocator" : true); + } + } + if (buf) { + buf->str.clear(); + } + + { // move construction + OSyncStream oss{buf}; + oss << "Some input"; + auto ossWrapped = oss.get_wrapped(); + OSyncStream oss1{move(oss)}; + + assert(oss1.get_wrapped() == ossWrapped); + assert(oss.get_wrapped() == nullptr); + } + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + + { + OSyncStream oss{buf}; + oss << "Some input"; + auto ossWrapped = oss.get_wrapped(); + + OSyncStream oss1{buf}; + oss1 << "An input to emit first\n"; + + oss1 = move(oss); + + assert(oss1.get_wrapped() == ossWrapped); + assert(oss.get_wrapped() == nullptr); + if (buf) { + assert(buf->str == "An input to emit first\n"); + buf->str.clear(); + } + } + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + + { // test synchronization + OSyncStream oss(buf); + oss << "Last "; + { OSyncStream(oss.get_wrapped()) << "First Input!\n"; } + oss << "Input!" << '\n'; + } + if (buf) { + assert(buf->str == "First Input!\nLast Input!\n"); + buf->str.clear(); + } +} + +int main() { + string_buffer char_buffer{}; + string_buffer no_sync_char_buffer{}; + + // Testing basic_syncbuf + test_syncbuf_member_functions>(); + test_syncbuf_member_functions>(); + + test_syncbuf_member_functions>(&char_buffer); + test_syncbuf_member_functions>(&char_buffer); + + test_syncbuf_synchronization>(&char_buffer); + test_syncbuf_synchronization>(&no_sync_char_buffer); + + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + + // Testing basic_osyncstream + test_osyncstream>(); + test_osyncstream>(); + + test_osyncstream>(&char_buffer); + test_osyncstream>(&char_buffer); +} diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp new file mode 100644 index 00000000000..beffa7170de --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +constexpr size_t Min_size_allocation = 50; +constexpr size_t Min_syncbuf_size = 32; + +template +class string_buffer : public basic_streambuf> { // represents the wrapped object in syncbuf +public: + string_buffer() = default; + ~string_buffer() = default; + + streamsize xsputn(const Ty* ptr, streamsize n) override { + str.append(ptr, static_cast(n)); + return n; + } + + int sync() override { + if constexpr (ThrowOnSync) { + return -1; + } else { + return 0; + } + } + + string str; +}; + +class small_size_allocation { +public: + using size_type = size_t; + + [[nodiscard]] size_type max_size() const noexcept { + return Min_size_allocation; + } +}; + +template +class allocator_base { +public: + using value_type = Ty; + using pointer = Ty*; + + [[nodiscard]] pointer allocate(const size_t n) { + return allocator{}.allocate(n); + } + + void deallocate(const pointer ptr, const size_t n) noexcept { + allocator{}.deallocate(ptr, n); + } +}; + +template +class small_size_allocator : public allocator_base, public small_size_allocation { +public: + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = true_type; + + constexpr small_size_allocator() noexcept = default; + + template + constexpr small_size_allocator(const small_size_allocator&) noexcept {} +}; + +template +[[nodiscard]] bool operator==(const small_size_allocator&, const small_size_allocator&) noexcept { + return true; +} + +class non_move_assignable_non_equal_allocator_id { +public: + non_move_assignable_non_equal_allocator_id() : id(id_gen++) {} + constexpr explicit non_move_assignable_non_equal_allocator_id(size_t _id) : id(_id) {} + ~non_move_assignable_non_equal_allocator_id() = default; + + size_t id; + +private: + inline static size_t id_gen = 0; +}; + +template +class non_move_assignable_non_equal_allocator : public non_move_assignable_non_equal_allocator_id, + public allocator_base { +public: + using size_type = size_t; + using propagate_on_container_move_assignment = false_type; + using propagate_on_container_swap = true_type; + using is_always_equal = false_type; + + non_move_assignable_non_equal_allocator() noexcept = default; + + template + constexpr non_move_assignable_non_equal_allocator( + const non_move_assignable_non_equal_allocator& rhs) noexcept + : non_move_assignable_non_equal_allocator_id{rhs.id} {} +}; + +template +[[nodiscard]] bool operator==(const non_move_assignable_non_equal_allocator& lhs, + const non_move_assignable_non_equal_allocator& rhs) noexcept { + return lhs.id == rhs.id; +} + +template +class non_move_assignable_equal_allocator : public allocator_base { +public: + using size_type = size_t; + using propagate_on_container_move_assignment = false_type; + using propagate_on_container_swap = true_type; + + constexpr non_move_assignable_equal_allocator() noexcept = default; + + template + constexpr non_move_assignable_equal_allocator(const non_move_assignable_equal_allocator&) noexcept {} +}; + +template +[[nodiscard]] bool operator==( + const non_move_assignable_equal_allocator&, const non_move_assignable_equal_allocator&) noexcept { + return true; +} + +template +class non_swappable_equal_allocator : public allocator_base { +public: + using size_type = size_t; + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = false_type; + + constexpr non_swappable_equal_allocator() noexcept = default; + + template + constexpr non_swappable_equal_allocator(const non_swappable_equal_allocator&) noexcept {} +}; + +template +[[nodiscard]] bool operator==( + const non_swappable_equal_allocator&, const non_swappable_equal_allocator&) noexcept { + return true; +} diff --git a/tests/std/tests/P0067R5_charconv/test.cpp b/tests/std/tests/P0067R5_charconv/test.cpp index 466a8d72d7c..4201ccf9856 100644 --- a/tests/std/tests/P0067R5_charconv/test.cpp +++ b/tests/std/tests/P0067R5_charconv/test.cpp @@ -101,7 +101,8 @@ void initialize_randomness(mt19937_64& mt64, const int argc, char** const argv) puts("Successfully seeded mt64. First three values:"); for (int i = 0; i < 3; ++i) { - printf("0x%016llX\n", mt64()); + // libc++ uses long for 64-bit values. + printf("0x%016llX\n", static_cast(mt64())); } } @@ -575,7 +576,8 @@ void assert_message_bits(const bool b, const char* const msg, const uint32_t bit void assert_message_bits(const bool b, const char* const msg, const uint64_t bits) { if (!b) { - fprintf(stderr, "%s failed for 0x%016llX\n", msg, bits); + // libc++ uses long for 64-bit values. + fprintf(stderr, "%s failed for 0x%016llX\n", msg, static_cast(bits)); fprintf(stderr, "This is a randomized test.\n"); fprintf(stderr, "DO NOT IGNORE/RERUN THIS FAILURE.\n"); fprintf(stderr, "You must report it to the STL maintainers.\n"); @@ -1098,8 +1100,8 @@ int main(int argc, char** argv) { printf("Total time: %lld ms\n", ms); if (ms < 3'000) { - puts("That was fast. Consider retuning PrefixesToTest and FractionBits."); + puts("That was fast. Consider tuning PrefixesToTest and FractionBits to test more cases."); } else if (ms > 30'000) { - puts("That was slow. Consider retuning PrefixesToTest and FractionBits."); + puts("That was slow. Consider tuning PrefixesToTest and FractionBits to test fewer cases."); } } diff --git a/tests/std/tests/P0088R3_variant/env.lst b/tests/std/tests/P0088R3_variant/env.lst index 48e3b76c748..474ef952dd3 100644 --- a/tests/std/tests/P0088R3_variant/env.lst +++ b/tests/std/tests/P0088R3_variant/env.lst @@ -10,7 +10,7 @@ RUNALL_CROSSLIST PM_CL="/w14640 /Zc:threadSafeInit-" RUNALL_CROSSLIST PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:noexceptTypes-" -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW" +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:wchar_t-" @@ -22,15 +22,15 @@ PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze: PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /fp:strict" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" -PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW" +PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog-" PM_CL="/Za /EHsc /MD /std:c++latest /permissive-" PM_CL="/Za /EHsc /MDd /std:c++latest /permissive-" -PM_CL="/clr /MD /std:c++17 /DCONSTEXPR_NOTHROW" -PM_CL="/clr /MDd /std:c++17 /DCONSTEXPR_NOTHROW" +PM_CL="/clr /MD /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" +PM_CL="/clr /MDd /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive-" PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++latest /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MDd /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MDd /std:c++17 /DTEST_PERMISSIVE" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MTd /std:c++latest /permissive- /fp:strict" diff --git a/tests/std/tests/P0088R3_variant/test.cpp b/tests/std/tests/P0088R3_variant/test.cpp index 7a66a9f06e0..87e482ffec0 100644 --- a/tests/std/tests/P0088R3_variant/test.cpp +++ b/tests/std/tests/P0088R3_variant/test.cpp @@ -1768,7 +1768,8 @@ int run_test() { static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, int>::value, ""); -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 static_assert(std::is_assignable, int>::value == VariantAllowsNarrowingConversions, ""); static_assert(std::is_assignable, int>::value @@ -1778,13 +1779,16 @@ int run_test() static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); +#endif // !__EDG__ static_assert(!std::is_assignable, decltype("meow")>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); - static_assert(!std::is_assignable, std::true_type>::value, ""); + static_assert(std::is_assignable, std::true_type>::value, ""); static_assert(!std::is_assignable, std::unique_ptr >::value, ""); +#ifndef TEST_PERMISSIVE static_assert(!std::is_assignable, decltype(nullptr)>::value, ""); -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 return 0; } @@ -3048,7 +3052,8 @@ void test_T_assignment_sfinae() { using V = std::variant; static_assert(!std::is_assignable::value, "no matching operator="); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 { using V = std::variant; static_assert(std::is_assignable::value == VariantAllowsNarrowingConversions, @@ -3063,10 +3068,11 @@ void test_T_assignment_sfinae() { }; static_assert(!std::is_assignable::value, "no boolean conversion in operator="); - static_assert(!std::is_assignable::value, + static_assert(std::is_assignable::value, "no converted to bool in operator="); } -#endif // TRANSITION, P0608 +#endif // !__EDG__ +#endif // _HAS_CXX20 { struct X {}; struct Y { @@ -3104,7 +3110,8 @@ void test_T_assignment_basic() { assert(v.index() == 1); assert(std::get<1>(v) == 43); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { std::variant v; @@ -3116,19 +3123,22 @@ void test_T_assignment_basic() { assert(std::get<0>(v) == 43); } #endif +#endif // !__EDG__ { std::variant v = true; v = "bar"; assert(v.index() == 0); assert(std::get<0>(v) == "bar"); } +#ifndef TEST_PERMISSIVE { std::variant> v; v = nullptr; assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 { std::variant v = 42; v = false; @@ -3266,7 +3276,8 @@ int run_test() { static_assert(!std::is_constructible, int>::value, ""); static_assert(!std::is_constructible, int>::value, ""); -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 static_assert(std::is_constructible, int>::value == VariantAllowsNarrowingConversions, ""); static_assert(std::is_constructible, int>::value @@ -3278,11 +3289,13 @@ int run_test() static_assert(!std::is_constructible, decltype("meow")>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); - - static_assert(!std::is_constructible, std::true_type>::value, ""); +#endif // !__EDG__ + static_assert(std::is_constructible, std::true_type>::value, ""); static_assert(!std::is_constructible, std::unique_ptr >::value, ""); +#ifndef TEST_PERMISSIVE static_assert(!std::is_constructible, decltype(nullptr)>::value, ""); -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 return 0; } @@ -4567,7 +4580,8 @@ void test_T_ctor_sfinae() { static_assert(!std::is_constructible::value, "no matching constructor"); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 { using V = std::variant; static_assert(std::is_constructible::value == VariantAllowsNarrowingConversions, @@ -4582,10 +4596,11 @@ void test_T_ctor_sfinae() { }; static_assert(!std::is_constructible::value, "no boolean conversion in constructor"); - static_assert(!std::is_constructible::value, + static_assert(std::is_constructible::value, "no converted to bool in constructor"); } -#endif // TRANSITION, P0608 +#endif // !__EDG__ +#endif // _HAS_CXX20 { struct X {}; struct Y { @@ -4629,7 +4644,8 @@ void test_T_ctor_basic() { static_assert(v.index() == 1, ""); static_assert(std::get<1>(v) == 42, ""); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { constexpr std::variant v(42); @@ -4637,17 +4653,20 @@ void test_T_ctor_basic() { static_assert(std::get<1>(v) == 42, ""); } #endif +#endif // !__EDG__ { std::variant v = "meow"; assert(v.index() == 0); assert(std::get<0>(v) == "meow"); } +#ifndef TEST_PERMISSIVE { std::variant> v = nullptr; assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 { std::variant v = true; assert(v.index() == 0); @@ -4681,6 +4700,7 @@ void test_T_ctor_basic() { #endif } +#if !_HAS_CXX20 // Narrowing check occurs with P0608R3 struct BoomOnAnything { template constexpr BoomOnAnything(T) { static_assert(!std::is_same::value, ""); } @@ -4692,6 +4712,7 @@ void test_no_narrowing_check_for_class_types() { assert(v.index() == 0); assert(std::get<0>(v) == 42); } +#endif // Narrowing check occurs with P0608R3 struct Bar {}; struct Baz {}; @@ -4708,7 +4729,9 @@ int run_test() { test_T_ctor_basic(); test_T_ctor_noexcept(); test_T_ctor_sfinae(); +#if !_HAS_CXX20 // Narrowing check occurs with P0608R3 test_no_narrowing_check_for_class_types(); +#endif // Narrowing check occurs with P0608R3 test_construction_with_repeated_types(); return 0; } diff --git a/tests/std/tests/P0218R1_filesystem/test.cpp b/tests/std/tests/P0218R1_filesystem/test.cpp index 7273aff888f..c1ac9a46ccf 100644 --- a/tests/std/tests/P0218R1_filesystem/test.cpp +++ b/tests/std/tests/P0218R1_filesystem/test.cpp @@ -1224,6 +1224,12 @@ void test_directory_entry() { EXPECT(good(ec)); remove(dirPath, ec); EXPECT(good(ec)); + + // LWG-3171 "LWG-2989 breaks directory_entry stream insertion" + const directory_entry iostreamEntry{path{R"(one\two\three)"}}; + ostringstream oss; + oss << iostreamEntry; + EXPECT(oss.str() == R"("one\\two\\three")"); } template @@ -3838,8 +3844,13 @@ basic_ostream& operator<<(basic_ostream& str, const return str << status.type() << L' ' << status.permissions(); } +struct directory_entry_wrapper { + directory_entry value; +}; + template -basic_ostream& operator<<(basic_ostream& str, const directory_entry& de) { +basic_ostream& operator<<(basic_ostream& str, const directory_entry_wrapper& de_wrapper) { + const directory_entry& de = de_wrapper.value; return str << L"\n symlink_status: " << de.symlink_status() << L"\n status: " << de.status() << L"\n size: " << de.file_size() << L"\n last_write_time: " << de.last_write_time().time_since_epoch().count() << L"\n hard_link_count: " << de.hard_link_count(); @@ -3872,7 +3883,7 @@ void run_interactive_tests(int argc, wchar_t* argv[]) { } else if (starts_with(arg, L"-stat:"sv)) { wcerr << quoted(arg) << L" => " << status(the_rest) << "\n"; } else if (starts_with(arg, L"-de:"sv)) { - wcerr << quoted(arg) << L" => " << directory_entry(the_rest) << "\n"; + wcerr << quoted(arg) << L" => " << directory_entry_wrapper{directory_entry(the_rest)} << "\n"; } else if (starts_with(arg, L"-mkdir:"sv)) { wcerr << L"create_directory => " << create_directory(the_rest) << "\n"; } else if (starts_with(arg, L"-mkdirs:"sv)) { @@ -3913,7 +3924,7 @@ int wmain(int argc, wchar_t* argv[]) { if (argc > 1) { run_interactive_tests(argc, argv); - return 0; // not a PM_ constant because the caller isn't run.pl here + return 0; } for (const auto& testCase : decompTestCases) { diff --git a/tests/std/tests/P0220R1_optional/test.cpp b/tests/std/tests/P0220R1_optional/test.cpp index a7bb292e541..ec0b2904315 100644 --- a/tests/std/tests/P0220R1_optional/test.cpp +++ b/tests/std/tests/P0220R1_optional/test.cpp @@ -5495,13 +5495,17 @@ constexpr int test() { optional opt(in_place, 2); Y y(3); +#ifndef __EDG__ // TRANSITION, VSO-1268140 assert(std::move(opt).value_or(y) == 2); assert(*opt == 0); +#endif // ^^^ no workaround ^^^ } { optional opt(in_place, 2); +#ifndef __EDG__ // TRANSITION, VSO-1268140 assert(std::move(opt).value_or(Y(3)) == 2); assert(*opt == 0); +#endif // ^^^ no workaround ^^^ } { optional opt; diff --git a/tests/std/tests/P0220R1_searchers/test.cpp b/tests/std/tests/P0220R1_searchers/test.cpp index 0551e3d6477..a0cd15d29b7 100644 --- a/tests/std/tests/P0220R1_searchers/test.cpp +++ b/tests/std/tests/P0220R1_searchers/test.cpp @@ -433,7 +433,7 @@ void test_case_randomized_cases() { if (elapsed > 10s) { cout << "test_case_randomized_cases() took " << duration_cast(elapsed).count() << " ms.\n"; - cout << "Consider retuning Needles and Haystacks.\n"; + cout << "Consider tuning Needles and Haystacks to test fewer cases.\n"; } } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/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/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp new file mode 100644 index 00000000000..26ac91006b0 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +using namespace std::chrono; + +struct not_a_clock { + bool rep(); + static char period; + int duration(); + static float time_point; + using is_steady = long; + static int now; +}; + +struct real_fake_clock { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_rep { + using period = char; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_period { + using rep = bool; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_duration { + using rep = bool; + using period = char; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_time_point { + using rep = bool; + using period = char; + using duration = float; + static long is_steady; + static short now(); +}; + +struct no_steady { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static short now(); +}; + +struct no_now { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static long is_steady; +}; + +static_assert(is_clock::value, "steady_clock is not a clock"); +static_assert(is_clock_v, "steady_clock is not a clock"); +static_assert(is_clock_v, "real_fake_clock is not a clock"); +static_assert(!is_clock_v, "not_a_clock is a clock"); + +static_assert(!is_clock_v, "no_rep is a clock"); +static_assert(!is_clock_v, "no_period is a clock"); +static_assert(!is_clock_v, "no_duration is a clock"); +static_assert(!is_clock_v, "no_time_point is a clock"); +static_assert(!is_clock_v, "no_steady is a clock"); +static_assert(!is_clock_v, "no_now is a clock"); + +int main() {} // COMPILE-ONLY diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_dates/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/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/P0355R7_calendars_and_time_zones_dates/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp new file mode 100644 index 00000000000..6a59c863a07 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp @@ -0,0 +1,1174 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +constexpr int y_min = -32767; +constexpr int y_max = 32767; + +// For testing LWG-3260 "year_month* arithmetic rejects durations convertible to years" +using Decades = duration, years::period>>; + +constexpr void day_test() { + day d{0u}; + + static_assert(noexcept(day{})); + static_assert(noexcept(day{0u})); + + static_assert(noexcept(++d)); + static_assert(noexcept(d++)); + static_assert(noexcept(--d)); + static_assert(noexcept(d--)); + static_assert(noexcept(d += days{})); + static_assert(noexcept(d -= days{})); + + static_assert(noexcept(static_cast(d))); + static_assert(noexcept(d.ok())); + + static_assert(noexcept(d == d)); + static_assert(noexcept(d <=> d)); + + static_assert(noexcept(d + days{})); + static_assert(noexcept(days{} + d)); + + static_assert(noexcept(d - days{})); + static_assert(noexcept(d - d)); + + static_assert(noexcept(0d)); + + assert(static_cast(d) == 0u); + assert(d == 0d); + assert(++d == 1d); + assert(d++ == 1d); + assert(d == 2d); + + assert(--d == 1d); + assert(d-- == 1d); + assert(d == 0d); + + d += days{2}; + assert(d == 2d); + d -= days{2}; + assert(d == 0d); + + assert(d < 2d); + assert(2d > d); + + for (unsigned int i = 0; i <= 255; ++i) { + if (i > 0 && i <= 31) { + assert(day{i}.ok()); + } else { + assert(!day{i}.ok()); + } + } + + assert(5d + days{5} == 10d); + assert(days{5} + 5d == 10d); + assert(10d - days{5} == 5d); + assert(10d - 2d == days{8}); +} + +constexpr void month_test() { + month m{1u}; + + static_assert(noexcept(month{})); + static_assert(noexcept(month{0u})); + + static_assert(noexcept(++m)); + static_assert(noexcept(m++)); + static_assert(noexcept(--m)); + static_assert(noexcept(m--)); + static_assert(noexcept(m += months{})); + static_assert(noexcept(m -= months{})); + + static_assert(noexcept(static_cast(m))); + static_assert(noexcept(m.ok())); + + static_assert(noexcept(m == m)); + static_assert(noexcept(m <=> m)); + + static_assert(noexcept(m + months{})); + static_assert(noexcept(months{0} + m)); + + static_assert(noexcept(m - months{})); + static_assert(noexcept(m - m)); + + assert(static_cast(m) == 1u); + assert(m == January); + assert(++m == February); + assert(m++ == February); + assert(m == March); + + assert(--m == February); + assert(m-- == February); + assert(m == January); + + m += months{2}; + assert(m == March); + m -= months{2}; + assert(m == January); + + for (unsigned int i = 0; i <= 255; ++i) { + if (i >= 1 && i <= 12) { + assert(month{i}.ok()); + } else { + assert(!month{i}.ok()); + } + } + + assert(February > m); + assert(m < February); + + assert(February + months{11} == January); + assert(months{11} + February == January); + assert(month{0} + months{1} == January); + assert(month{13} + months{1} == February); + assert(month{23} + months{1} == December); + assert(February - months{2} == December); + assert(January - February == months{11}); +} + +constexpr void year_test() { + year y{1}; + + static_assert(noexcept(year{})); + static_assert(noexcept(year{0})); + + static_assert(noexcept(++y)); + static_assert(noexcept(y++)); + static_assert(noexcept(--y)); + static_assert(noexcept(y--)); + static_assert(noexcept(y += years{})); + static_assert(noexcept(y -= years{})); + static_assert(noexcept(+y)); + static_assert(noexcept(-y)); + + static_assert(noexcept(y.is_leap())); + static_assert(noexcept(static_cast(y))); + static_assert(noexcept(y.ok())); + static_assert(noexcept(year::min())); + static_assert(noexcept(year::max())); + + static_assert(noexcept(y == y)); + static_assert(noexcept(y <=> y)); + + static_assert(noexcept(y + years{})); + static_assert(noexcept(years{} + y)); + + static_assert(noexcept(y - years{})); + static_assert(noexcept(y - y)); + + static_assert(noexcept(0y)); + + assert(static_cast(y) == 1); + assert(y == 1y); + assert(++y == 2y); + assert(y++ == 2y); + assert(y == 3y); + + assert(--y == 2y); + assert(y-- == 2y); + assert(y == 1y); + + y += years{2}; + assert(y == 3y); + y -= years{2}; + assert(y == 1y); + + year extreme{-30'000}; + extreme += years{60'000}; + assert(extreme == 30'000y); + extreme -= years{60'000}; + assert(extreme == -30'000y); + + assert(+y == 1y); + assert(-y == year{-1}); + auto y2 = -y; + assert(-y2 == y); + assert(+y2 == -y); + + assert(year::min() == year{y_min}); + assert(year::max() == year{y_max}); + + assert(!year{y_min - 1}.ok()); + assert(!year{y_max + 1}.ok()); + + for (int i = y_min; i <= y_max; ++i) { + assert(year{i}.ok()); + if (i % 4 == 0 && (i % 100 != 0 || i % 400 == 0)) { + assert(year{i}.is_leap()); + } else { + assert(!year{i}.is_leap()); + } + } + + assert(y < 2y); + assert(2y > y); + + assert(y + years{4} == 5y); + assert(years{4} + y == 5y); + + assert(y - years{4} == -3y); + assert(year{10} - year{5} == years{5}); + assert(year{-5} - year{-10} == years{5}); +} + +constexpr void weekday_test() { + weekday wd{0u}; + + static_assert(noexcept(weekday{})); + static_assert(noexcept(weekday{0u})); + static_assert(noexcept(weekday{sys_days{}})); + static_assert(noexcept(weekday{local_days{}})); + + static_assert(noexcept(++wd)); + static_assert(noexcept(wd++)); + static_assert(noexcept(--wd)); + static_assert(noexcept(wd--)); + static_assert(noexcept(wd += days{})); + static_assert(noexcept(wd -= days{})); + + static_assert(noexcept(wd.c_encoding())); + static_assert(noexcept(wd.iso_encoding())); + static_assert(noexcept(wd.ok())); + static_assert(noexcept(wd[0u])); + static_assert(noexcept(wd[last])); + + static_assert(noexcept(wd == wd)); + + static_assert(noexcept(wd + days{})); + static_assert(noexcept(days{} + wd)); + + static_assert(noexcept(wd - days{})); + static_assert(noexcept(wd - wd)); + + assert(weekday{7} == Sunday); + assert(weekday{sys_days{}} == Thursday); + assert(weekday{local_days{}} == sys_days{local_days{}.time_since_epoch()}); + + assert(wd == Sunday); + assert(++wd == Monday); + assert(wd++ == Monday); + assert(wd == Tuesday); + + assert(--wd == Monday); + assert(wd-- == Monday); + assert(wd == Sunday); + + wd += days{2}; + assert(wd == Tuesday); + wd -= days{3}; + assert(wd == Saturday); + + assert(Sunday.c_encoding() == 0u); + assert(Sunday.iso_encoding() == 7u); + + for (unsigned int i = 0; i <= 255; ++i) { + if (i <= 7) { + assert(weekday{i}.ok()); + } else { + assert(!weekday{i}.ok()); + } + } + assert(Monday + days{6} == Sunday); + assert(Monday + days{8} == Tuesday); + assert(Wednesday + days{14} == Wednesday); + assert(Sunday - Monday == days{6}); + assert(Sunday - Tuesday == days{5}); + assert(Wednesday - Thursday == days{6}); +} + +constexpr void weekday_indexed_test() { + const weekday_indexed wdi1{Monday, 2}; + + static_assert(noexcept(weekday_indexed{})); + static_assert(noexcept(weekday_indexed{weekday{}, 0u})); + + static_assert(noexcept(wdi1.weekday())); + static_assert(noexcept(wdi1.index())); + static_assert(noexcept(wdi1.ok())); + + static_assert(noexcept(wdi1 == wdi1)); + + assert(wdi1.weekday() == Monday); + assert(wdi1.index() == 2); + + weekday_indexed wdi2 = Monday[2]; + assert(wdi2.weekday() == Monday); + assert(wdi2.index() == 2); + + assert(wdi1 == wdi2); + + assert((!weekday_indexed{Sunday, 0}.ok())); + for (unsigned int i = 1; i <= 5; ++i) { + assert((weekday_indexed{Sunday, i}.ok())); + assert((weekday_indexed{Monday, i}.ok())); + assert((weekday_indexed{Tuesday, i}.ok())); + assert((weekday_indexed{Wednesday, i}.ok())); + assert((weekday_indexed{Thursday, i}.ok())); + assert((weekday_indexed{Friday, i}.ok())); + assert((weekday_indexed{Saturday, i}.ok())); + } + assert((!weekday_indexed{Sunday, 6}.ok())); + assert((!weekday_indexed{Sunday, 7}.ok())); +} + +constexpr void weekday_last_test() { + const weekday_last wdl{Monday}; + + static_assert(noexcept(weekday_last{weekday{}})); + + static_assert(noexcept(wdl.weekday())); + static_assert(noexcept(wdl.ok())); + + static_assert(noexcept(wdl == wdl)); + + assert(wdl.ok()); + assert(Monday[last].ok()); + assert(Monday[last].weekday() == Monday); + assert(wdl.weekday() == Monday); + assert(wdl == weekday_last{Monday}); +} + +constexpr void month_day_test() { + const month_day md{January, 1d}; + + static_assert(noexcept(month_day{})); + static_assert(noexcept(month_day{month{}, day{}})); + + static_assert(noexcept(md.month())); + static_assert(noexcept(md.day())); + static_assert(noexcept(md.ok())); + + static_assert(noexcept(md == md)); + static_assert(noexcept(md <=> md)); + + assert(md.month() == January); + assert(md.day() == 1d); + + assert((md < month_day{January, 2d})); + assert((month_day{January, 2d} > md)); + assert((md < month_day{December, 25d})); + assert((month_day{December, 25d} > md)); + assert((md == month_day{January, 1d})); + + if (is_constant_evaluated()) { + static_assert((January / 31).ok()); + static_assert((February / 29).ok()); + static_assert((April / 30).ok()); + static_assert(!(January / 32).ok()); + static_assert(!(February / 30).ok()); + static_assert(!(April / 31).ok()); + } else { + for (unsigned int i = 0; i <= 255; ++i) { + month m{i}; + for (unsigned int d = 0; d <= 255; ++d) { + if (d < 1 || d > 31 || i < 1 || i > 12) { + assert((!month_day{m, day{d}}.ok())); + } else if (d == 30 && m == February) { + assert((!month_day{m, day{d}}.ok())); + } else if (d == 31 && (m == February || m == April || m == June || m == September || m == November)) { + assert((!month_day{m, day{d}}.ok())); + } else { + assert((month_day{m, day{d}}.ok())); + } + } + } + } +} + +constexpr void month_day_last_test() { + const month_day_last mdl{January}; + + static_assert(noexcept(month_day_last{month{}})); + static_assert(noexcept(mdl.month())); + static_assert(noexcept(mdl.ok())); + + static_assert(noexcept(mdl == mdl)); + static_assert(noexcept(mdl <=> mdl)); + + assert((February / last).month() == February); + + assert(!(month{0} / last).ok()); + for (unsigned int i = 1; i <= 12; ++i) { + assert((month{i} / last).ok()); + } + assert(!(month{13} / last).ok()); + + assert(January / last == January / last); + assert(January / last < February / last); + assert(December / last > February / last); +} + +constexpr void month_weekday_test() { + const auto mwd1 = January / Monday[2]; + + static_assert(noexcept(month_weekday{month{}, weekday_indexed{Sunday, 0u}})); + + static_assert(noexcept(mwd1.month())); + static_assert(noexcept(mwd1.weekday_indexed())); + static_assert(noexcept(mwd1.ok())); + + static_assert(noexcept(mwd1 == mwd1)); + + assert(mwd1.month() == January); + assert(mwd1.weekday_indexed().weekday() == Monday); + assert(mwd1.weekday_indexed().index() == 2); + + assert(January / Monday[2] == January / Monday[2]); + + if (is_constant_evaluated()) { + static_assert((January / Monday[1]).ok()); + static_assert((January / Monday[5]).ok()); + static_assert(!(January / Monday[6]).ok()); + static_assert(!(January / Monday[0]).ok()); + static_assert(!(month{0} / Monday[1]).ok()); + } else { + for (auto m = 0u; m <= 255u; ++m) { + for (auto wd = 0u; wd <= 255u; ++wd) { + for (auto wdi = 0u; wdi <= 6u; ++wdi) { + const auto mwd = month{m} / weekday{wd}[wdi]; + if (m >= 1 && m <= 12 && wd <= 7 && wdi >= 1 && wdi <= 5) { + assert(mwd.ok()); + } else { + assert(!mwd.ok()); + } + } + } + } + } +} + +constexpr void month_weekday_last_test() { + const auto mwdl = January / Monday[last]; + + static_assert(noexcept(month_weekday_last{month{}, weekday_last{Sunday}})); + static_assert(noexcept(mwdl.month())); + static_assert(noexcept(mwdl.weekday_last())); + static_assert(noexcept(mwdl.ok())); + + static_assert(noexcept(mwdl == mwdl)); + + assert(mwdl.month() == January); + assert(mwdl.weekday_last().weekday() == Monday); + assert(mwdl == January / Monday[last]); +} + +constexpr void year_month_test() { + auto ym = 2020y / January; + + static_assert(noexcept(year_month{})); + static_assert(noexcept(year_month{year{}, month{}})); + + static_assert(noexcept(ym.year())); + static_assert(noexcept(ym.month())); + + static_assert(noexcept(ym += months{})); + static_assert(noexcept(ym -= months{})); + static_assert(noexcept(ym += years{})); + static_assert(noexcept(ym -= years{})); + + static_assert(noexcept(ym.ok())); + + static_assert(noexcept(ym == ym)); + static_assert(noexcept(ym <=> ym)); + + static_assert(noexcept(ym + months{})); + static_assert(noexcept(months{} + ym)); + static_assert(noexcept(ym - months{})); + + static_assert(noexcept(ym - ym)); + static_assert(noexcept(ym + years{})); + static_assert(noexcept(years{} + ym)); + static_assert(noexcept(ym - years{})); + + assert(ym.year() == 2020y); + assert(ym.month() == January); + + ym += months{2}; + assert(ym.year() == 2020y); + assert(ym.month() == March); + ym -= months{2}; + assert(ym.year() == 2020y); + assert(ym.month() == January); + + ym += years{2}; + assert(ym.year() == 2022y); + assert(ym.month() == January); + ym -= years{2}; + assert(ym.year() == 2020y); + assert(ym.month() == January); + + ym += Decades{2}; + assert(ym.year() == 2040y); + assert(ym.month() == January); + ym -= Decades{2}; + assert(ym.year() == 2020y); + assert(ym.month() == January); + + assert(2020y / April == 2020y / April); + assert(2019y / April < 2020y / April); + assert(2020y / March < 2020y / April); + assert(2020y / April > 2019y / April); + assert(2020y / April > 2020y / March); + + assert(ym + months{2} == 2020y / March); + assert(months{2} + ym == 2020y / March); + + assert(ym - months{2} == 2019y / November); + assert(ym - 2019y / January == months{12}); + + assert(ym + years{2} == 2022y / January); + assert(years{2} + ym == 2022y / January); + + assert(ym - years{2} == 2018y / January); + + assert(ym + Decades{2} == 2040y / January); + assert(Decades{2} + ym == 2040y / January); + + assert(ym - Decades{2} == 2000y / January); + + if (is_constant_evaluated()) { + static_assert((2020y / 1).ok()); + static_assert(!(2020y / 13).ok()); + static_assert(!(32768y / 1).ok()); + } else { + for (int y = y_min - 1; y <= y_max + 1; ++y) { + for (auto m = 0u; m <= 255u; ++m) { + const auto ym2 = year{y} / month{m}; + if (y == y_min - 1 || y == y_max + 1) { + assert(!ym2.ok()); + } else if (m >= 1 && m <= 12) { + assert(ym2.ok()); + } else { + assert(!ym2.ok()); + } + } + } + } +} + +constexpr void year_month_day_test() { + year_month_day ymd1{2020y / January / 1d}; + + static_assert(noexcept(year_month_day{})); + static_assert(noexcept(year_month_day{year{}, month{}, day{}})); + static_assert(noexcept(year_month_day{year_month_day_last{year{}, month_day_last{January}}})); + static_assert(noexcept(year_month_day{sys_days{}})); + static_assert(noexcept(year_month_day{local_days{}})); + + static_assert(noexcept(ymd1 += months{})); + static_assert(noexcept(ymd1 -= months{})); + static_assert(noexcept(ymd1 += years{})); + static_assert(noexcept(ymd1 -= years{})); + + static_assert(noexcept(ymd1.year())); + static_assert(noexcept(ymd1.month())); + static_assert(noexcept(ymd1.day())); + + static_assert(noexcept(static_cast(ymd1))); + static_assert(noexcept(static_cast(ymd1))); + static_assert(noexcept(ymd1.ok())); + + static_assert(noexcept(ymd1 == ymd1)); + static_assert(noexcept(ymd1 <=> ymd1)); + + static_assert(noexcept(ymd1 + months{})); + static_assert(noexcept(months{} + ymd1)); + static_assert(noexcept(ymd1 - months{})); + + static_assert(noexcept(ymd1 + years{})); + static_assert(noexcept(years{} + ymd1)); + static_assert(noexcept(ymd1 - years{})); + + assert(ymd1.year() == 2020y); + assert(ymd1.month() == January); + assert(ymd1.day() == 1d); + + year_month_day ymd2{2020y / January / last}; + assert(ymd2.year() == 2020y); + assert(ymd2.month() == January); + assert(ymd2.day() == 31d); + + year_month_day epoch{sys_days{}}; + assert(epoch == year_month_day{sys_days{epoch}}); + assert(epoch.year() == 1970y); + assert(epoch.month() == January); + assert(epoch.day() == 1d); + + local_days ldp; + sys_days sys{ldp.time_since_epoch()}; + year_month_day ymld{ldp}; + assert(ymld == year_month_day{sys}); + + ymd1 += months{2}; + assert(ymd1.year() == 2020y); + assert(ymd1.month() == March); + assert(ymd1.day() == 1d); + + ymd1 -= months{2}; + assert(ymd1.year() == 2020y); + assert(ymd1.month() == January); + assert(ymd1.day() == 1d); + + ymd1 += years{2}; + assert(ymd1.year() == 2022y); + assert(ymd1.month() == January); + assert(ymd1.day() == 1d); + + ymd1 -= years{2}; + assert(ymd1.year() == 2020y); + assert(ymd1.month() == January); + assert(ymd1.day() == 1d); + + ymd1 += Decades{2}; + assert(ymd1.year() == 2040y); + assert(ymd1.month() == January); + assert(ymd1.day() == 1d); + + ymd1 -= Decades{2}; + assert(ymd1.year() == 2020y); + assert(ymd1.month() == January); + assert(ymd1.day() == 1d); + + assert(2020y / April / 6d == sys_days{days{18'358}}); + assert(sys_days{2017y / January / 0} == 2016y / December / 31); + assert(sys_days{2017y / January / 31} == 2017y / January / 31); + assert(sys_days{2017y / January / 32} == 2017y / February / 1); + + assert(static_cast(ymld) == local_days{}); + + assert(2020y / January / 1d == 2020y / January / 1d); + assert(2019y / January / 1d < 2020y / January / 1d); + assert(2020y / January / 1d < 2020y / February / 1d); + assert(2020y / January / 1d < 2020y / January / 2d); + assert(2020y / January / 1d > 2019y / January / 1d); + assert(2020y / February / 1d > 2020y / January / 1d); + assert(2020y / January / 2d > 2020y / January / 1d); + + const auto ymd3 = 2019y / December / 31d + months{2}; + assert(ymd3 == 2020y / February / 31d); + assert(!ymd3.ok()); + assert(months{2} + 2019y / December / 31d == ymd3); + + + assert(2020y / January / 1d - months{2} == 2019y / November / 1d); + + assert(2020y / January / 1d + years{2} == 2022y / January / 1d); + assert(years{2} + 2020y / January / 1d == 2022y / January / 1d); + + assert(2020y / January / 1d - years{2} == 2018y / January / 1d); + + assert(2020y / January / 1d + Decades{2} == 2040y / January / 1d); + assert(Decades{2} + 2020y / January / 1d == 2040y / January / 1d); + + assert(2020y / January / 1d - Decades{2} == 2000y / January / 1d); + + if (is_constant_evaluated()) { + static_assert(!(-32768y / 1 / 1).ok()); + static_assert(!(32768y / 1 / 1).ok()); + static_assert((2020y / 1 / 1).ok()); + + static_assert(!(2020y / 0 / 1).ok()); + static_assert(!(2020y / 13 / 1).ok()); + static_assert((2020y / 5 / 1).ok()); + + static_assert(!(2020y / 2 / 30).ok()); + static_assert(!(2019y / 2 / 29).ok()); + static_assert(!(2020y / 1 / 0).ok()); + static_assert(!(2020y / 1 / 32).ok()); + static_assert((2020y / 2 / 29).ok()); + static_assert((2020y / 7 / 31).ok()); + } else { + for (int iy = -3000; iy <= 3000; ++iy) { // instead of [y_min, y_max], to limit the number of iterations + for (auto um = 0u; um <= 13u; ++um) { // instead of [0, 255], to limit the number of iterations + for (auto ud = 0u; ud <= 32u; ++ud) { + const year y{iy}; + const month m{um}; + const day d{ud}; + if (y.ok() && m.ok() && d >= 1d && d <= (y / m / last).day()) { + assert((y / m / d).ok()); + } else { + assert(!(y / m / d).ok()); + } + } + } + } + } + + if (is_constant_evaluated()) { + // clang-format off + static_assert(sys_days{ 2000y/ 1/ 1} == sys_days{days{ 10957}}); + static_assert(sys_days{ 2000y/ 1/31} == sys_days{days{ 10987}}); + static_assert(sys_days{ 2000y/ 2/ 1} == sys_days{days{ 10988}}); + static_assert(sys_days{ 2000y/ 2/29} == sys_days{days{ 11016}}); + static_assert(sys_days{ 2000y/ 3/ 1} == sys_days{days{ 11017}}); + static_assert(sys_days{ 2000y/ 3/31} == sys_days{days{ 11047}}); + static_assert(sys_days{ 2000y/ 4/ 1} == sys_days{days{ 11048}}); + static_assert(sys_days{ 2000y/ 4/30} == sys_days{days{ 11077}}); + static_assert(sys_days{ 2000y/ 5/ 1} == sys_days{days{ 11078}}); + static_assert(sys_days{ 2000y/ 5/31} == sys_days{days{ 11108}}); + static_assert(sys_days{ 2000y/ 6/ 1} == sys_days{days{ 11109}}); + static_assert(sys_days{ 2000y/ 6/30} == sys_days{days{ 11138}}); + static_assert(sys_days{ 2000y/ 7/ 1} == sys_days{days{ 11139}}); + static_assert(sys_days{ 2000y/ 7/31} == sys_days{days{ 11169}}); + static_assert(sys_days{ 2000y/ 8/ 1} == sys_days{days{ 11170}}); + static_assert(sys_days{ 2000y/ 8/31} == sys_days{days{ 11200}}); + static_assert(sys_days{ 2000y/ 9/ 1} == sys_days{days{ 11201}}); + static_assert(sys_days{ 2000y/ 9/30} == sys_days{days{ 11230}}); + static_assert(sys_days{ 2000y/10/ 1} == sys_days{days{ 11231}}); + static_assert(sys_days{ 2000y/10/31} == sys_days{days{ 11261}}); + static_assert(sys_days{ 2000y/11/ 1} == sys_days{days{ 11262}}); + static_assert(sys_days{ 2000y/11/30} == sys_days{days{ 11291}}); + static_assert(sys_days{ 2000y/12/ 1} == sys_days{days{ 11292}}); + static_assert(sys_days{ 2000y/12/31} == sys_days{days{ 11322}}); + static_assert(sys_days{ -400y/ 2/29} == sys_days{days{ -865566}}); + static_assert(sys_days{ -400y/ 3/ 1} == sys_days{days{ -865565}}); + static_assert(sys_days{ -1y/ 2/28} == sys_days{days{ -719835}}); + static_assert(sys_days{ -1y/ 3/ 1} == sys_days{days{ -719834}}); + static_assert(sys_days{ -1y/12/31} == sys_days{days{ -719529}}); + static_assert(sys_days{ 0y/ 1/ 1} == sys_days{days{ -719528}}); + static_assert(sys_days{ 0y/ 2/29} == sys_days{days{ -719469}}); + static_assert(sys_days{ 0y/ 3/ 1} == sys_days{days{ -719468}}); + static_assert(sys_days{ 1900y/ 3/ 1} == sys_days{days{ -25508}}); + static_assert(sys_days{ 1901y/ 2/28} == sys_days{days{ -25144}}); + static_assert(sys_days{ 1903y/ 3/ 1} == sys_days{days{ -24413}}); + static_assert(sys_days{ 1904y/ 2/29} == sys_days{days{ -24048}}); + static_assert(sys_days{ 1996y/ 3/ 1} == sys_days{days{ 9556}}); + static_assert(sys_days{ 1997y/ 2/28} == sys_days{days{ 9920}}); + static_assert(sys_days{ 1999y/ 3/ 1} == sys_days{days{ 10651}}); + static_assert(sys_days{ 2000y/ 2/29} == sys_days{days{ 11016}}); + static_assert(sys_days{ 2000y/ 3/ 1} == sys_days{days{ 11017}}); + static_assert(sys_days{ 2001y/ 2/28} == sys_days{days{ 11381}}); + static_assert(sys_days{ 2003y/ 3/ 1} == sys_days{days{ 12112}}); + static_assert(sys_days{ 2004y/ 2/29} == sys_days{days{ 12477}}); + static_assert(sys_days{ 2096y/ 3/ 1} == sys_days{days{ 46081}}); + static_assert(sys_days{ 2097y/ 2/28} == sys_days{days{ 46445}}); + static_assert(sys_days{ 2099y/ 3/ 1} == sys_days{days{ 47176}}); + static_assert(sys_days{ 2100y/ 2/28} == sys_days{days{ 47540}}); + static_assert(sys_days{-32767y/ 1/ 1} == sys_days{days{-12687428}}); + static_assert(sys_days{ 32767y/12/31} == sys_days{days{ 11248737}}); + + static_assert( 2000y/ 1/ 1 == year_month_day{sys_days{days{ 10957}}}); + static_assert( 2000y/ 1/31 == year_month_day{sys_days{days{ 10987}}}); + static_assert( 2000y/ 2/ 1 == year_month_day{sys_days{days{ 10988}}}); + static_assert( 2000y/ 2/29 == year_month_day{sys_days{days{ 11016}}}); + static_assert( 2000y/ 3/ 1 == year_month_day{sys_days{days{ 11017}}}); + static_assert( 2000y/ 3/31 == year_month_day{sys_days{days{ 11047}}}); + static_assert( 2000y/ 4/ 1 == year_month_day{sys_days{days{ 11048}}}); + static_assert( 2000y/ 4/30 == year_month_day{sys_days{days{ 11077}}}); + static_assert( 2000y/ 5/ 1 == year_month_day{sys_days{days{ 11078}}}); + static_assert( 2000y/ 5/31 == year_month_day{sys_days{days{ 11108}}}); + static_assert( 2000y/ 6/ 1 == year_month_day{sys_days{days{ 11109}}}); + static_assert( 2000y/ 6/30 == year_month_day{sys_days{days{ 11138}}}); + static_assert( 2000y/ 7/ 1 == year_month_day{sys_days{days{ 11139}}}); + static_assert( 2000y/ 7/31 == year_month_day{sys_days{days{ 11169}}}); + static_assert( 2000y/ 8/ 1 == year_month_day{sys_days{days{ 11170}}}); + static_assert( 2000y/ 8/31 == year_month_day{sys_days{days{ 11200}}}); + static_assert( 2000y/ 9/ 1 == year_month_day{sys_days{days{ 11201}}}); + static_assert( 2000y/ 9/30 == year_month_day{sys_days{days{ 11230}}}); + static_assert( 2000y/10/ 1 == year_month_day{sys_days{days{ 11231}}}); + static_assert( 2000y/10/31 == year_month_day{sys_days{days{ 11261}}}); + static_assert( 2000y/11/ 1 == year_month_day{sys_days{days{ 11262}}}); + static_assert( 2000y/11/30 == year_month_day{sys_days{days{ 11291}}}); + static_assert( 2000y/12/ 1 == year_month_day{sys_days{days{ 11292}}}); + static_assert( 2000y/12/31 == year_month_day{sys_days{days{ 11322}}}); + static_assert( -400y/ 2/29 == year_month_day{sys_days{days{ -865566}}}); + static_assert( -400y/ 3/ 1 == year_month_day{sys_days{days{ -865565}}}); + static_assert( -1y/ 2/28 == year_month_day{sys_days{days{ -719835}}}); + static_assert( -1y/ 3/ 1 == year_month_day{sys_days{days{ -719834}}}); + static_assert( -1y/12/31 == year_month_day{sys_days{days{ -719529}}}); + static_assert( 0y/ 1/ 1 == year_month_day{sys_days{days{ -719528}}}); + static_assert( 0y/ 2/29 == year_month_day{sys_days{days{ -719469}}}); + static_assert( 0y/ 3/ 1 == year_month_day{sys_days{days{ -719468}}}); + static_assert( 1900y/ 3/ 1 == year_month_day{sys_days{days{ -25508}}}); + static_assert( 1901y/ 2/28 == year_month_day{sys_days{days{ -25144}}}); + static_assert( 1903y/ 3/ 1 == year_month_day{sys_days{days{ -24413}}}); + static_assert( 1904y/ 2/29 == year_month_day{sys_days{days{ -24048}}}); + static_assert( 1996y/ 3/ 1 == year_month_day{sys_days{days{ 9556}}}); + static_assert( 1997y/ 2/28 == year_month_day{sys_days{days{ 9920}}}); + static_assert( 1999y/ 3/ 1 == year_month_day{sys_days{days{ 10651}}}); + static_assert( 2000y/ 2/29 == year_month_day{sys_days{days{ 11016}}}); + static_assert( 2000y/ 3/ 1 == year_month_day{sys_days{days{ 11017}}}); + static_assert( 2001y/ 2/28 == year_month_day{sys_days{days{ 11381}}}); + static_assert( 2003y/ 3/ 1 == year_month_day{sys_days{days{ 12112}}}); + static_assert( 2004y/ 2/29 == year_month_day{sys_days{days{ 12477}}}); + static_assert( 2096y/ 3/ 1 == year_month_day{sys_days{days{ 46081}}}); + static_assert( 2097y/ 2/28 == year_month_day{sys_days{days{ 46445}}}); + static_assert( 2099y/ 3/ 1 == year_month_day{sys_days{days{ 47176}}}); + static_assert( 2100y/ 2/28 == year_month_day{sys_days{days{ 47540}}}); + static_assert(-32767y/ 1/ 1 == year_month_day{sys_days{days{-12687428}}}); + static_assert( 32767y/12/31 == year_month_day{sys_days{days{ 11248737}}}); + + static_assert(sys_days{ 2000y/ 1/ 0} == sys_days{days{ 10956}}); + static_assert(sys_days{ 2000y/ 1/255} == sys_days{days{ 11211}}); + static_assert(sys_days{ 2000y/ 2/ 0} == sys_days{days{ 10987}}); + static_assert(sys_days{ 2000y/ 2/255} == sys_days{days{ 11242}}); + static_assert(sys_days{ 2000y/ 3/ 0} == sys_days{days{ 11016}}); + static_assert(sys_days{ 2000y/ 3/255} == sys_days{days{ 11271}}); + static_assert(sys_days{ 2000y/ 4/ 0} == sys_days{days{ 11047}}); + static_assert(sys_days{ 2000y/ 4/255} == sys_days{days{ 11302}}); + static_assert(sys_days{ 2000y/ 5/ 0} == sys_days{days{ 11077}}); + static_assert(sys_days{ 2000y/ 5/255} == sys_days{days{ 11332}}); + static_assert(sys_days{ 2000y/ 6/ 0} == sys_days{days{ 11108}}); + static_assert(sys_days{ 2000y/ 6/255} == sys_days{days{ 11363}}); + static_assert(sys_days{ 2000y/ 7/ 0} == sys_days{days{ 11138}}); + static_assert(sys_days{ 2000y/ 7/255} == sys_days{days{ 11393}}); + static_assert(sys_days{ 2000y/ 8/ 0} == sys_days{days{ 11169}}); + static_assert(sys_days{ 2000y/ 8/255} == sys_days{days{ 11424}}); + static_assert(sys_days{ 2000y/ 9/ 0} == sys_days{days{ 11200}}); + static_assert(sys_days{ 2000y/ 9/255} == sys_days{days{ 11455}}); + static_assert(sys_days{ 2000y/10/ 0} == sys_days{days{ 11230}}); + static_assert(sys_days{ 2000y/10/255} == sys_days{days{ 11485}}); + static_assert(sys_days{ 2000y/11/ 0} == sys_days{days{ 11261}}); + static_assert(sys_days{ 2000y/11/255} == sys_days{days{ 11516}}); + static_assert(sys_days{ 2000y/12/ 0} == sys_days{days{ 11291}}); + static_assert(sys_days{ 2000y/12/255} == sys_days{days{ 11546}}); + static_assert(sys_days{ -400y/ 2/255} == sys_days{days{ -865340}}); + static_assert(sys_days{ -400y/ 3/ 0} == sys_days{days{ -865566}}); + static_assert(sys_days{ -1y/ 2/255} == sys_days{days{ -719608}}); + static_assert(sys_days{ -1y/ 3/ 0} == sys_days{days{ -719835}}); + static_assert(sys_days{ -1y/12/255} == sys_days{days{ -719305}}); + static_assert(sys_days{ 0y/ 1/ 0} == sys_days{days{ -719529}}); + static_assert(sys_days{ 0y/ 2/255} == sys_days{days{ -719243}}); + static_assert(sys_days{ 0y/ 3/ 0} == sys_days{days{ -719469}}); + static_assert(sys_days{ 1900y/ 3/ 0} == sys_days{days{ -25509}}); + static_assert(sys_days{ 1901y/ 2/255} == sys_days{days{ -24917}}); + static_assert(sys_days{ 1903y/ 3/ 0} == sys_days{days{ -24414}}); + static_assert(sys_days{ 1904y/ 2/255} == sys_days{days{ -23822}}); + static_assert(sys_days{ 1996y/ 3/ 0} == sys_days{days{ 9555}}); + static_assert(sys_days{ 1997y/ 2/255} == sys_days{days{ 10147}}); + static_assert(sys_days{ 1999y/ 3/ 0} == sys_days{days{ 10650}}); + static_assert(sys_days{ 2000y/ 2/255} == sys_days{days{ 11242}}); + static_assert(sys_days{ 2000y/ 3/ 0} == sys_days{days{ 11016}}); + static_assert(sys_days{ 2001y/ 2/255} == sys_days{days{ 11608}}); + static_assert(sys_days{ 2003y/ 3/ 0} == sys_days{days{ 12111}}); + static_assert(sys_days{ 2004y/ 2/255} == sys_days{days{ 12703}}); + static_assert(sys_days{ 2096y/ 3/ 0} == sys_days{days{ 46080}}); + static_assert(sys_days{ 2097y/ 2/255} == sys_days{days{ 46672}}); + static_assert(sys_days{ 2099y/ 3/ 0} == sys_days{days{ 47175}}); + static_assert(sys_days{ 2100y/ 2/255} == sys_days{days{ 47767}}); + static_assert(sys_days{-32767y/ 1/ 0} == sys_days{days{-12687429}}); + static_assert(sys_days{ 32767y/12/255} == sys_days{days{ 11248961}}); + // clang-format on + } else { + sys_days sys2{year{y_min} / 1 / 1}; + + for (int iy = y_min; iy <= y_max; ++iy) { + const year y{iy}; + + for (auto um = 1u; um <= 12u; ++um) { + const month m{um}; + + const year_month_day ymd_first = y / m / 1; + assert(sys_days{ymd_first} == sys2); + assert(year_month_day{sys2} == ymd_first); + + const year_month_day ymd_min = y / m / 0; + assert(sys_days{ymd_min} == sys2 - days{1}); + + const year_month_day ymd_max = y / m / 255; + assert(sys_days{ymd_max} == sys2 + days{254}); + + const year_month_day ymd_last = y / m / last; + sys2 += (ymd_last.day() - 1d); + assert(sys_days{ymd_last} == sys2); + assert(year_month_day{sys2} == ymd_last); + + sys2 += days{1}; + } + } + } +} + +constexpr void year_month_day_last_test() { + auto ymdl = 2020y / February / last; + + static_assert(noexcept(year_month_day_last{year{}, month_day_last{January}})); + + static_assert(noexcept(ymdl += months{})); + static_assert(noexcept(ymdl -= months{})); + static_assert(noexcept(ymdl += years{})); + static_assert(noexcept(ymdl -= years{})); + + static_assert(noexcept(ymdl.year())); + static_assert(noexcept(ymdl.month())); + static_assert(noexcept(ymdl.month_day_last())); + static_assert(noexcept(ymdl.day())); + + static_assert(noexcept(static_cast(ymdl))); + static_assert(noexcept(static_cast(ymdl))); + static_assert(noexcept(ymdl.ok())); + + static_assert(noexcept(ymdl == ymdl)); + static_assert(noexcept(ymdl <=> ymdl)); + + static_assert(noexcept(ymdl + months{})); + static_assert(noexcept(months{} + ymdl)); + static_assert(noexcept(ymdl - months{})); + + static_assert(noexcept(ymdl + years{})); + static_assert(noexcept(years{} + ymdl)); + static_assert(noexcept(ymdl - years{})); + + assert(ymdl == 2020y / February / last); + assert(ymdl == 2020y / February / 29d); + assert(ymdl.year() == 2020y); + assert(ymdl.month() == February); + assert(ymdl.month_day_last() == February / last); + assert(ymdl.day() == 29d); + + ymdl += months{2}; + assert(ymdl == 2020y / April / 30d); + + ymdl -= months{2}; + assert(ymdl == 2020y / February / 29d); + + ymdl += years{2}; + assert(ymdl == 2022y / February / 28d); + + ymdl -= years{2}; + assert(ymdl == 2020y / February / 29d); + + ymdl += Decades{2}; + assert(ymdl == 2040y / February / 29d); + + ymdl -= Decades{2}; + assert(ymdl == 2020y / February / 29d); + + assert(2020y / April / last == sys_days{days{18'382}}); + assert(static_cast(ymdl) == local_days{ymdl}); + + assert(ymdl < 2021y / February / last); + assert(ymdl < 2020y / March / last); + assert(2021y / February / last > ymdl); + assert(2020y / March / last > ymdl); + + assert(ymdl + months{2} == 2020y / April / last); + assert(months{2} + ymdl == 2020y / April / last); + + assert(ymdl - months{2} == 2019y / December / last); + + assert(ymdl + years{2} == 2022y / February / last); + assert(years{2} + ymdl == 2022y / February / last); + + assert(ymdl - years{2} == 2018y / February / last); + + assert(ymdl + Decades{2} == 2040y / February / last); + assert(Decades{2} + ymdl == 2040y / February / last); + + assert(ymdl - Decades{2} == 2000y / February / last); + + if (is_constant_evaluated()) { + static_assert((2020y / 1 / last).ok()); + static_assert(!(2020y / 13 / last).ok()); + static_assert(!(2020y / 0 / last).day().ok()); // implementation-specific assumption, see GH-1647 + } else { + for (int iy = y_min; iy <= y_max; ++iy) { + for (auto m = 0u; m <= 255u; ++m) { + const year y{iy}; + const auto mdl = month{m} / last; + if (y.ok() && mdl.ok()) { + assert((y / mdl).ok()); + } else { + assert(!(y / mdl).ok()); + assert((y / mdl).day().ok() || (y / mdl).day() == day{255}); // implementation-specific assumption + } + } + } + } +} + +constexpr void year_month_weekday_test() { + auto ymwd = 2020y / April / Tuesday[2]; + + static_assert(noexcept(year_month_weekday{})); + static_assert(noexcept(year_month_weekday{year{}, month{}, weekday_indexed{}})); + static_assert(noexcept(year_month_weekday{sys_days{}})); + static_assert(noexcept(year_month_weekday{local_days{}})); + + static_assert(noexcept(ymwd += months{})); + static_assert(noexcept(ymwd -= months{})); + static_assert(noexcept(ymwd += years{})); + static_assert(noexcept(ymwd -= years{})); + + static_assert(noexcept(ymwd.year())); + static_assert(noexcept(ymwd.month())); + static_assert(noexcept(ymwd.weekday())); + static_assert(noexcept(ymwd.index())); + static_assert(noexcept(ymwd.weekday_indexed())); + + static_assert(noexcept(static_cast(ymwd))); + static_assert(noexcept(static_cast(ymwd))); + static_assert(noexcept(ymwd.ok())); + + static_assert(noexcept(ymwd == ymwd)); + + static_assert(noexcept(ymwd + months{})); + static_assert(noexcept(months{} + ymwd)); + static_assert(noexcept(ymwd - months{})); + + static_assert(noexcept(ymwd + years{})); + static_assert(noexcept(years{} + ymwd)); + static_assert(noexcept(ymwd - years{})); + + assert(ymwd == 2020y / April / Tuesday[2]); + assert(ymwd.year() == 2020y); + assert(ymwd.month() == April); + assert(ymwd.weekday() == Tuesday); + assert(ymwd.index() == 2u); + assert(ymwd.weekday_indexed() == Tuesday[2]); + + const year_month_weekday epoch{sys_days{}}; + assert(epoch == year_month_weekday{sys_days{epoch}}); + assert(epoch == 1970y / January / Thursday[1]); + + local_days ldp; + sys_days sys{ldp.time_since_epoch()}; + year_month_weekday ymlwd{ldp}; + assert(ymlwd == year_month_weekday{sys}); + + ymwd += months{2}; + assert(ymwd == 2020y / June / Tuesday[2]); + ymwd -= months{2}; + assert(ymwd == 2020y / April / Tuesday[2]); + + ymwd += years{2}; + assert(ymwd == 2022y / April / Tuesday[2]); + ymwd -= years{2}; + assert(ymwd == 2020y / April / Tuesday[2]); + + ymwd += Decades{2}; + assert(ymwd == 2040y / April / Tuesday[2]); + ymwd -= Decades{2}; + assert(ymwd == 2020y / April / Tuesday[2]); + + assert(static_cast(epoch) == sys_days{}); + const auto previous = 1970y / January / Thursday[0]; + assert(static_cast(previous) == (sys_days{} - days{7})); + assert(static_cast(ymwd) == local_days{ymwd}); + + + assert((2020y / April / Wednesday[5]).ok()); + assert(!(-32768y / April / Wednesday[1]).ok()); + assert(!(2020y / month{0} / Wednesday[1]).ok()); + assert(!(2020y / April / Tuesday[5]).ok()); + + assert(ymwd + months{2} == 2020y / June / Tuesday[2]); + assert(months{2} + ymwd == 2020y / June / Tuesday[2]); + + assert(ymwd - months{2} == 2020y / February / Tuesday[2]); + + assert(ymwd + years{2} == 2022y / April / Tuesday[2]); + assert(years{2} + ymwd == 2022y / April / Tuesday[2]); + + assert(ymwd - years{2} == 2018y / April / Tuesday[2]); + + assert(ymwd + Decades{2} == 2040y / April / Tuesday[2]); + assert(Decades{2} + ymwd == 2040y / April / Tuesday[2]); + + assert(ymwd - Decades{2} == 2000y / April / Tuesday[2]); +} + +constexpr void year_month_weekday_last_test() { + auto ymwdl = 2020y / January / Monday[last]; + + static_assert(noexcept(year_month_weekday_last{year{}, month{}, weekday_last{Sunday}})); + + static_assert(noexcept(ymwdl += months{})); + static_assert(noexcept(ymwdl -= months{})); + static_assert(noexcept(ymwdl += years{})); + static_assert(noexcept(ymwdl -= years{})); + + static_assert(noexcept(ymwdl.year())); + static_assert(noexcept(ymwdl.month())); + static_assert(noexcept(ymwdl.weekday())); + static_assert(noexcept(ymwdl.weekday_last())); + + static_assert(noexcept(static_cast(ymwdl))); + static_assert(noexcept(static_cast(ymwdl))); + static_assert(noexcept(ymwdl.ok())); + + static_assert(noexcept(ymwdl == ymwdl)); + + static_assert(noexcept(ymwdl + months{})); + static_assert(noexcept(months{} + ymwdl)); + static_assert(noexcept(ymwdl - months{})); + + static_assert(noexcept(ymwdl + years{})); + static_assert(noexcept(years{} + ymwdl)); + static_assert(noexcept(ymwdl - years{})); + + assert(ymwdl == 2020y / January / Monday[last]); + assert(ymwdl.year() == 2020y); + assert(ymwdl.month() == January); + assert(ymwdl.weekday() == Monday); + assert(ymwdl.weekday_last() == Monday[last]); + + ymwdl += months{2}; + assert(ymwdl == 2020y / March / Monday[last]); + ymwdl -= months{2}; + assert(ymwdl == 2020y / January / Monday[last]); + + ymwdl += years{2}; + assert(ymwdl == 2022y / January / Monday[last]); + ymwdl -= years{2}; + assert(ymwdl == 2020y / January / Monday[last]); + + ymwdl += Decades{2}; + assert(ymwdl == 2040y / January / Monday[last]); + ymwdl -= Decades{2}; + assert(ymwdl == 2020y / January / Monday[last]); + + assert(static_cast(ymwdl) == sys_days{days{18'288}}); + assert(static_cast(ymwdl) == local_days{ymwdl}); + + assert((2020y / April / Wednesday[last]).ok()); + assert(!(-32768y / April / Wednesday[last]).ok()); + assert(!(2020y / month{0} / Wednesday[last]).ok()); + assert(!(2020y / April / weekday{8}[last]).ok()); + + assert(ymwdl + months{2} == 2020y / March / Monday[last]); + assert(months{2} + ymwdl == 2020y / March / Monday[last]); + + assert(ymwdl - months{2} == 2019y / November / Monday[last]); + + assert(ymwdl + years{2} == 2022y / January / Monday[last]); + assert(years{2} + ymwdl == 2022y / January / Monday[last]); + + assert(ymwdl - years{2} == 2018y / January / Monday[last]); + + assert(ymwdl + Decades{2} == 2040y / January / Monday[last]); + assert(Decades{2} + ymwdl == 2040y / January / Monday[last]); + + assert(ymwdl - Decades{2} == 2000y / January / Monday[last]); +} + +constexpr bool test() { + day_test(); + month_test(); + year_test(); + weekday_test(); + weekday_indexed_test(); + weekday_last_test(); + month_day_test(); + month_day_last_test(); + month_weekday_test(); + month_weekday_last_test(); + year_month_test(); + year_month_day_test(); + year_month_day_last_test(); + year_month_weekday_test(); + year_month_weekday_last_test(); + return true; +} + +int main() { + test(); + static_assert(test()); +} diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_dates_literals/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_dates_literals/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_dates_literals/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/P0355R7_calendars_and_time_zones_dates_literals/test.compile.pass.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_dates_literals/test.compile.pass.cpp new file mode 100644 index 00000000000..f0512320f08 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_dates_literals/test.compile.pass.cpp @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +using namespace std; +using namespace std::chrono; + +int main() {} // COMPILE-ONLY + +static_assert(noexcept(year{} / month{})); +static_assert(noexcept(year{} / 0)); +static_assert(noexcept(month{} / day{})); +static_assert(noexcept(month{} / 0)); +static_assert(noexcept(0 / day{})); +static_assert(noexcept(day{} / month{})); +static_assert(noexcept(day{} / 0)); +static_assert(noexcept(month{} / last)); +static_assert(noexcept(0 / last)); +static_assert(noexcept(last / month{})); +static_assert(noexcept(last / 0)); +static_assert(noexcept(month{} / Sunday[1])); +static_assert(noexcept(0 / Sunday[1])); +static_assert(noexcept(Sunday[1] / month{})); +static_assert(noexcept(Sunday[1] / 0)); +static_assert(noexcept(month{} / Sunday[last])); +static_assert(noexcept(0 / Sunday[last])); +static_assert(noexcept(Sunday[last] / month{})); +static_assert(noexcept(Sunday[last] / 0)); +static_assert(noexcept(year_month{} / day{})); +static_assert(noexcept(year_month{} / 0)); +static_assert(noexcept(year{} / month_day{})); +static_assert(noexcept(0 / month_day{})); +static_assert(noexcept(month_day{} / year{})); +static_assert(noexcept(month_day{} / 0)); +static_assert(noexcept(year_month{} / last)); +static_assert(noexcept(year{} / month_day_last{January})); +static_assert(noexcept(0 / month_day_last{January})); +static_assert(noexcept(month_day_last{January} / year{})); +static_assert(noexcept(month_day_last{January} / 0)); +static_assert(noexcept(year_month{} / Sunday[1])); +static_assert(noexcept(year{} / month_weekday{January, Sunday[1]})); +static_assert(noexcept(0 / month_weekday{January, Sunday[1]})); +static_assert(noexcept(month_weekday{January, Sunday[1]} / year{})); +static_assert(noexcept(month_weekday{January, Sunday[1]} / 0)); +static_assert(noexcept(year_month{} / Sunday[last])); +static_assert(noexcept(year{} / month_weekday_last{January, Sunday[last]})); +static_assert(noexcept(0 / month_weekday_last{January, Sunday[last]})); +static_assert(noexcept(month_weekday_last{January, Sunday[last]} / year{})); +static_assert(noexcept(month_weekday_last{January, Sunday[last]} / 0)); + +#define TRIVIAL_COPY_STANDARD_LAYOUT(TYPE) \ + static_assert(is_trivially_copyable_v, "chrono::" #TYPE " is not trivially copyable"); \ + static_assert(is_standard_layout_v, "chrono::" #TYPE " is not standard-layout"); + +TRIVIAL_COPY_STANDARD_LAYOUT(day) +TRIVIAL_COPY_STANDARD_LAYOUT(month) +TRIVIAL_COPY_STANDARD_LAYOUT(year) +TRIVIAL_COPY_STANDARD_LAYOUT(weekday) +TRIVIAL_COPY_STANDARD_LAYOUT(weekday_indexed) +TRIVIAL_COPY_STANDARD_LAYOUT(weekday_last) +TRIVIAL_COPY_STANDARD_LAYOUT(month_day) +TRIVIAL_COPY_STANDARD_LAYOUT(month_day_last) +TRIVIAL_COPY_STANDARD_LAYOUT(month_weekday) +TRIVIAL_COPY_STANDARD_LAYOUT(month_weekday_last) +TRIVIAL_COPY_STANDARD_LAYOUT(year_month) +TRIVIAL_COPY_STANDARD_LAYOUT(year_month_day) +TRIVIAL_COPY_STANDARD_LAYOUT(year_month_day_last) +TRIVIAL_COPY_STANDARD_LAYOUT(year_month_weekday) +TRIVIAL_COPY_STANDARD_LAYOUT(year_month_weekday_last) + +#define TYPE_ASSERT(TYPE, EXPR) \ + static_assert(is_same_v>, #EXPR " is not chrono::" #TYPE); + +TYPE_ASSERT(day, 0d) +TYPE_ASSERT(year, 0y) +TYPE_ASSERT(month, January) +TYPE_ASSERT(month, February) +TYPE_ASSERT(month, March) +TYPE_ASSERT(month, April) +TYPE_ASSERT(month, May) +TYPE_ASSERT(month, June) +TYPE_ASSERT(month, July) +TYPE_ASSERT(month, August) +TYPE_ASSERT(month, September) +TYPE_ASSERT(month, October) +TYPE_ASSERT(month, November) +TYPE_ASSERT(month, December) +TYPE_ASSERT(weekday, Sunday) +TYPE_ASSERT(weekday, Monday) +TYPE_ASSERT(weekday, Tuesday) +TYPE_ASSERT(weekday, Wednesday) +TYPE_ASSERT(weekday, Thursday) +TYPE_ASSERT(weekday, Friday) +TYPE_ASSERT(weekday, Saturday) +TYPE_ASSERT(last_spec, last) +TYPE_ASSERT(weekday_indexed, declval()[1]) +TYPE_ASSERT(weekday_last, declval()[last]) + +TYPE_ASSERT(year_month, 2020y / January) +TYPE_ASSERT(year_month, 2020y / 1) + +TYPE_ASSERT(month_day, January / 1d) +TYPE_ASSERT(month_day, January / 1) +TYPE_ASSERT(month_day, 1 / 1d) +TYPE_ASSERT(month_day, 1d / January) +TYPE_ASSERT(month_day, 1d / 1) + +TYPE_ASSERT(month_day_last, January / last) +TYPE_ASSERT(month_day_last, 1 / last) +TYPE_ASSERT(month_day_last, last / January) +TYPE_ASSERT(month_day_last, last / 1) + +TYPE_ASSERT(month_weekday, January / Monday[1]) +TYPE_ASSERT(month_weekday, 1 / Monday[1]) +TYPE_ASSERT(month_weekday, Monday[1] / January) +TYPE_ASSERT(month_weekday, Monday[1] / 1) + +TYPE_ASSERT(month_weekday_last, January / Monday[last]) +TYPE_ASSERT(month_weekday_last, 1 / Monday[last]) +TYPE_ASSERT(month_weekday_last, Monday[last] / January) +TYPE_ASSERT(month_weekday_last, Monday[last] / 1) + +TYPE_ASSERT(year_month_day, 2020y / January / 1d) +TYPE_ASSERT(year_month_day, 2020y / January / 1) +constexpr auto md = January / 1; +TYPE_ASSERT(year_month_day, 2020y / md) +TYPE_ASSERT(year_month_day, 2020 / md) +TYPE_ASSERT(year_month_day, January / 1 / 2020y) +TYPE_ASSERT(year_month_day, January / 1 / 2020) +TYPE_ASSERT(year_month_day, 1d / January / 2020y) +TYPE_ASSERT(year_month_day, 1d / January / 2020) +TYPE_ASSERT(year_month_day, 1d / 1 / 2020) +TYPE_ASSERT(year_month_day, 1d / 1 / 2020y) + +TYPE_ASSERT(year_month_day_last, 2020y / January / last) +constexpr auto mdl = January / last; +TYPE_ASSERT(year_month_day_last, 2020y / mdl) +TYPE_ASSERT(year_month_day_last, 2020 / mdl) +TYPE_ASSERT(year_month_day_last, last / January / 2020y) +TYPE_ASSERT(year_month_day_last, January / last / 2020) + +TYPE_ASSERT(year_month_weekday, 2020y / January / Monday[1]) +constexpr auto mwd = January / Monday[1]; +TYPE_ASSERT(year_month_weekday, 2020y / mwd) +TYPE_ASSERT(year_month_weekday, 2020 / mwd) +TYPE_ASSERT(year_month_weekday, January / Monday[1] / 2020y) +TYPE_ASSERT(year_month_weekday, January / Monday[1] / 2020) +TYPE_ASSERT(year_month_weekday, Monday[1] / January / 2020y) +TYPE_ASSERT(year_month_weekday, Monday[1] / January / 2020) + +TYPE_ASSERT(year_month_weekday_last, 2020y / January / Monday[last]) +constexpr auto mwdl = January / Monday[last]; +TYPE_ASSERT(year_month_weekday_last, 2020y / mwdl) +TYPE_ASSERT(year_month_weekday_last, 2020 / mwdl) +TYPE_ASSERT(year_month_weekday_last, January / Monday[last] / 2020y) +TYPE_ASSERT(year_month_weekday_last, January / Monday[last] / 2020) +TYPE_ASSERT(year_month_weekday_last, 1 / Monday[last] / 2020) +TYPE_ASSERT(year_month_weekday_last, 1 / Monday[last] / 2020y) +TYPE_ASSERT(year_month_weekday_last, Monday[last] / 1 / 2020y) +TYPE_ASSERT(year_month_weekday_last, Monday[last] / 1 / 2020) +TYPE_ASSERT(year_month_weekday_last, Monday[last] / January / 2020) +TYPE_ASSERT(year_month_weekday_last, Monday[last] / January / 2020y) + +#define VALUE_ASSERT(VALUE, EXPECTED) static_assert(VALUE == EXPECTED, "chrono::" #VALUE " is not " #EXPECTED); +VALUE_ASSERT(month{1}, January) +VALUE_ASSERT(month{2}, February) +VALUE_ASSERT(month{3}, March) +VALUE_ASSERT(month{4}, April) +VALUE_ASSERT(month{5}, May) +VALUE_ASSERT(month{6}, June) +VALUE_ASSERT(month{7}, July) +VALUE_ASSERT(month{8}, August) +VALUE_ASSERT(month{9}, September) +VALUE_ASSERT(month{10}, October) +VALUE_ASSERT(month{11}, November) +VALUE_ASSERT(month{12}, December) + +VALUE_ASSERT(weekday{0}, Sunday) +VALUE_ASSERT(weekday{1}, Monday) +VALUE_ASSERT(weekday{2}, Tuesday) +VALUE_ASSERT(weekday{3}, Wednesday) +VALUE_ASSERT(weekday{4}, Thursday) +VALUE_ASSERT(weekday{5}, Friday) +VALUE_ASSERT(weekday{6}, Saturday) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/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/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp new file mode 100644 index 00000000000..05d0320e311 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -0,0 +1,225 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +using hms_hours = hh_mm_ss; +using f_hours = duration>; +using f_hms_hours = hh_mm_ss; + +constexpr void am_pm() { + static_assert(noexcept(is_am(hours{}))); + static_assert(noexcept(is_pm(hours{}))); + + for (hours i = 0h; i < 12h; ++i) { + assert(is_am(i)); + assert(!is_pm(i)); + } + for (hours i = 12h; i < 24h; ++i) { + assert(!is_am(i)); + assert(is_pm(i)); + } +} + +constexpr void make12_24() { + static_assert(noexcept(make12(hours{}))); + static_assert(noexcept(make24(hours{}, true))); + + assert(make12(1h) == 1h); + assert(make12(13h) == 1h); + assert(make12(0h) == 12h); + assert(make12(12h) == 12h); + + assert(make24(12h, true) == 12h); + assert(make24(12h, false) == 0h); + assert(make24(5h, true) == 17h); + assert(make24(5h, false) == 5h); +} + +template +constexpr auto width() { + return hh_mm_ss::fractional_width; +} +template +constexpr auto width() { + return hh_mm_ss>>::fractional_width; +} + +constexpr void fractional_width() { + static_assert(width() == 0); + static_assert(width() == 0); + static_assert(width() == 0); + static_assert(width() == 0); + static_assert(width() == 3); + static_assert(width() == 6); + static_assert(width() == 9); + + static_assert(width() == 1); + static_assert(width() == 6); + static_assert(width() == 2); + static_assert(width() == 1); + static_assert(width() == 6); + static_assert(width() == 6); + static_assert(width() == 3); + static_assert(width() == 6); + static_assert(width() == 1); + static_assert(width() == 4); + static_assert(width() == 3); + static_assert(width() == 6); + // static_assert(width() == 6); overflows + + static_assert(width() == 1); + static_assert(width() == 6); + static_assert(width() == 2); + static_assert(width() == 1); + static_assert(width() == 6); + static_assert(width() == 6); + static_assert(width() == 3); + static_assert(width() == 6); + static_assert(width() == 1); + static_assert(width() == 4); + static_assert(width() == 3); + static_assert(width() == 6); + // static_assert(width() == 6); overflows +} + +constexpr void constructor() { + static_assert(noexcept(hms_hours{})); + static_assert(noexcept(f_hms_hours{})); + + assert(hms_hours{}.hours() == hms_hours{hours::zero()}.hours()); + assert(hms_hours{}.minutes() == hms_hours{hours::zero()}.minutes()); + assert(hms_hours{}.seconds() == hms_hours{hours::zero()}.seconds()); + assert(hms_hours{}.subseconds() == hms_hours{hours::zero()}.subseconds()); + + assert(f_hms_hours{}.hours() == f_hms_hours{hours::zero()}.hours()); + assert(f_hms_hours{}.minutes() == f_hms_hours{hours::zero()}.minutes()); + assert(f_hms_hours{}.seconds() == f_hms_hours{hours::zero()}.seconds()); + assert(f_hms_hours{}.subseconds() == f_hms_hours{hours::zero()}.subseconds()); +} + +constexpr void is_negative() { + static_assert(noexcept(hms_hours{}.is_negative())); + static_assert(noexcept(f_hms_hours{}.is_negative())); + + assert(hh_mm_ss(days{-1}).is_negative()); + assert(!hh_mm_ss(days{1}).is_negative()); + + assert(hh_mm_ss{-1h}.is_negative()); + assert(!hh_mm_ss{1h}.is_negative()); + + assert(hh_mm_ss{-1min}.is_negative()); + assert(!hh_mm_ss{1min}.is_negative()); + + assert(hh_mm_ss{-1s}.is_negative()); + assert(!hh_mm_ss{1s}.is_negative()); + + assert(hh_mm_ss{-1ms}.is_negative()); + assert(!hh_mm_ss{1ms}.is_negative()); + + assert(hh_mm_ss{-1us}.is_negative()); + assert(!hh_mm_ss{1us}.is_negative()); + + assert(hh_mm_ss{-1ns}.is_negative()); + assert(!hh_mm_ss{1ns}.is_negative()); + + assert(f_hms_hours{f_hours{-1.f}}.is_negative()); + assert(!f_hms_hours{f_hours{1.f}}.is_negative()); +} + +constexpr auto ones = 1h + 1min + 1s + 1ms; + +constexpr void hour() { + static_assert(noexcept(hms_hours{}.hours())); + static_assert(noexcept(f_hms_hours{}.hours())); + + assert(hh_mm_ss(days{1}).hours() == 24h); + assert(hh_mm_ss(ones).hours() == 1h); + assert(hh_mm_ss(-ones).hours() == 1h); + assert(hh_mm_ss(59min).hours() == 0h); + assert(f_hms_hours{f_hours{1.f}}.hours() == 1h); +} + +constexpr void mins() { + static_assert(noexcept(hms_hours{}.minutes())); + static_assert(noexcept(f_hms_hours{}.minutes())); + + assert(hh_mm_ss(ones).minutes() == 1min); + assert(hh_mm_ss(-ones).minutes() == 1min); + assert(hh_mm_ss(59s).minutes() == 0min); + assert(f_hms_hours{f_hours{0.0166667f}}.minutes() == 1min); +} + +constexpr void secs() { + static_assert(noexcept(hms_hours{}.seconds())); + static_assert(noexcept(f_hms_hours{}.seconds())); + + assert(hh_mm_ss(ones).seconds() == 1s); + assert(hh_mm_ss(-ones).seconds() == 1s); + assert(hh_mm_ss(999ms).seconds() == 0s); + assert(f_hms_hours{f_hours{0.000277778f}}.seconds() == 1s); +} + +constexpr void subsecs() { + static_assert(noexcept(hms_hours{}.subseconds())); + static_assert(noexcept(f_hms_hours{}.subseconds())); + + assert(hh_mm_ss(ones).subseconds() == 1ms); + assert(hh_mm_ss(-ones).subseconds() == 1ms); + assert(hh_mm_ss(999us).subseconds() == 999us); + assert(hh_mm_ss(duration_cast(999us)).subseconds() == 0ms); + using f_hms_milli = hh_mm_ss>; + assert(f_hms_milli{1ms}.subseconds() == 1ms); +} + +constexpr void to_duration() { + using precision = hms_hours::precision; + using f_precision = f_hms_hours::precision; + + static_assert(noexcept(hms_hours{}.to_duration())); + static_assert(noexcept(static_cast(hms_hours{}))); + static_assert(noexcept(f_hms_hours{}.to_duration())); + static_assert(noexcept(static_cast(f_hms_hours{}))); + + assert(hh_mm_ss(ones).to_duration() == ones); + assert(hh_mm_ss(-ones).to_duration() == -ones); + assert(f_hms_hours{f_hours{1.f}}.to_duration() == 1h); + assert(f_hms_hours{f_hours{-1.f}}.to_duration() == -1h); + + hh_mm_ss hms(50ms); + milliseconds milli_val = static_cast(hms); + static_assert(is_same_v); + assert(hms.to_duration() == milli_val); + + f_hms_hours fhms{f_hours{1}}; + auto fhours_val = static_cast(fhms); + static_assert(is_same_v); + static_assert(is_same_v, f_precision>); + assert(fhms.to_duration() == fhours_val); +} + +constexpr bool test() { + am_pm(); + make12_24(); + fractional_width(); + constructor(); + is_negative(); + hour(); + mins(); + secs(); + subsecs(); + to_duration(); + return true; +} + +int main() { + test(); + static_assert(test()); +} diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_time_point_and_durations/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_time_point_and_durations/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_point_and_durations/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/P0355R7_calendars_and_time_zones_time_point_and_durations/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_time_point_and_durations/test.cpp new file mode 100644 index 00000000000..66bec4ea7da --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_point_and_durations/test.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +#define DURATION_TEST(TYPE, BITS, ...) \ + static_assert( \ + is_integral_v && is_signed_v, "chrono::" #TYPE "::rep is not a signed integral type."); \ + static_assert( \ + numeric_limits::digits >= BITS, "chrono::" #TYPE "::rep is not at least " #BITS " bits."); \ + static_assert(is_same_v, "chrono::" #TYPE "::period is not " #__VA_ARGS__ "."); + +DURATION_TEST(days, 25, ratio_multiply, hours::period>) +DURATION_TEST(weeks, 22, ratio_multiply, days::period>) +DURATION_TEST(months, 20, ratio_divide>) +DURATION_TEST(years, 17, ratio_multiply, days::period>) + +// clang-format off +static_assert(is_same_v>, + "sys_seconds is not time_point."); +static_assert(is_same_v>, + "sys_days is not time_point."); + +static_assert(is_same_v>, + "local_seconds is not time_point."); +static_assert(is_same_v>, + "local_days is not time_point."); +// clang-format on + +constexpr bool test() { + steady_clock::time_point tp1; + + static_assert(noexcept(++tp1)); // strengthened + static_assert(noexcept(tp1++)); // strengthened + static_assert(noexcept(--tp1)); // strengthened + static_assert(noexcept(tp1--)); // strengthened + + auto tp2 = tp1++; + assert(tp1.time_since_epoch().count() == 1); + assert(tp2.time_since_epoch().count() == 0); + tp2 = ++tp1; + assert(tp1.time_since_epoch().count() == 2); + assert(tp2.time_since_epoch().count() == 2); + + tp2 = tp1--; + assert(tp1.time_since_epoch().count() == 1); + assert(tp2.time_since_epoch().count() == 2); + tp2 = --tp1; + assert(tp1.time_since_epoch().count() == 0); + assert(tp2.time_since_epoch().count() == 0); + + return true; +} + +int main() { + test(); + static_assert(test()); +} diff --git a/tests/std/tests/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/env.lst b/tests/std/tests/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/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/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/test.cpp b/tests/std/tests/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/test.cpp new file mode 100644 index 00000000000..78309c6cc21 --- /dev/null +++ b/tests/std/tests/P0466R5_layout_compatibility_and_pointer_interconvertibility_traits/test.cpp @@ -0,0 +1,254 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include +#include + +using namespace std; + +#define ASSERT(...) assert((__VA_ARGS__)) + +struct S { // Must be declared at namespace scope due to static data member + static int s1; + int v1; + int v2; +}; + +constexpr bool test() { +#ifndef __EDG__ // TRANSITION, VSO-1268984 +#ifndef __clang__ // TRANSITION, LLVM-48860 + // is_layout_compatible tests + { + struct S0 { + int v1; + int v2; + }; + + struct S1 { + S0 s1; + int v3; + }; + + struct S2 { + S0 s1; + int v2; + }; + + struct S3 { + S0 s1; + int v2; + int v3; + }; + + struct S4 { + int v1; + + private: + int v2; + }; + + struct S5 { + int v1; + + private: + int v2; + }; + + enum E1 { e1, e2, e3, e4 }; + enum E2 : int { e5 }; + enum E3 : unsigned int { e6, e7, e8 }; + enum class E4 : unsigned int { no, yes }; + enum class E5 { zero, fortytwo = 42 }; + + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1269781 + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); + ASSERT(is_layout_compatible_v); +#endif // TRANSITION, VSO-1269781 + + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + ASSERT(!is_layout_compatible_v); + } + + // is_pointer_interconvertible_base_of tests + { + class A {}; + class B : public A {}; + class C : public A { + int : 0; + }; + class D : public C {}; +// Disable warning C4408: anonymous union did not declare any data members +#pragma warning(push) +#pragma warning(disable : 4408) + class E : public A { + union {}; + }; +#pragma warning(pop) + class F : private A {}; // Non-public inheritance + class NS : public B, public C {}; // Non-standard layout + class I; // Incomplete + + union U { + int i; + char c; + }; + + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + ASSERT(is_pointer_interconvertible_base_of_v); + + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + ASSERT(!is_pointer_interconvertible_base_of_v); + } + + // is_corresponding_member tests + { + struct S1 { + int v1; + int v2; + }; + + struct S2 { + int w1; + int w2; + }; + + struct S3 { + int v1; + int v2; + int v3; + }; + + struct S4 { + char v1; + int v2; + int v3; + }; + + struct S5 { + int v1; + int v2; + void* v3; + }; + + struct S6 { + int v1; + int v2; + double v3; + }; + + struct S7 { + int f1() { + return 0; + } + }; + + struct NS : S1, S2 {}; // Non-standard layout + + ASSERT(is_corresponding_member(&S1::v1, &S::v1)); + ASSERT(is_corresponding_member(&S1::v2, &S::v2)); + ASSERT(is_corresponding_member(&S1::v1, &S1::v1)); + ASSERT(is_corresponding_member(&S1::v2, &S1::v2)); + ASSERT(is_corresponding_member(&S1::v1, &S2::w1)); + ASSERT(is_corresponding_member(&S1::v2, &S2::w2)); + ASSERT(is_corresponding_member(&S1::v1, &S3::v1)); + ASSERT(is_corresponding_member(&S1::v2, &S3::v2)); + ASSERT(is_corresponding_member(&S5::v1, &S6::v1)); + ASSERT(is_corresponding_member(&S5::v2, &S6::v2)); + + ASSERT(!is_corresponding_member(&S1::v1, &S1::v2)); + ASSERT(!is_corresponding_member(&S1::v2, &S1::v1)); + ASSERT(!is_corresponding_member(&S1::v2, &S2::w1)); + ASSERT(!is_corresponding_member(&S1::v1, &S4::v1)); + ASSERT(!is_corresponding_member(&S1::v2, &S4::v2)); + ASSERT(!is_corresponding_member(&S3::v1, &S4::v1)); + ASSERT(!is_corresponding_member(&S3::v2, &S4::v2)); + ASSERT(!is_corresponding_member(&S5::v1, &S6::v2)); + ASSERT(!is_corresponding_member(&S5::v2, &S6::v1)); + ASSERT(!is_corresponding_member(&S5::v3, &S6::v3)); + ASSERT(!is_corresponding_member(&NS::v1, &NS::w1)); + ASSERT(!is_corresponding_member(&S7::f1, &S7::f1)); + ASSERT(!is_corresponding_member(static_cast(nullptr), static_cast(nullptr))); + ASSERT(!is_corresponding_member(&S1::v1, static_cast(nullptr))); + } + + // is_pointer_interconvertible_with_class tests + { + struct A { + int a; + }; + + struct B { + int b; + }; + + struct C { + int f1() { + return 0; + } + }; + + struct NS : A, B {}; // Non-standard layout + + union U { + int v1; + char v2; + }; + + ASSERT(is_pointer_interconvertible_with_class(&A::a)); + ASSERT(is_pointer_interconvertible_with_class(&NS::b)); + ASSERT(is_pointer_interconvertible_with_class(&U::v1)); + ASSERT(is_pointer_interconvertible_with_class(&U::v2)); + + ASSERT(!is_pointer_interconvertible_with_class(&NS::a)); + ASSERT(!is_pointer_interconvertible_with_class(&NS::b)); + ASSERT(!is_pointer_interconvertible_with_class(&C::f1)); + ASSERT(!is_pointer_interconvertible_with_class(static_cast(nullptr))); + } +#endif // __clang__ +#endif // __EDG__ + return true; +} + +int main() { + static_assert(test()); + test(); +} diff --git a/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/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/P0475R1_P0591R4_uses_allocator_construction/test.cpp b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp new file mode 100644 index 00000000000..8c5924c5412 --- /dev/null +++ b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +void test_P0475R1() { + struct DoNotCopy { + DoNotCopy() = default; + DoNotCopy(const DoNotCopy&) { + assert(false); + } + }; + + struct X { + using allocator_type = allocator; + X(DoNotCopy&&, const allocator_type&) {} + }; + + scoped_allocator_adaptor>> alloc; + auto ptr = alloc.allocate(1); + alloc.construct(ptr, piecewise_construct, tuple{}, make_tuple(1)); + alloc.destroy(ptr); + alloc.deallocate(ptr, 1); +} + +constexpr bool test_P0591R4() { + allocator alloc; + int i = 5; + pair p(i, i); + + struct AllocatorArgConstructible { + using allocator_type = allocator; + + constexpr AllocatorArgConstructible(allocator_arg_t, const allocator&, int y) : x(y) {} + + int x; + }; + + struct AllocatorConstructible { + using allocator_type = allocator; + + constexpr AllocatorConstructible(int y, const allocator&) : x(y) {} + + int x; + }; + + struct OnlyAllocatorArgConstructible { + using allocator_type = allocator; + + constexpr OnlyAllocatorArgConstructible(allocator_arg_t, const allocator&) {} + }; + + struct OnlyAllocatorConstructible { + using allocator_type = allocator; + + constexpr OnlyAllocatorConstructible(const allocator&) {} + }; + + struct DefaultConstructible { + constexpr DefaultConstructible() {} + }; + + using AllocatorArgConstructArgs = tuple&, int&>; + using AllocatorConstructArgs = tuple&>; + using ConstAllocatorArgConstructArgs = tuple&, const int&>; + using ConstAllocatorConstructArgs = tuple&>; + using MovedAllocatorArgConstructArgs = tuple&, int&&>; + using MovedAllocatorConstructArgs = tuple&>; + using OnlyAllocatorArgConstructArgs = tuple&>; + using OnlyAllocatorConstructArgs = tuple&>; + using DefaultConstructArgs = tuple<>; + + { // non-pair overload + auto tuple1 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v>); + + auto tuple2 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v); + + auto tuple3 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v); + } + + { // pair(piecewise_construct_t, tuple, tuple) overload + auto tuple4 = uses_allocator_construction_args>( + alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple()); + static_assert( + is_same_v, OnlyAllocatorArgConstructArgs>>); + + auto tuple5 = uses_allocator_construction_args>( + alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple()); + static_assert( + is_same_v>); + } + + { // pair() overload + auto tuple6 = + uses_allocator_construction_args>(alloc); + static_assert(is_same_v>); + + auto tuple7 = uses_allocator_construction_args>(alloc); + static_assert(is_same_v>); + } + + { // pair(first, second) overload + auto tuple8 = uses_allocator_construction_args>(alloc, i, i); + static_assert( + is_same_v, AllocatorArgConstructArgs>>); + + auto tuple9 = uses_allocator_construction_args>(alloc, i, i); + static_assert(is_same_v>>); + } + + { // pair(const pair&) overload + auto tuple10 = uses_allocator_construction_args>(alloc, p); + static_assert(is_same_v, ConstAllocatorArgConstructArgs>>); + + auto tuple11 = uses_allocator_construction_args>(alloc, p); + static_assert( + is_same_v>>); + } + + { // pair(pair&&) overload + auto tuple12 = uses_allocator_construction_args>(alloc, move(p)); + static_assert( + is_same_v, MovedAllocatorArgConstructArgs>>); + + auto tuple13 = uses_allocator_construction_args>(alloc, move(p)); + static_assert( + is_same_v>>); + } + + { + auto obj1 = make_obj_using_allocator(alloc, i); + static_assert(is_same_v); + assert(obj1.x == i); + + auto obj2 = make_obj_using_allocator(alloc, i); + static_assert(is_same_v); + assert(obj2.x == i); + } + + { + allocator alloc2; + auto ptr2 = alloc2.allocate(1); + + uninitialized_construct_using_allocator(ptr2, alloc, i); + assert(ptr2->x == i); + destroy_at(ptr2); + + alloc2.deallocate(ptr2, 1); + + allocator alloc3; + auto ptr3 = alloc3.allocate(1); + + uninitialized_construct_using_allocator(ptr3, alloc, i); + assert(ptr3->x == i); + destroy_at(ptr3); + + alloc3.deallocate(ptr3, 1); + } + + return true; +} + +int main() { + test_P0475R1(); + + assert(test_P0591R4()); + static_assert(test_P0591R4()); +} diff --git a/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst b/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0608R3_improved_variant_converting_constructor/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/P0608R3_improved_variant_converting_constructor/test.cpp b/tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp new file mode 100644 index 00000000000..2d407f7d1e5 --- /dev/null +++ b/tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Also tests for P1957R2: Converting from T* to bool should be considered narrowing + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct double_double { + double_double(double x) : x_(x) {} + + double x_; +}; +struct convertible_bool { + convertible_bool(bool x) : x_(x) {} + ~convertible_bool() = default; + + operator bool() const noexcept { + return x_; + } + + bool x_; +}; +struct default_struct {}; + +void assert_P0608R3() { + // P0608R3 examples + static_assert(is_constructible_v, const char*>); + static_assert(is_constructible_v, string>); + static_assert(is_constructible_v>, char16_t>); + static_assert(is_constructible_v>, double&>); + static_assert(is_constructible_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, long long>, int>); +#endif // !__EDG__ + static_assert(is_constructible_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(!is_constructible_v, int>); + static_assert(!is_constructible_v>, int>); +#endif // !__EDG__ + static_assert(!is_constructible_v, int>); + + static_assert(is_assignable_v, const char*>); + static_assert(is_assignable_v, string>); + static_assert(is_assignable_v>, char16_t>); + static_assert(is_assignable_v>, double&>); + static_assert(is_assignable_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, long long>, int>); +#endif // !__EDG__ + static_assert(is_assignable_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(!is_assignable_v, int>); + static_assert(!is_assignable_v>, int>); +#endif // !__EDG__ + static_assert(!is_assignable_v, int>); +} + +void assert_P1957R2() { + // P1957R2 examples + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, bitset<4>::reference>); + static_assert(is_constructible_v, bitset<4>::reference>); + + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, bitset<4>::reference>); + static_assert(is_assignable_v, bitset<4>::reference>); +} + +void assert_more_examples() { + // More examples + static_assert(is_constructible_v, double>); + static_assert(is_constructible_v>, optional, int>, int>); + static_assert(is_constructible_v>, optional>, int>); + static_assert(is_constructible_v, optional, float>, int>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, bool>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 +#ifdef __clang__ // TRANSITION, DevCom-1338628 + static_assert(is_constructible_v, int>); +#endif // __clang__ + static_assert(!is_constructible_v, unsigned int>); + static_assert(!is_constructible_v, int>); +#endif // !__EDG__ + static_assert(!is_constructible_v, int>); + + static_assert(is_assignable_v, double>); + static_assert(is_assignable_v>, optional, int>, int>); + static_assert(is_assignable_v>, optional>, int>); + static_assert(is_assignable_v, optional, float>, int>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, bool>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 +#ifdef __clang__ // TRANSITION, DevCom-1338628 + static_assert(is_assignable_v, int>); +#endif // __clang__ + static_assert(!is_assignable_v, unsigned int>); + static_assert(!is_assignable_v, int>); +#endif // !__EDG__ + static_assert(!is_assignable_v, int>); +} + +void test_variant_constructor_P0608R3() { + // P0608R3 runtime checks + variant a = "abc"; // string + assert(a.index() == 0); + assert(get<0>(a) == "abc"); + + variant> b = u'\u2043'; // optional + assert(b.index() == 1); + assert(get>(b) == u'\u2043'); + + double c_data = 3.14; + variant> c = c_data; // reference_wrapper + assert(c.index() == 1); + assert(get<1>(c) == c_data); + + variant d; + assert(d.index() == 0); + d = 0; // int + assert(d.index() == 1); + + variant e; + assert(e.index() == 0); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + e = 0; // long + assert(e.index() == 1); +#endif // !__EDG__ + + variant f = 'a'; // int + assert(f.index() == 1); + assert(get(f) == 97); + +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + variant g = 0; // long + assert(g.index() == 1); + + variant h = 0; // long + assert(h.index() == 1); + + variant, long long> i = 0; // long long + assert(i.index() == 2); +#endif // !__EDG__ + + variant j = 'a'; // int + assert(j.index() == 1); + assert(get(j) == 97); +} + +void test_variant_constructor_P1957R2() { + bitset<4> a_bitset("0101"); + bool a_data = a_bitset[2]; + variant a = a_data; // bool + assert(a.index() == 0); + assert(get<0>(a)); + + bitset<4> b_bitset("0101"); + variant b = b_bitset[2]; // bool + variant b2 = b_bitset[1]; // bool + assert(b.index() == 0); + assert(get<0>(b)); + assert(b2.index() == 0); + assert(!get<0>(b2)); +} + +void test_variant_constructor_more_examples() { + variant> a = true; // bool + assert(a.index() == 3); + + variant b = convertible_bool{true}; // bool + assert(b.index() == 0); + assert(get<0>(b)); + + variant c = false; // bool + assert(c.index() == 2); + + variant d = convertible_bool{true}; // convertible_bool + assert(d.index() == 2); + + variant e = bool{}; // bool + assert(e.index() == 1); + assert(!get<1>(e)); + + variant f = convertible_bool{false}; // bool + assert(f.index() == 1); + assert(!get<1>(f)); + + variant g = true_type{}; // bool + assert(g.index() == 0); + assert(get<0>(g)); +} + +void test_assignment_operator() { + variant a; // string + assert(a.index() == 0); + assert(get(a) == ""); + a = 3; // int + assert(a.index() == 2); + assert(get(a) == 3); + a = true; // bool + assert(a.index() == 1); + assert(get(a) == true); + +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + bool b_data = true; + variant b = b_data; // bool + assert(b.index() == 0); + assert(get<0>(b) == b_data); + b = 12; // int + assert(b.index() == 1); + assert(get<1>(b) == 12); + b = 12.5; // double_double + assert(b.index() == 2); + assert(get<2>(b).x_ == 12.5); +#endif // !__EDG__ + +#ifdef __clang__ // TRANSITION, DevCom-1338628 + variant c; + assert(c.index() == 0); + c = false; // bool + assert(c.index() == 1); + assert(get<1>(c) == false); + c = 5.12; // double_double + assert(c.index() == 2); + assert(get<2>(c).x_ == 5.12); + double_double c_data{1.2}; + c = static_cast(&c_data); // void* + assert(c.index() == 0); + assert(static_cast(get<0>(c))->x_ == 1.2); +#endif // __clang__ +} + +int main() { + assert_P0608R3(); + assert_P1957R2(); + assert_more_examples(); + test_variant_constructor_P0608R3(); + test_variant_constructor_P1957R2(); + test_variant_constructor_more_examples(); + test_assignment_operator(); +} diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/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/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp new file mode 100644 index 00000000000..f58fde52393 --- /dev/null +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +class string_buffer : public basic_streambuf> { // represents the wrapped object in syncbuf +public: + string_buffer() = default; + ~string_buffer() = default; + + streamsize xsputn(const Ty* ptr, streamsize n) override { + str.append(ptr, static_cast(n)); + return n; + } + + int sync() override { + if constexpr (ThrowOnSync) { + return -1; + } else { + return 0; + } + } + + string str; +}; + + +template +void test_osyncstream_manipulators(string_buffer* buf = nullptr) { + using char_type = typename Ty::char_type; + using traits_type = typename Ty::traits_type; + + static_assert(is_base_of_v, Ty>); + + Ty os{buf}; + os << "Some input"; + + assert(addressof(emit_on_flush(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + auto* aSyncbuf = static_cast*>(os.rdbuf()); + assert(aSyncbuf->_Stl_internal_check_get_emit_on_sync() == true); + if (buf) { + assert(buf->str == ""); + } + } + + assert(addressof(flush_emit(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + if constexpr (ThrowOnSync) { + assert(os.rdstate() == ios::badbit); + } else { + assert(os.rdstate() == (buf ? ios::goodbit : ios::badbit)); + } + + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + os.clear(); + os << "Another input"; + } + + assert(addressof(noemit_on_flush(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + auto* aSyncbuf = static_cast*>(os.rdbuf()); + assert(aSyncbuf->_Stl_internal_check_get_emit_on_sync() == false); + } + + assert(addressof(flush_emit(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + assert(os.rdstate() == ios::goodbit); + if (buf) { + assert(buf->str == "Another input"); + } + os.clear(); + } + + if (buf) { + buf->str.clear(); + } +} + +int main() { + string_buffer char_buffer{}; + string_buffer no_sync_char_buffer{}; + + test_osyncstream_manipulators>(); + test_osyncstream_manipulators, allocator>, allocator>(); + + test_osyncstream_manipulators>(&char_buffer); + test_osyncstream_manipulators, allocator>, allocator>( + &char_buffer); + + test_osyncstream_manipulators>(&no_sync_char_buffer); + test_osyncstream_manipulators, allocator>, allocator>( + &no_sync_char_buffer); +} diff --git a/tests/std/tests/P0784R7_library_machinery/env.lst b/tests/std/tests/P0784R7_library_machinery/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0784R7_library_machinery/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/P0784R7_library_machinery/test.cpp b/tests/std/tests/P0784R7_library_machinery/test.cpp new file mode 100644 index 00000000000..3fde728f335 --- /dev/null +++ b/tests/std/tests/P0784R7_library_machinery/test.cpp @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4582) // '%s': constructor is not implicitly called +#pragma warning(disable : 4583) // '%s': destructor is not implicitly called + +using namespace std; + +struct int_wrapper_copy { + constexpr int_wrapper_copy() = default; + constexpr int_wrapper_copy(const int v) : _val(v) {} + + constexpr int_wrapper_copy(const int_wrapper_copy& other) : _val(other._val) {} + constexpr int_wrapper_copy& operator=(const int_wrapper_copy& other) { + _val = other._val; + return *this; + } + + int_wrapper_copy(int_wrapper_copy&&) = delete; + int_wrapper_copy& operator=(int_wrapper_copy&&) = delete; + + constexpr bool operator==(const int_wrapper_copy&) const = default; + + int _val = 0; +}; + +struct int_wrapper_move { + constexpr int_wrapper_move() = default; + constexpr int_wrapper_move(const int v) : _val(v) {} + + int_wrapper_move(const int_wrapper_move&) = delete; + int_wrapper_move& operator=(const int_wrapper_move&) = delete; + + constexpr int_wrapper_move(int_wrapper_move&& other) : _val(exchange(other._val, -1)) {} + constexpr int_wrapper_move& operator=(int_wrapper_move&& other) { + _val = exchange(other._val, -1); + return *this; + } + + constexpr bool operator==(const int_wrapper_move&) const = default; + + int _val = 0; +}; + +static constexpr int_wrapper_copy expected_copy[] = {1, 2, 3, 4}; +static constexpr int_wrapper_move expected_move[] = {1, 2, 3, 4}; +static constexpr int_wrapper_move expected_after_move[] = {-1, -1, -1, -1}; + +_CONSTEXPR20_DYNALLOC bool test() { + { // _Copy_unchecked + int_wrapper_copy input[] = {1, 2, 3, 4}; + int_wrapper_copy output[4] = {5, 6, 7, 8}; + + const auto result = _Copy_unchecked(begin(input), end(input), begin(output)); + static_assert(is_same_v, int_wrapper_copy*>); + assert(result == end(output)); + assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output))); + } + + { // _Copy_backward_unchecked + int_wrapper_copy input[] = {1, 2, 3, 4}; + int_wrapper_copy output[4] = {5, 6, 7, 8}; + + const auto result = _Copy_backward_unchecked(begin(input), end(input), end(output)); + static_assert(is_same_v, int_wrapper_copy*>); + assert(result == begin(output)); + assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output))); + } + +#if defined(__cpp_constexpr_dynamic_alloc) + { // _Uninitialized_copy_unchecked + int_wrapper_copy input[] = {1, 2, 3, 4}; + int_wrapper_copy output[4]; + + const auto result = _Uninitialized_copy_unchecked(begin(input), end(input), begin(output)); + static_assert(is_same_v, int_wrapper_copy*>); + assert(result == end(output)); + assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output))); + } +#endif // defined(__cpp_constexpr_dynamic_alloc) + + { // _Move_unchecked + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4] = {5, 6, 7, 8}; + + const auto result = _Move_unchecked(begin(input), end(input), begin(output)); + static_assert(is_same_v, int_wrapper_move*>); + assert(result == end(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } + + { // _Move_backward_unchecked + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4] = {5, 6, 7, 8}; + + const auto result = _Move_backward_unchecked(begin(input), end(input), end(output)); + static_assert(is_same_v, int_wrapper_move*>); + assert(result == begin(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } + +#ifdef __cpp_lib_concepts + { // _Move_backward_common + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4] = {5, 6, 7, 8}; + + const auto result = ranges::_Move_backward_common(begin(input), end(input), end(output)); + static_assert(is_same_v, int_wrapper_move*>); + assert(result == begin(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } +#endif // __cpp_lib_concepts + +#if defined(__cpp_constexpr_dynamic_alloc) + { // _Uninitialized_move_unchecked + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4]; + + const auto result = _Uninitialized_move_unchecked(begin(input), end(input), begin(output)); + static_assert(is_same_v, int_wrapper_move*>); + assert(result == end(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } +#endif // defined(__cpp_constexpr_dynamic_alloc) + return true; +} + +int main() { + test(); + static_assert(test()); +} diff --git a/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp b/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp index 52da34b9f96..7939af14b21 100644 --- a/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp +++ b/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp @@ -227,6 +227,7 @@ void test_array(const T& val) { } #ifdef __cpp_lib_constexpr_dynamic_alloc +#ifndef __EDG__ // TRANSITION, VSO-1269976 template struct storage_for { union { @@ -244,9 +245,11 @@ constexpr void test_compiletime() { assert(s.object == 42); destroy_at(&s.object); +#ifdef __cpp_lib_concepts ranges::construct_at(&s.object, 1729); assert(s.object == 1729); ranges::destroy_at(&s.object); +#endif // __cpp_lib_concepts } struct nontrivial { @@ -262,12 +265,15 @@ constexpr void test_compiletime() { assert(s.object.x == 42); destroy_at(&s.object); +#ifdef __cpp_lib_concepts ranges::construct_at(&s.object, 1729); assert(s.object.x == 1729); ranges::destroy_at(&s.object); +#endif // __cpp_lib_concepts } } static_assert((test_compiletime(), true)); +#endif // __EDG__ template struct A { @@ -286,6 +292,7 @@ struct nontrivial_A { }; constexpr void test_compiletime_destroy_variants() { +#ifndef __EDG__ // TRANSITION, VSO-1270011 { allocator> alloc{}; A* a = alloc.allocate(10); @@ -304,6 +311,7 @@ constexpr void test_compiletime_destroy_variants() { destroy(a, a + 10); alloc.deallocate(a, 10); } +#endif // __EDG__ #ifdef __cpp_lib_concepts { allocator> alloc{}; @@ -385,6 +393,7 @@ constexpr void test_compiletime_destroy_variants() { } static_assert((test_compiletime_destroy_variants(), true)); +#ifndef __EDG__ // TRANSITION, VSO-1269976 template struct Alloc { using value_type = T; @@ -498,6 +507,7 @@ constexpr void test_compiletime_allocator_traits() { } } static_assert((test_compiletime_allocator_traits(), true)); +#endif // __EDG__ constexpr void test_compiletime_allocator() { { diff --git a/tests/std/tests/P0896R4_ranges_alg_inplace_merge/env.lst b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/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_inplace_merge/test.cpp b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp new file mode 100644 index 00000000000..9cf822b5dcb --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/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 expected[] = {P{0, 1}, P{0, 5}, P{4, 2}, P{4, 6}, P{6, 7}, P{7, 3}, P{8, 4}, P{9, 8}, P{10, 9}}; + + template + static void call() { + using ranges::equal, ranges::is_sorted, ranges::iterator_t, ranges::inplace_merge; + + { // Validate range overload + P input[] = {P{0, 1}, P{4, 2}, P{7, 3}, P{8, 4}, P{0, 5}, P{4, 6}, P{6, 7}, P{9, 8}, P{10, 9}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as> auto result = inplace_merge(range, mid, ranges::less{}, get_first); + assert(result == range.end()); + assert(equal(input, expected)); + + // Validate empty range + const Range empty_range{}; + const same_as> auto empty_result = + inplace_merge(empty_range, empty_range.begin(), ranges::less{}, get_first); + assert(empty_result == empty_range.begin()); + } + + { // Validate iterator overload + P input[] = {P{0, 1}, P{4, 2}, P{7, 3}, P{8, 4}, P{0, 5}, P{4, 6}, P{6, 7}, P{9, 8}, P{10, 9}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as> auto result = + inplace_merge(range.begin(), mid, range.end(), ranges::less{}, get_first); + assert(result == range.end()); + assert(equal(input, expected)); + + // Validate empty range + const Range empty_range{}; + const same_as> auto empty_result = + inplace_merge(empty_range.begin(), empty_range.begin(), empty_range.end(), ranges::less{}, get_first); + assert(empty_result == empty_range.end()); + } + } +}; + +int main() { + test_bidi(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_stable_partition/env.lst b/tests/std/tests/P0896R4_ranges_alg_stable_partition/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_partition/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_stable_partition/test.cpp b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp new file mode 100644 index 00000000000..cc9cd58ee15 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp @@ -0,0 +1,67 @@ +// 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; + +constexpr auto is_even = [](int i) { return i % 2 == 0; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, is_even)), ranges::dangling>); +STATIC_ASSERT(same_as{}, is_even)), ranges::subrange>); + +struct instantiator { + template + static void call() { + using ranges::is_partitioned, ranges::is_sorted, ranges::iterator_t, ranges::stable_partition, ranges::subrange; + + { // Validate range overload + P input[] = {P{0, 1}, P{1, 2}, P{0, 3}, P{1, 4}, P{0, 5}, P{1, 6}, P{0, 7}, P{1, 8}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as>> auto result = stable_partition(range, is_even, get_first); + assert(result.begin() == mid); + assert(result.end() == range.end()); + assert(is_partitioned(range, is_even, get_first)); + assert(is_sorted(range)); + + // Validate empty range + const Range empty_range{}; + const same_as>> auto empty_result = + stable_partition(empty_range, is_even, get_first); + assert(empty_result.begin() == empty_range.end()); + assert(empty_result.end() == empty_range.end()); + } + + { // Validate iterator overload + P input[] = {P{0, 1}, P{1, 2}, P{0, 3}, P{1, 4}, P{0, 5}, P{1, 6}, P{0, 7}, P{1, 8}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as>> auto result = + stable_partition(range.begin(), range.end(), is_even, get_first); + assert(result.begin() == mid); + assert(result.end() == range.end()); + assert(is_partitioned(range, is_even, get_first)); + assert(is_sorted(range)); + + // Validate empty range + const Range empty_range{}; + const same_as>> auto empty_result = + stable_partition(empty_range.begin(), empty_range.end(), is_even, get_first); + assert(empty_result.begin() == empty_range.end()); + assert(empty_result.end() == empty_range.end()); + } + } +}; + +int main() { + test_bidi(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_stable_sort/env.lst b/tests/std/tests/P0896R4_ranges_alg_stable_sort/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_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_stable_sort/test.cpp b/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp new file mode 100644 index 00000000000..f20095aa55b --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), int*>); + +struct instantiator { + static constexpr array input = {P{1, 0}, P{-1260655766, 1}, P{-1298559576, 2}, P{1, 3}, P{-2095681771, 4}, + P{-441494788, 5}, P{-47163201, 6}, P{1, 7}, P{1429106719, 8}, P{1, 9}}; + + template + static void call() { + using ranges::stable_sort, ranges::is_sorted, ranges::iterator_t, ranges::less; + + { // Validate range overload + auto buff = input; + const R range{buff}; + const same_as> auto result = stable_sort(range, less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range)); // Check for stability by not using a projection + } + + { // Validate iterator overload + auto buff = input; + const R range{buff}; + const same_as> auto result = stable_sort(range.begin(), range.end(), less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range.begin(), range.end())); // Check for stability by not using a projection + } + + { // Validate empty range + const R range{}; + const same_as> auto result = stable_sort(range, less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range, less{})); + } + } +}; + +int main() { + test_random(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp index f2c607162fe..0199745aa45 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp @@ -76,21 +76,16 @@ struct holder { } }; -template -void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy - for (auto& e : r) { - destroy_at(&e); - } -} - struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; template static void call() { - using ranges::uninitialized_copy, ranges::uninitialized_copy_result, ranges::equal, ranges::equal_to, - ranges::iterator_t; + using ranges::destroy, ranges::uninitialized_copy, ranges::uninitialized_copy_result, ranges::equal, + ranges::equal_to, ranges::iterator_t; { // Validate range overload int_wrapper input[3] = {13, 55, 12345}; @@ -107,7 +102,7 @@ struct instantiator { assert(result.out == wrapped_output.end()); assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); + destroy(wrapped_output); assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } @@ -127,10 +122,51 @@ struct instantiator { assert(result.out == wrapped_output.end()); assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate range overload shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } + + { // Validate range overload shorter input + int_wrapper input[3] = {13, 55, 12345}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -160,6 +196,72 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + { // Validate matching ranges + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = ranges::uninitialized_copy(input, output); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate input shorter + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1, -1}; + + auto result = ranges::uninitialized_copy(input, output); + assert(result.in == end(input)); + assert(++result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output_long)); + } + + { // Validate output shorter + int input[] = {13, 55, 12345, 42}; + int output[] = {-1, -1, -1}; + + auto result = ranges::uninitialized_copy(input, output); + assert(++result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input_long)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate non-common input range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_copy(begin(input), unreachable_sentinel, begin(output), end(output)); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate non-common output range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_copy(begin(input), end(input), begin(output), unreachable_sentinel); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + } +}; + template using test_input = test::range; @@ -174,4 +276,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp index 9e86c952d8d..3096d91b16a 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -66,39 +67,77 @@ struct holder { } }; -template -void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy - for (auto& e : r) { - destroy_at(&e); - } -} - struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; template static void call() { - using ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, ranges::equal_to, - ranges::iterator_t; + using ranges::destroy, ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, + ranges::equal_to, ranges::iterator_t; + + { // Validate equal ranges + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as, iterator_t>> auto result = + uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper input[3] = {13, 55, 12345}; - Read wrapped_input{input}; - holder mem; - Write wrapped_output{mem.as_span()}; + { // Validate shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper::clear_counts(); - const same_as, iterator_t>> auto result = - uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 0); - assert(result.in == wrapped_input.end()); - assert(result.out == wrapped_output.end()); - assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); - assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 3); + { // Validate shorter input + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -126,6 +165,53 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345, -1}; + static constexpr int expected_output_long[] = {13, 55, -1, -1}; + static constexpr int expected_input[] = {13, 55, 12345, 42}; + static constexpr int expected_input_short[] = {13, 55}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + using ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, ranges::iterator_t; + { // Validate range overload + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 3, output.begin(), output.end()); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } + + { // Validate shorter input + vector input = {13, 55}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 2, output.begin(), output.end()); + assert(result.in == input.end()); + assert(next(result.out, 2) == output.end()); + assert(equal(input, expected_input_short)); + assert(equal(output, expected_output_long)); + } + + { // Validate shorter output + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 2, output.begin(), output.end()); + assert(next(result.in, 2) == input.end()); + assert(result.out == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_input_short)); + } + } +}; + template using test_input = test::range; @@ -140,4 +226,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp index 2c7801a5348..a04fe76cdb9 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp @@ -141,6 +141,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr unsigned char expected[] = {42, 42, 42}; + + static void call() { + { // Validate only range overload + unsigned char input[3]; + + const auto result = ranges::uninitialized_fill(input, static_cast(42)); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -150,4 +164,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp index 4d0be9f186c..87faa500d0e 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp @@ -120,6 +120,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr unsigned char expected[] = {42, 42, 42}; + + static void call() { + { // Validate only range overload + unsigned char input[3]; + + const auto result = ranges::uninitialized_fill_n(input, 3, static_cast(42)); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -129,4 +143,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp index 8be0b1e8de3..45adaeecf7e 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp @@ -78,8 +78,10 @@ struct holder { }; struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_input_long[] = {-1, -1, -1, 42}; template static void call() { @@ -125,6 +127,47 @@ struct instantiator { assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } + + { // Validate range overload shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } + + { // Validate range overload shorter input + int_wrapper input[3] = {13, 55, 12345}; + R wrapped_input{input}; + holder mem; + W wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move(wrapped_input, wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -154,6 +197,72 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {13, 55, 12345}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + { // Validate matching ranges + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = ranges::uninitialized_move(input, output); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate input shorter + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1, -1}; + + auto result = ranges::uninitialized_move(input, output); + assert(result.in == end(input)); + assert(++result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output_long)); + } + + { // Validate output shorter + int input[] = {13, 55, 12345, 42}; + int output[] = {-1, -1, -1}; + + auto result = ranges::uninitialized_move(input, output); + assert(++result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input_long)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate non-common input range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_move(begin(input), unreachable_sentinel, begin(output), end(output)); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate non-common output range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_move(begin(input), end(input), begin(output), unreachable_sentinel); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + } +}; + template using test_input = test::range; @@ -168,4 +277,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp index eef82b952c4..60d6a79c732 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -67,39 +68,76 @@ struct holder { } }; -template -void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy - for (auto& e : r) { - destroy_at(&e); - } -} - struct instantiator { - static constexpr int expected_output[] = {13, 55, 12345}; - static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_output[] = {13, 55, 12345}; + static constexpr int expected_output_long[] = {13, 55, 12345, -1}; + static constexpr int expected_input[] = {-1, -1, -1}; + static constexpr int expected_input_long[] = {-1, -1, -1, 42}; template static void call() { - using ranges::uninitialized_move_n, ranges::uninitialized_move_n_result, ranges::equal, ranges::equal_to, - ranges::iterator_t; + using ranges::destroy, ranges::uninitialized_move_n, ranges::uninitialized_move_n_result, ranges::equal, + ranges::equal_to, ranges::iterator_t; + { // Validate matching ranges + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as, iterator_t>> auto result = + uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper input[3] = {13, 55, 12345}; - Read wrapped_input{input}; - holder mem; - Write wrapped_output{mem.as_span()}; + { // Validate shorter output + int_wrapper input[4] = {13, 55, 12345, 42}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(++result.in == wrapped_input.end()); + assert(result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input_long, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper::clear_counts(); - const same_as, iterator_t>> auto result = - uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 0); - assert(result.in == wrapped_input.end()); - assert(result.out == wrapped_output.end()); - assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); - assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 3); + { // Validate shorter input + int_wrapper input[3] = {13, 55, 12345}; + Read wrapped_input{input}; + holder mem; + Write wrapped_output{mem.as_span()}; + + int_wrapper::clear_counts(); + same_as, iterator_t>> auto result = + uninitialized_move_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result.in == wrapped_input.end()); + construct_at(addressof(*result.out), -1); // Need to construct non written element for comparison + assert(++result.out == wrapped_output.end()); + assert(equal(wrapped_output, expected_output_long, equal_to{}, &int_wrapper::val)); + assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); + destroy(wrapped_output); + assert(int_wrapper::constructions == 4); + assert(int_wrapper::destructions == 4); + } } }; @@ -128,6 +166,53 @@ struct throwing_test { } }; +struct memcpy_test { + static constexpr int expected_output[] = {13, 55, 12345, -1}; + static constexpr int expected_output_long[] = {13, 55, -1, -1}; + static constexpr int expected_input[] = {13, 55, 12345, 42}; + static constexpr int expected_input_short[] = {13, 55}; + static constexpr int expected_input_long[] = {13, 55, 12345, 42}; + + static void call() { + using ranges::uninitialized_move_n, ranges::uninitialized_move_n_result, ranges::equal, ranges::iterator_t; + { // Validate range overload + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 3, output.begin(), output.end()); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } + + { // Validate shorter input + vector input = {13, 55}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 2, output.begin(), output.end()); + assert(result.in == input.end()); + assert(next(result.out, 2) == output.end()); + assert(equal(input, expected_input_short)); + assert(equal(output, expected_output_long)); + } + + { // Validate shorter output + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 2, output.begin(), output.end()); + assert(next(result.in, 2) == input.end()); + assert(result.out == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_input_short)); + } + } +}; + template using test_input = test::range; @@ -142,4 +227,5 @@ int main() { instantiator::call, test_output>(); throwing_test::call, test_output>(); throwing_test::call, test_output>(); + memcpy_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp index a17ede367c5..f5c27485556 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp @@ -121,6 +121,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr int expected[] = {0, 0, 0}; + + static void call() { + { // Validate only range overload + int input[3]; + + const auto result = ranges::uninitialized_value_construct(input); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -130,4 +144,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp index 6a9f08449d8..2797150ec44 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp @@ -100,6 +100,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr int expected[] = {0, 0, 0}; + + static void call() { + { // Validate only range overload + int input[3]; + + const auto result = ranges::uninitialized_value_construct_n(begin(input), 3); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -109,4 +123,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 576c4340517..2e4698779e3 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -104,6 +104,7 @@ STATIC_ASSERT(test_cpo(ranges::views::drop)); STATIC_ASSERT(test_cpo(ranges::views::drop_while)); STATIC_ASSERT(test_cpo(ranges::views::elements<42>)); STATIC_ASSERT(test_cpo(ranges::views::filter)); +STATIC_ASSERT(test_cpo(ranges::views::iota)); STATIC_ASSERT(test_cpo(ranges::views::keys)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); @@ -1499,9 +1500,7 @@ namespace borrowed_range_testing { STATIC_ASSERT(test_borrowed_range, std::span::iterator>()); STATIC_ASSERT(test_borrowed_range, int*>()); STATIC_ASSERT(test_borrowed_range, int*>()); -#if 0 // TRANSITION, future - STATIC_ASSERT(test_borrowed_range, ...>()); -#endif // TRANSITION, future + STATIC_ASSERT(test_borrowed_range, ranges::iterator_t>>()); struct simple_borrowed_range { int* begin() const { diff --git a/tests/std/tests/P0896R4_views_drop/test.cpp b/tests/std/tests/P0896R4_views_drop/test.cpp index f8d503138de..2e154446815 100644 --- a/tests/std/tests/P0896R4_views_drop/test.cpp +++ b/tests/std/tests/P0896R4_views_drop/test.cpp @@ -20,41 +20,58 @@ using namespace std; 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; +inline constexpr bool is_span = false; +template +inline constexpr bool is_span> = true; template inline constexpr bool is_subrange = false; template inline constexpr bool is_subrange> = true; +template +struct mapped { + template + using apply = ranges::drop_view>; +}; +template +struct mapped> { + template + using apply = ranges::empty_view; +}; +template +struct mapped> { + template + using apply = span; +}; +template +struct mapped> { + template + using apply = basic_string_view; +}; +// clang-format off +template + requires ranges::random_access_range> + && ranges::sized_range> +struct mapped> { + // clang-format on + template + using apply = ranges::iota_view; +}; // 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 + requires random_access_iterator +struct mapped> { + // clang-format on + template + using apply = ranges::subrange; +}; template -using pipeline_t = mapped_t>>>>; +using mapped_t = typename mapped>::template apply; + +template +using pipeline_t = mapped_t>>>; template concept CanViewDrop = requires(Rng&& r) { @@ -71,7 +88,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr bool is_view = ranges::view>; using V = views::all_t; - using M = mapped_t; + using M = mapped_t; STATIC_ASSERT(ranges::view); STATIC_ASSERT(common_range == common_range); STATIC_ASSERT(input_range == input_range); @@ -81,7 +98,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto drop_four = views::drop(4); + constexpr auto closure = views::drop(4); // ... with lvalue argument STATIC_ASSERT(CanViewDrop == (!is_view || copyable) ); @@ -91,8 +108,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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 | closure) == is_noexcept); STATIC_ASSERT(same_as>); STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); @@ -100,26 +117,26 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const lvalue argument STATIC_ASSERT(CanViewDrop&> == (!is_view || copyable) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (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) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } else if constexpr (!is_view) { - using RC = mapped_t&>>; + 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) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); @@ -127,13 +144,13 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with rvalue argument STATIC_ASSERT(CanViewDrop> == is_view || enable_borrowed_range>); - if constexpr (is_view) { + if constexpr (is_span> || 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -145,8 +162,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -155,14 +172,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const rvalue argument STATIC_ASSERT(CanViewDrop> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (is_view && copyable) ) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -174,8 +191,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -455,9 +472,13 @@ 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); + constexpr span s0{some_ints}; + STATIC_ASSERT(test_one(s0, only_four_ints)); + test_one(s0, only_four_ints); + + constexpr span s1{some_ints}; + STATIC_ASSERT(test_one(s1, only_four_ints)); + test_one(s1, only_four_ints); STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); test_one(ranges::subrange{some_ints}, only_four_ints); @@ -468,9 +489,8 @@ int main() { 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); + 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)); @@ -490,13 +510,6 @@ int main() { 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(); diff --git a/tests/std/tests/P0896R4_views_iota/env.lst b/tests/std/tests/P0896R4_views_iota/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_iota/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_iota/test.cpp b/tests/std/tests/P0896R4_views_iota/test.cpp new file mode 100644 index 00000000000..ed990295ba3 --- /dev/null +++ b/tests/std/tests/P0896R4_views_iota/test.cpp @@ -0,0 +1,299 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +concept CanViewIota = requires(W w, B b) { + views::iota(w, b); +}; + +template +concept CanSize = requires(R& r) { + ranges::size(r); +}; + +struct empty_type {}; + +template +constexpr void test_integral() { + constexpr T low = 0; + constexpr T high = 8; + constexpr T expected[] = {0, 1, 2, 3, 4, 5, 6, 7}; + + { + // Validate bounded (both upper and lower bounds exist) iota_view + using R = ranges::iota_view; + + // Validate type properties + static_assert(same_as, T>); + static_assert(same_as, T>); + + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(ranges::common_range); + + static_assert(ranges::view); + static_assert(semiregular); + static_assert(is_nothrow_copy_constructible_v); + static_assert(is_nothrow_copy_assignable_v); + static_assert(is_nothrow_move_constructible_v); + static_assert(is_nothrow_move_assignable_v); + + if constexpr (sizeof(T) < sizeof(int)) { + static_assert(same_as, int>); + } else { + static_assert(same_as, long long>); + } + + // iota_view is always a simple-view, i.e., const and non-const are always valid ranges with the same iterators: + static_assert(ranges::common_range); + static_assert( + same_as>, ranges::iterator_t>>); + + static_assert(same_as, T>); + static_assert(same_as, ranges::range_difference_t>); + + const same_as auto rng = views::iota(low, high); + static_assert(noexcept(views::iota(low, high))); // strengthened + + assert(ranges::equal(rng, expected)); + static_assert(noexcept(rng.begin())); // strengthened + static_assert(noexcept(rng.end())); // strengthened + + assert(rng.size() == 8u); + static_assert(noexcept(rng.size() == 8u)); // strengthened + + using I = ranges::iterator_t; + static_assert(same_as); + static_assert(same_as::iterator_category, input_iterator_tag>); + + assert(I{} == I{T{0}}); + static_assert(is_nothrow_default_constructible_v); + assert(I{} == I{}); + assert(!(I{} != I{})); + assert(!(I{} < I{})); + assert(!(I{} > I{})); + assert(I{} <= I{}); + assert(I{} >= I{}); + assert(I{} <=> I{} == 0); + assert(I{} - I{} == 0); + + const I first = rng.begin(); + const I second = ranges::next(first); + + assert(*first == T{0}); + assert(*second == T{1}); + static_assert(noexcept(*first)); + + assert(first == I{T{0}}); + assert(second == I{T{1}}); + static_assert(noexcept(noexcept(I{T{0}}))); // strengthened + + { + I tmp = first; + static_assert(is_nothrow_copy_constructible_v); + + I i = move(tmp); + static_assert(is_nothrow_move_constructible_v); + + tmp = i; + static_assert(is_nothrow_copy_assignable_v); + + i = move(tmp); + static_assert(is_nothrow_move_assignable_v); + } + + assert(!(first == second)); + assert(!(second == first)); + static_assert(noexcept(first == second)); // strengthened + + assert(first != second); + assert(second != first); + static_assert(noexcept(first != second)); // strengthened + + assert(first < second); + assert(!(second < first)); + static_assert(noexcept(first < second)); // strengthened + + assert(!(first > second)); + assert(second > first); + static_assert(noexcept(first > second)); // strengthened + + assert(first <= second); + assert(!(second <= first)); + static_assert(noexcept(first <= second)); // strengthened + + assert(!(first >= second)); + assert(second >= first); + static_assert(noexcept(first >= second)); // strengthened + + assert(first <=> second < 0); + assert(second <=> first > 0); + static_assert(noexcept(first <=> second)); // strengthened + + { + I i = first; + + assert(&++i == &i); + assert(i == second); + static_assert(noexcept(++i)); // strengthened + + i = first; + + assert(i++ == first); + assert(i == second); + static_assert(noexcept(i++)); // strengthened + + assert(&--i == &i); + assert(i == first); + static_assert(noexcept(--i)); // strengthened + + i = second; + + assert(i-- == second); + assert(i == first); + static_assert(noexcept(i--)); // strengthened + + assert(i + 1 == second); + static_assert(noexcept(i + 1)); // strengthened + + assert(1 + i == second); + static_assert(noexcept(1 + i)); // strengthened + + assert(&(i += 1) == &i); + assert(i == second); + static_assert(noexcept(i += 1)); // strengthened + assert(i - 1 == first); + static_assert(noexcept(i - 1)); // strengthened + + assert(&(i -= 1) == &i); + assert(i == first); + static_assert(noexcept(i -= 1)); // strengthened + + assert(second - first == 1); + static_assert(noexcept(second - first)); // strengthened + + assert(first[1] == *second); + assert(second[-1] == *first); + static_assert(noexcept(first[1])); // strengthened + } + + const same_as> auto last = rng.end(); + assert(!(first == last)); + static_assert(noexcept(first == last)); // strengthened + assert(first != last); + static_assert(noexcept(first != last)); // strengthened + assert(last - first == 8); + static_assert(noexcept(last - first)); // strengthened + } + + { + // Validate unbounded (upper bound is unreachable) iota_view + using R = ranges::iota_view; + static_assert(same_as, T>); + static_assert(same_as, T>); + + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(!ranges::common_range); + static_assert(same_as>, unreachable_sentinel_t>); + + // Bounded and unbounded iota_view have the same iterator type, however: + static_assert(same_as>, ranges::iterator_t>>); + + if constexpr (sizeof(T) < sizeof(int)) { + static_assert(same_as, int>); + } else { + static_assert(same_as, long long>); + } + + { + const same_as auto rng = views::iota(low); + + auto i = low; + for (const auto& e : rng) { + assert(e == i); + if (++i == high) { + break; + } + } + } + + static_assert(!CanSize>); + } +} + +int main() { + // Validate standard signed integer types + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Validate standard unsigned integer types + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Validate other integer types +#ifndef __clang__ // TRANSITION, LLVM-48173 + static_assert(!CanViewIota); +#endif // TRANSITION, LLVM-48173 + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); +#ifdef __cpp_char8_t + static_assert((test_integral(), true)); + test_integral(); +#endif // __cpp_char8_t + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Some non-integer coverage: + { + // Pointers + empty_type objects[] = {{}, {}, {}}; + const auto address = [](auto& x) { return &x; }; + assert(ranges::equal( + views::iota(begin(as_const(objects)), end(objects)), objects, ranges::equal_to{}, identity{}, address)); + assert(ranges::equal( + views::iota(begin(objects), end(as_const(objects))), objects, ranges::equal_to{}, identity{}, address)); + } + { + // Iterator and sentinel of a non-common range + const int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const int even_ints[] = {0, 2, 4, 6}; + + const auto even = [](int x) { return x % 2 == 0; }; + const auto deref = [](auto x) -> decltype(auto) { return *x; }; + + auto f = some_ints | views::filter(even); + auto r = views::iota(ranges::begin(f), ranges::end(f)); + + assert(ranges::equal(r, even_ints, ranges::equal_to{}, deref)); + } +} diff --git a/tests/std/tests/P0896R4_views_take/test.cpp b/tests/std/tests/P0896R4_views_take/test.cpp index 27ccb1b7cc3..34fb9e3a305 100644 --- a/tests/std/tests/P0896R4_views_take/test.cpp +++ b/tests/std/tests/P0896R4_views_take/test.cpp @@ -20,41 +20,58 @@ using namespace std; 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; +inline constexpr bool is_span = false; +template +inline constexpr bool is_span> = true; template inline constexpr bool is_subrange = false; template inline constexpr bool is_subrange> = true; +template +struct mapped { + template + using apply = ranges::take_view>; +}; +template +struct mapped> { + template + using apply = ranges::empty_view; +}; +template +struct mapped> { + template + using apply = span; +}; +template +struct mapped> { + template + using apply = basic_string_view; +}; +// clang-format off +template + requires ranges::random_access_range> + && ranges::sized_range> +struct mapped> { + // clang-format on + template + using apply = ranges::iota_view; +}; // 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 + requires random_access_iterator +struct mapped> { + // clang-format on + template + using apply = ranges::subrange; +}; + +template +using mapped_t = typename mapped>::template apply; template -using pipeline_t = mapped_t>>>>; +using pipeline_t = mapped_t>>>; template concept CanViewTake = requires(Rng&& r) { @@ -71,7 +88,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr bool is_view = ranges::view>; using V = views::all_t; - using M = mapped_t; + using M = mapped_t; STATIC_ASSERT(ranges::view); STATIC_ASSERT(input_range == input_range); STATIC_ASSERT(forward_range == forward_range); @@ -80,7 +97,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto take_four = views::take(4); + constexpr auto closure = views::take(4); // ... with lvalue argument STATIC_ASSERT(CanViewTake == (!is_view || copyable) ); @@ -90,8 +107,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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 | closure) == is_noexcept); STATIC_ASSERT(same_as>); STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); @@ -99,40 +116,40 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... 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); + if constexpr (is_span> || (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) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } else if constexpr (!is_view) { - using RC = mapped_t&>>; + 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) | closure) == 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) { + STATIC_ASSERT(CanViewTake> == (is_view || enable_borrowed_range>) ); + if constexpr (is_span> || 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -144,8 +161,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -154,14 +171,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const rvalue argument STATIC_ASSERT(CanViewTake> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (is_view && copyable) ) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -173,8 +190,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -496,10 +513,14 @@ constexpr void output_range_test() { 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); + // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view + constexpr span s0{some_ints}; + STATIC_ASSERT(test_one(s0, only_four_ints)); + test_one(s0, only_four_ints); + + constexpr span s1{some_ints}; + STATIC_ASSERT(test_one(s1, only_four_ints)); + test_one(s1, only_four_ints); STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); test_one(ranges::subrange{some_ints}, only_four_ints); @@ -510,9 +531,8 @@ int main() { 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); + 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)); @@ -532,13 +552,6 @@ int main() { 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(); diff --git a/tests/std/tests/P0898R3_concepts/test.cpp b/tests/std/tests/P0898R3_concepts/test.cpp index 6a3ece735e0..a0d79d6010f 100644 --- a/tests/std/tests/P0898R3_concepts/test.cpp +++ b/tests/std/tests/P0898R3_concepts/test.cpp @@ -150,6 +150,10 @@ struct ExplicitDefault { explicit ExplicitDefault() = default; }; +struct AggregatesExplicitDefault { + ExplicitDefault meow; +}; + struct DeletedDefault { DeletedDefault() = delete; }; @@ -1488,17 +1492,17 @@ namespace test_default_initializable { using std::default_initializable, std::initializer_list; STATIC_ASSERT(default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1084668 +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / workaround vvv +#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv STATIC_ASSERT(default_initializable); -#endif // TRANSITION, VSO-1084668 +#endif // TRANSITION, DevCom-952724 STATIC_ASSERT(default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1084668 +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / workaround vvv +#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv STATIC_ASSERT(default_initializable); -#endif // TRANSITION, VSO-1084668 +#endif // TRANSITION, DevCom-952724 STATIC_ASSERT(default_initializable); STATIC_ASSERT(!default_initializable); @@ -1511,11 +1515,11 @@ namespace test_default_initializable { STATIC_ASSERT(!default_initializable); STATIC_ASSERT(!default_initializable); STATIC_ASSERT(!default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1084668 +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / workaround vvv +#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv STATIC_ASSERT(default_initializable); -#endif // TRANSITION, VSO-1084668 +#endif // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); STATIC_ASSERT(!default_initializable); @@ -1559,11 +1563,18 @@ namespace test_default_initializable { int x; }; STATIC_ASSERT(default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1084668 +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / workaround vvv +#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv STATIC_ASSERT(default_initializable); -#endif // TRANSITION, VSO-1084668 +#endif // TRANSITION, DevCom-952724 + + // Also test GH-1603 "default_initializable accepts types that are not default-initializable" +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-1326684 + STATIC_ASSERT(!default_initializable); +#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv + STATIC_ASSERT(default_initializable); +#endif // TRANSITION, DevCom-1326684 } // namespace test_default_initializable namespace test_move_constructible { @@ -2876,10 +2887,13 @@ namespace test_invocable_concepts { #define MCALLCONV __thiscall #include "invocable_cc.hpp" +#if !defined(_M_ARM) && !defined(_M_ARM64) #define NAME test_vector_vector #define CALLCONV __vectorcall #define MCALLCONV __vectorcall #include "invocable_cc.hpp" +#endif // ^^^ !ARM && !ARM64 ^^^ + } // namespace test_invocable_concepts namespace test_predicate { diff --git a/tests/std/tests/P0912R5_coroutine/env.lst b/tests/std/tests/P0912R5_coroutine/env.lst index 642f530ffad..f873150e6bb 100644 --- a/tests/std/tests/P0912R5_coroutine/env.lst +++ b/tests/std/tests/P0912R5_coroutine/env.lst @@ -1,4 +1,21 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_latest_matrix.lst +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/EHsc /MD /await:strict /std:c++14" +PM_CL="/EHsc /MD /await:strict /std:c++14 /permissive-" +PM_CL="/EHsc /MTd /await:strict /std:c++14 /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /await:strict /std:c++14 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MD /await:strict /std:c++17" +PM_CL="/EHsc /MD /await:strict /std:c++17 /permissive-" +PM_CL="/EHsc /MTd /await:strict /std:c++17 /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /await:strict /std:c++17 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MD /std:c++latest /permissive" +PM_CL="/EHsc /MD /std:c++latest /permissive-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MTd /std:c++latest /permissive-" diff --git a/tests/std/tests/P0912R5_coroutine/test.cpp b/tests/std/tests/P0912R5_coroutine/test.cpp index 0aa48651cb6..2edce782cee 100644 --- a/tests/std/tests/P0912R5_coroutine/test.cpp +++ b/tests/std/tests/P0912R5_coroutine/test.cpp @@ -9,6 +9,8 @@ #include using namespace std; +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + int g_tasks_destroyed{0}; struct Task { @@ -33,7 +35,8 @@ struct Task { void await_resume() noexcept {} coroutine_handle<> await_suspend(coroutine_handle h) noexcept { - if (auto& pre = h.promise().previous; pre) { + auto& pre = h.promise().previous; + if (pre) { return pre; // resume awaiting coroutine } @@ -97,50 +100,50 @@ Task triangular_number(const int n) { void test_noop_handle() { // Validate noop_coroutine_handle const noop_coroutine_handle noop = noop_coroutine(); - static_assert(noexcept(noop_coroutine())); + STATIC_ASSERT(noexcept(noop_coroutine())); const coroutine_handle<> as_void = noop; - static_assert(noexcept(static_cast>(noop_coroutine()))); + STATIC_ASSERT(noexcept(static_cast>(noop_coroutine()))); assert(noop); assert(as_void); - static_assert(noexcept(static_cast(noop))); - static_assert(noexcept(static_cast(as_void))); + STATIC_ASSERT(noexcept(static_cast(noop))); + STATIC_ASSERT(noexcept(static_cast(as_void))); assert(!noop.done()); assert(!as_void.done()); - static_assert(noexcept(noop.done())); - static_assert(noexcept(as_void.done())); + STATIC_ASSERT(noexcept(noop.done())); + STATIC_ASSERT(noexcept(as_void.done())); assert(noop); assert(as_void); noop(); as_void(); - static_assert(noexcept(noop())); + STATIC_ASSERT(noexcept(noop())); assert(noop); assert(as_void); noop.resume(); as_void.resume(); - static_assert(noexcept(noop.resume())); + STATIC_ASSERT(noexcept(noop.resume())); assert(noop); assert(as_void); noop.destroy(); as_void.destroy(); - static_assert(noexcept(noop.destroy())); + STATIC_ASSERT(noexcept(noop.destroy())); assert(noop); assert(as_void); assert(&noop.promise() != nullptr); - static_assert(noexcept(noop.promise())); + STATIC_ASSERT(noexcept(noop.promise())); assert(noop); assert(as_void); assert(noop.address() != nullptr); assert(noop.address() == as_void.address()); - static_assert(noexcept(noop.address())); - static_assert(noexcept(as_void.address())); + STATIC_ASSERT(noexcept(noop.address())); + STATIC_ASSERT(noexcept(as_void.address())); } int main() { diff --git a/tests/std/tests/P0980R1_constexpr_strings/env.lst b/tests/std/tests/P0980R1_constexpr_strings/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0980R1_constexpr_strings/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/P0980R1_constexpr_strings/test.cpp b/tests/std/tests/P0980R1_constexpr_strings/test.cpp new file mode 100644 index 00000000000..495abb7d558 --- /dev/null +++ b/tests/std/tests/P0980R1_constexpr_strings/test.cpp @@ -0,0 +1,1799 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma warning(disable : 4389) // signed/unsigned mismatch in arithmetic + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wsign-compare" +#endif // __clang__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +constexpr auto literal_input = "Hello fluffy kittens"; +constexpr auto literal_input_u16 = u"Hello fluffy kittens"; +constexpr auto literal_input_u32 = U"Hello fluffy kittens"; +constexpr auto literal_input_w = L"Hello fluffy kittens"; + +constexpr auto view_input = "Hello fluffy kittens"sv; +constexpr auto view_input_u16 = u"Hello fluffy kittens"sv; +constexpr auto view_input_u32 = U"Hello fluffy kittens"sv; +constexpr auto view_input_w = L"Hello fluffy kittens"sv; + +#ifdef __cpp_char8_t +constexpr auto literal_input_u8 = u8"Hello fluffy kittens"; +constexpr auto view_input_u8 = u8"Hello fluffy kittens"sv; +#endif // __cpp_char8_t + +template +constexpr auto get_literal_input() { + if constexpr (is_same_v) { + return literal_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u16; + } else if constexpr (is_same_v) { + return literal_input_u32; + } else { + return literal_input_w; + } +} + +template +constexpr auto get_view_input() { + if constexpr (is_same_v) { + return view_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u16; + } else if constexpr (is_same_v) { + return view_input_u32; + } else { + return view_input_w; + } +} + +template +constexpr auto get_cat() { + if constexpr (is_same_v) { + return "kitten"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"kitten"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"kitten"; + } else if constexpr (is_same_v) { + return U"kitten"; + } else { + return L"kitten"; + } +} + +template +constexpr auto get_dog() { + if constexpr (is_same_v) { + return "dog"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"dog"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"dog"; + } else if constexpr (is_same_v) { + return U"dog"; + } else { + return L"dog"; + } +} + +template +constexpr auto get_no_needle() { + if constexpr (is_same_v) { + return "vxz"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"vxz"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"vxz"; + } else if constexpr (is_same_v) { + return U"vxz"; + } else { + return L"vxz"; + } +} + +template +constexpr auto get_cute_and_scratchy() { + if constexpr (is_same_v) { + return "cute and scratchy "; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"cute and scratchy "; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"cute and scratchy "; + } else if constexpr (is_same_v) { + return U"cute and scratchy "; + } else { + return L"cute and scratchy "; + } +} + +template +struct string_view_convertible { + _CONSTEXPR20_CONTAINER operator basic_string_view() const { + if constexpr (is_same_v) { + return view_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u16; + } else if constexpr (is_same_v) { + return view_input_u32; + } else { + return view_input_w; + } + } +}; + +// TRANSITION, EDG concepts support +template +constexpr bool equalRanges(const Range1& range1, const Range2& range2) noexcept { +#ifdef __cpp_lib_concepts + return ranges::equal(range1, range2); +#else // ^^^ __cpp_lib_concepts ^^^ / vvv !__cpp_lib_concepts vvv + return equal(begin(range1), end(range1), begin(range2), end(range2)); +#endif // !__cpp_lib_concepts +} + +template +_CONSTEXPR20_CONTAINER bool test_interface() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 + using str = basic_string; + + { // constructors + // range constructors + str literal_constructed{get_literal_input()}; + assert(equalRanges(literal_constructed, get_view_input())); + + str view_constructed(get_view_input()); + assert(equalRanges(view_constructed, literal_constructed)); + + str initializer_list_constructed({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(initializer_list_constructed, "meow"sv)); + + // special member functions + str default_constructed; + assert(default_constructed.empty()); + + str copy_constructed(literal_constructed); + assert(equalRanges(copy_constructed, literal_constructed)); + + str move_constructed(move(copy_constructed)); + assert(equalRanges(move_constructed, literal_constructed)); + assert(copy_constructed.empty()); + + str copy_assigned(get_dog()); + copy_assigned = literal_constructed; + assert(equalRanges(copy_assigned, literal_constructed)); + + str move_assigned(get_dog()); + move_assigned = move(copy_assigned); + assert(equalRanges(move_assigned, literal_constructed)); + assert(copy_assigned.empty()); + + // Other constructors + str size_value_constructed(5, CharType{'a'}); + assert(equalRanges(size_value_constructed, "aaaaa"sv)); + + str copy_start_constructed(literal_constructed, 2); + assert(equalRanges(copy_start_constructed, "llo fluffy kittens"sv)); + + str copy_start_length_constructed(literal_constructed, 2, 3); + assert(equalRanges(copy_start_length_constructed, "llo"sv)); + + str ptr_size_constructed(get_literal_input(), 2); + assert(equalRanges(ptr_size_constructed, "He"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1270433 + str iterator_constructed(literal_constructed.begin(), literal_constructed.end()); + assert(equalRanges(iterator_constructed, literal_constructed)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + const string_view_convertible convertible; + str conversion_constructed(convertible); + assert(equalRanges(conversion_constructed, literal_constructed)); + + str conversion_start_length_constructed(convertible, 2, 3); + assert(equalRanges(conversion_start_length_constructed, "llo"sv)); + } + + { // allocator constructors + allocator alloc; + + // range constructors + str literal_constructed{get_literal_input(), alloc}; + assert(equalRanges(literal_constructed, get_view_input())); + + str view_constructed{get_view_input(), alloc}; + assert(equalRanges(view_constructed, literal_constructed)); + + str initializer_list_constructed({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}, alloc); + assert(equalRanges(initializer_list_constructed, "meow"sv)); + + // special member functions + str default_constructed{alloc}; + assert(default_constructed.empty()); + + str copy_constructed{literal_constructed, alloc}; + assert(equalRanges(copy_constructed, literal_constructed)); + + str move_constructed{move(copy_constructed), alloc}; + assert(equalRanges(move_constructed, literal_constructed)); + assert(copy_constructed.empty()); + + // Other constructors + str size_value_constructed(5, CharType{'a'}, alloc); + assert(equalRanges(size_value_constructed, "aaaaa"sv)); + + str copy_start_constructed(literal_constructed, 2, alloc); + assert(equalRanges(copy_start_constructed, "llo fluffy kittens"sv)); + + str copy_start_length_constructed(literal_constructed, 2, 3, alloc); + assert(equalRanges(copy_start_length_constructed, "llo"sv)); + + str ptr_size_constructed(get_literal_input(), 2, alloc); + assert(equalRanges(ptr_size_constructed, "He"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1270433 + str iterator_constructed(literal_constructed.begin(), literal_constructed.end(), alloc); + assert(equalRanges(iterator_constructed, literal_constructed)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + const string_view_convertible convertible; + str conversion_constructed(convertible, alloc); + assert(equalRanges(conversion_constructed, literal_constructed)); + + str conversion_start_length_constructed(convertible, 2, 3, alloc); + assert(equalRanges(conversion_start_length_constructed, "llo"sv)); + } + + { // assignment operator + str literal_constructed = get_literal_input(); + + str copy_assigned; + copy_assigned = literal_constructed; + assert(equalRanges(copy_assigned, literal_constructed)); + + str move_assigned; + move_assigned = move(copy_assigned); + assert(equalRanges(move_assigned, literal_constructed)); + assert(copy_assigned.empty()); + + str literal_assigned; + literal_assigned = get_literal_input(); + assert(equalRanges(literal_assigned, literal_constructed)); + + str char_assigned; + char_assigned = CharType{'!'}; + assert(equalRanges(char_assigned, "!"sv)); + + str initializer_list_assigned; + initializer_list_assigned = {CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}; + assert(equalRanges(initializer_list_assigned, "meow"sv)); + + const string_view_convertible convertible; + str conversion_assigned; + conversion_assigned = convertible; + assert(equalRanges(conversion_assigned, literal_constructed)); + } + + { // assign + str literal_constructed = get_literal_input(); + + str assign_size_char; + assign_size_char.assign(5, CharType{'a'}); + assert(equalRanges(assign_size_char, "aaaaa"sv)); + + str assign_str; + assign_str.assign(literal_constructed); + assert(equalRanges(assign_str, literal_constructed)); + + str assign_str_pos; + assign_str_pos.assign(literal_constructed, 2); + assert(equalRanges(assign_str_pos, "llo fluffy kittens"sv)); + + str assign_str_pos_len; + assign_str_pos_len.assign(literal_constructed, 2, 3); + assert(equalRanges(assign_str_pos_len, "llo"sv)); + + str assign_moved_str; + assign_moved_str.assign(move(assign_str_pos_len)); + assert(equalRanges(assign_moved_str, "llo"sv)); + assert(assign_str_pos_len.empty()); + + str assign_literal; + assign_literal.assign(get_literal_input()); + assert(equalRanges(assign_literal, literal_constructed)); + + str assign_literal_count; + assign_literal_count.assign(get_literal_input(), 2); + assert(equalRanges(assign_literal_count, "He"sv)); + + str assign_iterator; + assign_iterator.assign(begin(get_view_input()), end(get_view_input())); + assert(equalRanges(assign_iterator, get_view_input())); + + str assign_initializer_list; + assign_initializer_list.assign({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(assign_initializer_list, "meow"sv)); + + const string_view_convertible convertible; + str assign_conversion; + assign_conversion.assign(convertible); + assert(equalRanges(assign_conversion, literal_constructed)); + + str assign_conversion_start_length; + assign_conversion_start_length.assign(convertible, 2, 3); + assert(equalRanges(assign_conversion_start_length, "llo"sv)); + } + + { // allocator + str default_constructed; + [[maybe_unused]] const auto alloc = default_constructed.get_allocator(); + static_assert(is_same_v, allocator>); + } + + { // access + str literal_constructed = get_literal_input(); + const str const_literal_constructed = get_literal_input(); + + const auto at = literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(at == CharType{'l'}); + + literal_constructed.at(2) = CharType{'v'}; + const auto at2 = literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(at2 == CharType{'v'}); + + const auto cat = const_literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(cat == CharType{'l'}); + + const auto op = literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(op == CharType{'l'}); + + literal_constructed[3] = CharType{'u'}; + const auto op2 = literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(op2 == CharType{'u'}); + + const auto cop = const_literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(cop == CharType{'l'}); + + const auto f = literal_constructed.front(); + static_assert(is_same_v, CharType>); + assert(f == CharType{'H'}); + + const auto cf = const_literal_constructed.front(); + static_assert(is_same_v, CharType>); + assert(cf == CharType{'H'}); + + const auto b = literal_constructed.back(); + static_assert(is_same_v, CharType>); + assert(b == CharType{'s'}); + + const auto cb = const_literal_constructed.back(); + static_assert(is_same_v, CharType>); + assert(cb == CharType{'s'}); + + const auto d = literal_constructed.data(); + static_assert(is_same_v); + assert(*d == CharType{'H'}); + + const auto cd = const_literal_constructed.data(); + static_assert(is_same_v); + assert(*cd == CharType{'H'}); + + const auto cs = literal_constructed.c_str(); + static_assert(is_same_v); + assert(cs == literal_constructed.data()); + assert(char_traits::length(cs) == literal_constructed.size()); + } + + { // iterators + str literal_constructed = get_literal_input(); + const str const_literal_constructed = get_literal_input(); + + const auto b = literal_constructed.begin(); + static_assert(is_same_v, typename str::iterator>); + assert(*b == CharType{'H'}); + + const auto cb = literal_constructed.cbegin(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*cb == CharType{'H'}); + + const auto cb2 = const_literal_constructed.begin(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*cb2 == CharType{'H'}); + + const auto e = literal_constructed.end(); + static_assert(is_same_v, typename str::iterator>); + assert(*prev(e) == CharType{'s'}); + + const auto ce = literal_constructed.cend(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*prev(ce) == CharType{'s'}); + + const auto ce2 = const_literal_constructed.end(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*prev(ce2) == CharType{'s'}); + + const auto rb = literal_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*rb == CharType{'s'}); + + const auto crb = literal_constructed.crbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb == CharType{'s'}); + + const auto crb2 = const_literal_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb2 == CharType{'s'}); + + const auto re = literal_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(re) == CharType{'H'}); + + const auto cre = literal_constructed.crend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre) == CharType{'H'}); + + const auto cre2 = const_literal_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre2) == CharType{'H'}); + } + + { // capacity + str literal_constructed = get_literal_input(); + + const auto e = literal_constructed.empty(); + static_assert(is_same_v, bool>); + assert(!e); + + const auto s = literal_constructed.size(); + static_assert(is_same_v, size_t>); + assert(s == size(get_view_input())); + + const auto l = literal_constructed.length(); + static_assert(is_same_v, size_t>); + assert(l == s); + + const auto ms = literal_constructed.max_size(); + static_assert(is_same_v, size_t>); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(ms == static_cast(-1) / sizeof(CharType) - 1); + } else { + assert(ms == static_cast(-1) / 2); + } + + literal_constructed.reserve(20); + + const auto c = literal_constructed.capacity(); + static_assert(is_same_v, size_t>); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(c == 23); + } else { + assert(c == 31); + } + + // make reserve actually do work + literal_constructed.reserve(35); + const auto c2 = literal_constructed.capacity(); + if constexpr (is_same_v || is_same_v) { + assert(c2 == 39); + } else if constexpr (is_same_v) { + assert(c2 == 35); + } else { + assert(c2 == 47); + } + + // shrink back to previous size + literal_constructed.shrink_to_fit(); + + const auto c3 = literal_constructed.capacity(); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(c3 == 23); + } else { + assert(c3 == 31); + } + + literal_constructed.erase(3); + literal_constructed.shrink_to_fit(); + + const auto c4 = literal_constructed.capacity(); + if (is_constant_evaluated()) { // check minimum allocation of _BUF_SIZE when constant evaluated + assert(c4 == 16 / sizeof(CharType)); + } else { + if constexpr (is_same_v || is_same_v) { + assert(c4 == 7); + } else if constexpr (is_same_v) { + assert(c4 == 3); + } else { + assert(c4 == 15); + } + } + } + + { // clear + str cleared = get_literal_input(); + cleared.clear(); + assert(cleared.empty()); + assert(cleared.capacity() == str{get_literal_input()}.capacity()); + } + + { // insert + str insert_char = get_literal_input(); + const CharType to_be_inserted = CharType{','}; + insert_char.insert(insert_char.begin() + 5, to_be_inserted); + assert(equalRanges(insert_char, "Hello, fluffy kittens"sv)); + + str insert_const_char = get_literal_input(); + insert_const_char.insert(insert_const_char.cbegin() + 5, to_be_inserted); + assert(equalRanges(insert_const_char, "Hello, fluffy kittens"sv)); + + str insert_char_rvalue = get_literal_input(); + insert_char_rvalue.insert(insert_char_rvalue.begin() + 5, CharType{','}); + assert(equalRanges(insert_char_rvalue, "Hello, fluffy kittens"sv)); + + str insert_const_char_rvalue = get_literal_input(); + insert_const_char_rvalue.insert(insert_const_char_rvalue.cbegin() + 5, CharType{','}); + assert(equalRanges(insert_const_char_rvalue, "Hello, fluffy kittens"sv)); + + str insert_range(2, CharType{'b'}); + const auto it = insert_range.insert( + insert_range.begin() + 1, begin(get_view_input()), end(get_view_input())); + assert(it == insert_range.begin() + 1); + assert(equalRanges(insert_range, "bHello fluffy kittensb"sv)); + + str insert_const_range(2, CharType{'b'}); + const auto cit = insert_const_range.insert( + insert_const_range.cbegin() + 1, begin(get_view_input()), end(get_view_input())); + assert(cit == insert_const_range.cbegin() + 1); + assert(equalRanges(insert_const_range, "bHello fluffy kittensb"sv)); + + str insert_initializer_list = get_literal_input(); + const auto it_ilist = insert_initializer_list.insert(insert_initializer_list.begin() + 6, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(it_ilist == insert_initializer_list.begin() + 6); + assert(equalRanges(insert_initializer_list, "Hello cute fluffy kittens"sv)); + + str insert_const_initializer_list = get_literal_input(); + const auto cit_ilist = insert_const_initializer_list.insert(insert_const_initializer_list.cbegin() + 6, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(cit_ilist == insert_const_initializer_list.cbegin() + 6); + assert(equalRanges(insert_const_initializer_list, "Hello cute fluffy kittens"sv)); + + str insert_pos_str = get_literal_input(); + const str to_insert = get_cute_and_scratchy(); + insert_pos_str.insert(6, to_insert); + assert(equalRanges(insert_pos_str, "Hello cute and scratchy fluffy kittens"sv)); + + str insert_pos_substr = get_literal_input(); + insert_pos_substr.insert(6, to_insert, 0, 5); + assert(equalRanges(insert_pos_substr, "Hello cute fluffy kittens"sv)); + + const string_view_convertible convertible; + str insert_pos_conversion = get_literal_input(); + insert_pos_conversion.insert(6, convertible); + assert(equalRanges(insert_pos_conversion, "Hello Hello fluffy kittensfluffy kittens"sv)); + + str insert_pos_conversion_substr = get_literal_input(); + insert_pos_conversion_substr.insert(6, convertible, 6, 7); + assert(equalRanges(insert_pos_conversion_substr, "Hello fluffy fluffy kittens"sv)); + + str insert_pos_literal = get_literal_input(); + insert_pos_literal.insert(6, get_literal_input()); + assert(equalRanges(insert_pos_literal, "Hello Hello fluffy kittensfluffy kittens"sv)); + + str insert_pos_literal_substr = get_literal_input(); + insert_pos_literal_substr.insert(6, get_literal_input(), 6); + assert(equalRanges(insert_pos_literal_substr, "Hello Hello fluffy kittens"sv)); + + str insert_pos_count_char = get_literal_input(); + insert_pos_count_char.insert(6, 3, CharType{'b'}); + assert(equalRanges(insert_pos_count_char, "Hello bbbfluffy kittens"sv)); + + str insert_iter_count_char = get_literal_input(); + insert_iter_count_char.insert(begin(insert_iter_count_char) + 5, 4, CharType{'o'}); + assert(equalRanges(insert_iter_count_char, "Hellooooo fluffy kittens"sv)); + } + + { // erase + str erase_pos_count = get_literal_input(); + erase_pos_count.erase(0, 6); + assert(equalRanges(erase_pos_count, "fluffy kittens"sv)); + + str erase_iter = get_literal_input(); + erase_iter.erase(erase_iter.begin()); + assert(equalRanges(erase_iter, "ello fluffy kittens"sv)); + + str erase_const_iter = get_literal_input(); + erase_const_iter.erase(erase_const_iter.cbegin()); + assert(equalRanges(erase_const_iter, "ello fluffy kittens"sv)); + + str erase_iter_iter = get_literal_input(); + erase_iter_iter.erase(erase_iter_iter.begin(), erase_iter_iter.begin() + 6); + assert(equalRanges(erase_iter_iter, "fluffy kittens"sv)); + + str erase_const_iter_iter = get_literal_input(); + erase_const_iter_iter.erase(erase_const_iter_iter.cbegin(), erase_const_iter_iter.cbegin() + 6); + assert(equalRanges(erase_const_iter_iter, "fluffy kittens"sv)); + + str erased_free = get_literal_input(); + erase(erased_free, CharType{'l'}); + assert(equalRanges(erased_free, "Heo fuffy kittens"sv)); + + str erased_free_if = get_literal_input(); + erase_if(erased_free_if, [](const CharType val) { return val == CharType{'t'}; }); + assert(equalRanges(erased_free_if, "Hello fluffy kiens"sv)); + } + + { // push_back / pop_back + str pushed; + pushed.push_back(CharType{'y'}); + assert(pushed.size() == 1); + assert(pushed.back() == CharType{'y'}); + + const CharType to_be_pushed = CharType{'z'}; + pushed.push_back(to_be_pushed); + assert(pushed.size() == 2); + assert(pushed.back() == CharType{'z'}); + + pushed.pop_back(); + assert(pushed.size() == 1); + assert(pushed.back() == CharType{'y'}); + } + + { // append + const str literal_constructed = get_literal_input(); + + str append_size_char(2, CharType{'b'}); + append_size_char.append(5, CharType{'a'}); + assert(equalRanges(append_size_char, "bbaaaaa"sv)); + + str append_str(2, CharType{'b'}); + append_str.append(literal_constructed); + assert(equalRanges(append_str, "bbHello fluffy kittens"sv)); + + str append_str_pos(2, CharType{'b'}); + append_str_pos.append(literal_constructed, 2); + assert(equalRanges(append_str_pos, "bbllo fluffy kittens"sv)); + + str append_str_pos_len(2, CharType{'b'}); + append_str_pos_len.append(literal_constructed, 2, 3); + assert(equalRanges(append_str_pos_len, "bbllo"sv)); + + str append_literal(2, CharType{'b'}); + append_literal.append(get_literal_input()); + assert(equalRanges(append_literal, "bbHello fluffy kittens"sv)); + + str append_literal_count(2, CharType{'b'}); + append_literal_count.append(get_literal_input(), 2); + assert(equalRanges(append_literal_count, "bbHe"sv)); + + str append_iterator(2, CharType{'b'}); + append_iterator.append(begin(get_view_input()), end(get_view_input())); + assert(equalRanges(append_iterator, "bbHello fluffy kittens"sv)); + + str append_initializer_list(2, CharType{'b'}); + append_initializer_list.append({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(append_initializer_list, "bbmeow"sv)); + + const string_view_convertible convertible; + str append_conversion(2, CharType{'b'}); + append_conversion.append(convertible); + assert(equalRanges(append_conversion, "bbHello fluffy kittens"sv)); + + str append_conversion_start_length(2, CharType{'b'}); + append_conversion_start_length.append(convertible, 2, 3); + assert(equalRanges(append_conversion_start_length, "bbllo"sv)); + } + + { // operator+= + str literal_constructed = get_literal_input(); + + str plus_string(2, CharType{'b'}); + plus_string += literal_constructed; + assert(equalRanges(plus_string, "bbHello fluffy kittens"sv)); + + str plus_character(2, CharType{'b'}); + plus_character += CharType{'a'}; + assert(equalRanges(plus_character, "bba"sv)); + + str plus_literal(2, CharType{'b'}); + plus_literal += get_literal_input(); + assert(equalRanges(plus_literal, "bbHello fluffy kittens"sv)); + + str plus_initializer_list(2, CharType{'b'}); + plus_initializer_list += {CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}; + assert(equalRanges(plus_initializer_list, "bbmeow"sv)); + + const string_view_convertible convertible; + str plus_conversion(2, CharType{'b'}); + plus_conversion += convertible; + assert(equalRanges(plus_conversion, "bbHello fluffy kittens"sv)); + } + + { // compare + const str first = get_literal_input(); + const str second = get_cat(); + + const int comp_str_eq = first.compare(first); + assert(comp_str_eq == 0); + + const int comp_str_less = first.compare(second); + assert(comp_str_less == -1); + + const int comp_str_greater = second.compare(first); + assert(comp_str_greater == 1); + + const int comp_pos_count_str_eq = first.compare(3, 7, first.substr(3, 7)); + assert(comp_pos_count_str_eq == 0); + + const int comp_pos_count_str_less = first.compare(0, 2, second); + assert(comp_pos_count_str_less == -1); + + const int comp_pos_count_str_greater = second.compare(0, 2, first); + assert(comp_pos_count_str_greater == 1); + + const int comp_pos_count_str_pos_eq = first.compare(3, 20, first, 3); + assert(comp_pos_count_str_pos_eq == 0); + + const int comp_pos_count_str_pos_less = first.compare(0, 2, second, 2); + assert(comp_pos_count_str_pos_less == -1); + + const int comp_pos_count_str_pos_greater = second.compare(0, 2, first, 6); + assert(comp_pos_count_str_pos_greater == 1); + + const int comp_pos_count_str_pos_count_eq = first.compare(3, 5, first, 3, 5); + assert(comp_pos_count_str_pos_count_eq == 0); + + const int comp_pos_count_str_pos_count_less = first.compare(0, 2, second, 2, 3); + assert(comp_pos_count_str_pos_count_less == -1); + + const int comp_pos_count_str_pos_count_greater = second.compare(0, 2, first, 6, 4); + assert(comp_pos_count_str_pos_count_greater == 1); + + const int comp_literal_eq = first.compare(get_literal_input()); + assert(comp_literal_eq == 0); + + const int comp_literal_less = first.compare(get_cat()); + assert(comp_literal_less == -1); + + const int comp_literal_greater = second.compare(get_literal_input()); + assert(comp_literal_greater == 1); + + const int comp_pos_count_literal_eq = first.compare(13, 6, get_cat()); + assert(comp_pos_count_literal_eq == 0); + + const int comp_pos_count_literal_less = first.compare(0, 2, get_cat()); + assert(comp_pos_count_literal_less == -1); + + const int comp_pos_count_literal_greater = second.compare(0, 2, get_literal_input()); + assert(comp_pos_count_literal_greater == 1); + + const int comp_pos_count_literal_count_eq = first.compare(13, 5, get_cat(), 5); + assert(comp_pos_count_literal_count_eq == 0); + + const int comp_pos_count_literal_count_less = first.compare(0, 2, get_cat(), 2); + assert(comp_pos_count_literal_count_less == -1); + + const int comp_pos_count_literal_count_greater = second.compare(0, 2, get_literal_input(), 6); + assert(comp_pos_count_literal_count_greater == 1); + + const int comp_pos_count_literal_pos_count_eq = first.compare(3, 5, get_literal_input(), 3, 5); + assert(comp_pos_count_literal_pos_count_eq == 0); + + const int comp_pos_count_literal_pos_count_less = first.compare(0, 2, get_cat(), 2, 3); + assert(comp_pos_count_literal_pos_count_less == -1); + + const int comp_pos_count_literal_pos_count_greater = second.compare(0, 2, get_literal_input(), 6, 4); + assert(comp_pos_count_literal_pos_count_greater == 1); + + const string_view_convertible convertible; + const int comp_conversion_eq = first.compare(convertible); + assert(comp_conversion_eq == 0); + + const int comp_conversion_less = first.compare(second); + assert(comp_conversion_less == -1); + + const int comp_conversion_greater = second.compare(convertible); + assert(comp_conversion_greater == 1); + + const int comp_pos_count_conversion_eq = first.compare(0, 20, convertible); + assert(comp_pos_count_conversion_eq == 0); + + const int comp_pos_count_conversion_less = first.compare(5, 4, convertible); + assert(comp_pos_count_conversion_less == -1); + + const int comp_pos_count_conversion_greater = second.compare(0, 2, convertible); + assert(comp_pos_count_conversion_greater == 1); + + const int comp_pos_count_conversion_pos_eq = first.compare(3, 20, convertible, 3); + assert(comp_pos_count_conversion_pos_eq == 0); + + const int comp_pos_count_conversion_pos_less = first.compare(0, 2, second, 2); + assert(comp_pos_count_conversion_pos_less == -1); + + const int comp_pos_count_conversion_pos_greater = second.compare(0, 2, convertible, 6); + assert(comp_pos_count_conversion_pos_greater == 1); + + const int comp_pos_count_conversion_pos_count_eq = first.compare(3, 5, convertible, 3, 5); + assert(comp_pos_count_conversion_pos_count_eq == 0); + + const int comp_pos_count_conversion_pos_count_less = first.compare(0, 2, second, 2, 3); + assert(comp_pos_count_conversion_pos_count_less == -1); + + const int comp_pos_count_conversion_pos_count_greater = second.compare(0, 2, convertible, 6, 4); + assert(comp_pos_count_conversion_pos_count_greater == 1); + } + + { // starts_with + const str starts = get_literal_input(); + const str input_string_true = starts.substr(0, 5); + assert(starts.starts_with(input_string_true)); + + const str input_string_false = get_cat(); + assert(!starts.starts_with(input_string_false)); + + assert(starts.starts_with(CharType{'H'})); + assert(!starts.starts_with(CharType{'h'})); + + assert(starts.starts_with(get_literal_input())); + assert(!input_string_false.starts_with(get_literal_input())); + } + + { // ends_with + const str ends = get_literal_input(); + const str input_string_true = ends.substr(5); + assert(ends.ends_with(input_string_true)); + + const str input_string_false = get_cat(); + assert(!ends.ends_with(input_string_false)); + + assert(ends.ends_with(CharType{'s'})); + assert(!ends.ends_with(CharType{'S'})); + + assert(ends.ends_with(get_literal_input())); + assert(!input_string_false.ends_with(get_literal_input())); + } + + { // replace + const str input = get_dog(); + + str replaced_pos_count_str = get_literal_input(); + replaced_pos_count_str.replace(13, 7, input); + assert(equalRanges(replaced_pos_count_str, "Hello fluffy dog"sv)); + + str replaced_pos_count_str_shift = get_literal_input(); + replaced_pos_count_str_shift.replace(13, 2, input); + assert(equalRanges(replaced_pos_count_str_shift, "Hello fluffy dogttens"sv)); + + str replaced_iter_str = get_literal_input(); + replaced_iter_str.replace(replaced_iter_str.cbegin() + 13, replaced_iter_str.cend(), input); + assert(equalRanges(replaced_iter_str, "Hello fluffy dog"sv)); + + str replaced_iter_str_shift = get_literal_input(); + replaced_iter_str_shift.replace( + replaced_iter_str_shift.cbegin() + 13, replaced_iter_str_shift.cbegin() + 15, input); + assert(equalRanges(replaced_iter_str_shift, "Hello fluffy dogttens"sv)); + + str replaced_pos_count_str_pos_count = get_literal_input(); + replaced_pos_count_str_pos_count.replace(13, 7, input, 1); + assert(equalRanges(replaced_pos_count_str_pos_count, "Hello fluffy og"sv)); + + str replaced_pos_count_str_pos_count_less = get_literal_input(); + replaced_pos_count_str_pos_count_less.replace(13, 2, input, 1, 2); + assert(equalRanges(replaced_pos_count_str_pos_count_less, "Hello fluffy ogttens"sv)); + + str replaced_iter_iter = get_literal_input(); + replaced_iter_iter.replace( + replaced_iter_iter.cbegin() + 13, replaced_iter_iter.cend(), input.begin(), input.end()); + assert(equalRanges(replaced_iter_iter, "Hello fluffy dog"sv)); + + str replaced_iter_iter_less = get_literal_input(); + replaced_iter_iter_less.replace(replaced_iter_iter_less.cbegin() + 13, replaced_iter_iter_less.cbegin() + 15, + input.begin() + 1, input.end()); + assert(equalRanges(replaced_iter_iter_less, "Hello fluffy ogttens"sv)); + + str replaced_pos_count_literal = get_literal_input(); + replaced_pos_count_literal.replace(13, 2, get_dog()); + assert(equalRanges(replaced_pos_count_literal, "Hello fluffy dogttens"sv)); + + str replaced_pos_count_literal_count = get_literal_input(); + replaced_pos_count_literal_count.replace(13, 2, get_dog(), 2); + assert(equalRanges(replaced_pos_count_literal_count, "Hello fluffy dottens"sv)); + + str replaced_iter_literal = get_literal_input(); + replaced_iter_literal.replace( + replaced_iter_literal.cbegin() + 13, replaced_iter_literal.cbegin() + 15, get_dog()); + assert(equalRanges(replaced_iter_literal, "Hello fluffy dogttens"sv)); + + str replaced_iter_literal_count = get_literal_input(); + replaced_iter_literal_count.replace(replaced_iter_literal_count.cbegin() + 13, + replaced_iter_literal_count.cbegin() + 15, get_dog(), 2); + assert(equalRanges(replaced_iter_literal_count, "Hello fluffy dottens"sv)); + + str replaced_pos_count_chars = get_literal_input(); + replaced_pos_count_chars.replace(13, 2, 5, CharType{'a'}); + assert(equalRanges(replaced_pos_count_chars, "Hello fluffy aaaaattens"sv)); + + str replaced_iter_chars = get_literal_input(); + replaced_iter_chars.replace( + replaced_iter_chars.cbegin() + 13, replaced_iter_chars.cbegin() + 15, 5, CharType{'a'}); + assert(equalRanges(replaced_iter_chars, "Hello fluffy aaaaattens"sv)); + + str replaced_iter_init = get_literal_input(); + replaced_iter_init.replace(replaced_iter_init.cbegin() + 13, replaced_iter_init.cbegin() + 15, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(equalRanges(replaced_iter_init, "Hello fluffy cute ttens"sv)); + + const string_view_convertible convertible; + str replaced_pos_count_conversion = get_dog(); + replaced_pos_count_conversion.replace(1, 5, convertible); + assert(equalRanges(replaced_pos_count_conversion, "dHello fluffy kittens"sv)); + + str replaced_iter_conversion = get_dog(); + replaced_iter_conversion.replace( + replaced_iter_conversion.cbegin() + 1, replaced_iter_conversion.cbegin() + 2, convertible); + assert(equalRanges(replaced_iter_conversion, "dHello fluffy kittensg"sv)); + + str replaced_pos_count_conversion_pos = get_dog(); + replaced_pos_count_conversion_pos.replace(1, 5, convertible, 6); + assert(equalRanges(replaced_pos_count_conversion_pos, "dfluffy kittens"sv)); + + str replaced_pos_count_conversion_pos_count = get_dog(); + replaced_pos_count_conversion_pos_count.replace(1, 5, convertible, 6, 6); + assert(equalRanges(replaced_pos_count_conversion_pos_count, "dfluffy"sv)); + } + + { // substr + const str input = get_literal_input(); + + const str substr_pos = input.substr(6); + assert(equalRanges(substr_pos, "fluffy kittens"sv)); + + const str substr_pos_count = input.substr(6, 6); + assert(equalRanges(substr_pos_count, "fluffy"sv)); + } + + { // copy + const str input = get_literal_input(); + + CharType copy_count[5]; + input.copy(copy_count, 5); + assert(equalRanges(copy_count, "Hello"sv)); + + CharType copy_count_pos[6]; + input.copy(copy_count_pos, 6, 6); + assert(equalRanges(copy_count_pos, "fluffy"sv)); + } + + { // resize + str resized = get_literal_input(); + resized.resize(3); + assert(equalRanges(resized, "Hel"sv)); + + resized.resize(6, CharType{'a'}); + assert(equalRanges(resized, "Helaaa"sv)); + } + + { // swap + constexpr basic_string_view expected_first = get_dog(); + constexpr basic_string_view expected_second = get_cat(); + str first{get_cat()}; + str second{get_dog()}; + swap(first, second); + + assert(equalRanges(first, expected_first)); + assert(equalRanges(second, expected_second)); + + first.swap(second); + assert(equalRanges(second, expected_first)); + assert(equalRanges(first, expected_second)); + } + + { // find + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_str = input.find(needle); + assert(find_str == 13u); + + const auto find_str_none = input.find(no_needle); + assert(find_str_none == str::npos); + + const auto find_str_pos = input.find(needle, 6); + assert(find_str_pos == 13u); + + const auto find_str_pos_none = input.find(needle, 14); + assert(find_str_pos_none == str::npos); + + const auto find_str_overflow = input.find(needle, 50); + assert(find_str_overflow == str::npos); + + const auto find_literal = input.find(get_cat()); + assert(find_literal == 13u); + + const auto find_literal_none = input.find(get_dog()); + assert(find_literal_none == str::npos); + + const auto find_literal_pos = input.find(get_cat(), 6); + assert(find_literal_pos == 13u); + + const auto find_literal_pos_none = input.find(get_cat(), 14); + assert(find_literal_pos_none == str::npos); + + const auto find_literal_overflow = input.find(get_cat(), 50); + assert(find_literal_overflow == str::npos); + + const auto find_literal_pos_count = input.find(get_cat(), 6, 4); + assert(find_literal_pos_count == 13u); + + const auto find_literal_pos_count_none = input.find(get_dog(), 14, 4); + assert(find_literal_pos_count_none == str::npos); + + const auto find_char = input.find(CharType{'e'}); + assert(find_char == 1u); + + const auto find_char_none = input.find(CharType{'x'}); + assert(find_char_none == str::npos); + + const auto find_char_pos = input.find(CharType{'e'}, 4); + assert(find_char_pos == 17u); + + const string_view_convertible convertible; + const auto find_convertible = input.find(convertible); + assert(find_convertible == 0); + + const auto find_convertible_pos = input.find(convertible, 2); + assert(find_convertible_pos == str::npos); + } + + { // rfind + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto rfind_str = input.rfind(needle); + assert(rfind_str == 13u); + + const auto rfind_str_none = input.rfind(no_needle); + assert(rfind_str_none == str::npos); + + const auto rfind_str_pos = input.rfind(needle, 15); + assert(rfind_str_pos == 13u); + + const auto rfind_str_pos_none = input.rfind(needle, 6); + assert(rfind_str_pos_none == str::npos); + + const auto rfind_str_overflow = input.rfind(needle, 50); + assert(rfind_str_overflow == 13u); + + const auto rfind_literal = input.rfind(get_cat()); + assert(rfind_literal == 13u); + + const auto rfind_literal_none = input.rfind(get_dog()); + assert(rfind_literal_none == str::npos); + + const auto rfind_literal_pos = input.rfind(get_cat(), 15); + assert(rfind_literal_pos == 13u); + + const auto rfind_literal_pos_none = input.rfind(get_cat(), 6); + assert(rfind_literal_pos_none == str::npos); + + const auto rfind_literal_overflow = input.rfind(get_cat(), 50); + assert(rfind_literal_overflow == 13u); + + const auto rfind_literal_pos_count = input.rfind(get_cat(), 15, 4); + assert(rfind_literal_pos_count == 13u); + + const auto rfind_literal_pos_count_none = input.rfind(get_dog(), 6, 4); + assert(rfind_literal_pos_count_none == str::npos); + + const auto rfind_char = input.rfind(CharType{'e'}); + assert(rfind_char == 17u); + + const auto rfind_char_none = input.rfind(CharType{'x'}); + assert(rfind_char_none == str::npos); + + const auto rfind_char_pos = input.rfind(CharType{'e'}, 4); + assert(rfind_char_pos == 1u); + + const string_view_convertible convertible; + const auto rfind_convertible = input.rfind(convertible); + assert(rfind_convertible == 0); + + const auto rfind_convertible_pos = input.rfind(convertible, 5); + assert(rfind_convertible_pos == 0); + } + + { // find_first_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_first_of_str = input.find_first_of(needle); + assert(find_first_of_str == 1u); + + const auto find_first_of_str_none = input.find_first_of(no_needle); + assert(find_first_of_str_none == str::npos); + + const auto find_first_of_str_pos = input.find_first_of(needle, 6); + assert(find_first_of_str_pos == 13u); + + const auto find_first_of_str_pos_none = input.find_first_of(no_needle, 14); + assert(find_first_of_str_pos_none == str::npos); + + const auto find_first_of_str_overflow = input.find_first_of(needle, 50); + assert(find_first_of_str_overflow == str::npos); + + const auto find_first_of_literal = input.find_first_of(get_cat()); + assert(find_first_of_literal == 1u); + + const auto find_first_of_literal_none = input.find_first_of(get_no_needle()); + assert(find_first_of_literal_none == str::npos); + + const auto find_first_of_literal_pos = input.find_first_of(get_cat(), 6); + assert(find_first_of_literal_pos == 13u); + + const auto find_first_of_literal_pos_none = input.find_first_of(get_no_needle(), 14); + assert(find_first_of_literal_pos_none == str::npos); + + const auto find_first_of_literal_overflow = input.find_first_of(get_cat(), 50); + assert(find_first_of_literal_overflow == str::npos); + + const auto find_first_of_literal_pos_count = input.find_first_of(get_cat(), 6, 4); + assert(find_first_of_literal_pos_count == 13u); + + const auto find_first_of_literal_pos_count_none = input.find_first_of(get_no_needle(), 14, 4); + assert(find_first_of_literal_pos_count_none == str::npos); + + const auto find_first_of_char = input.find_first_of(CharType{'e'}); + assert(find_first_of_char == 1u); + + const auto find_first_of_char_none = input.find_first_of(CharType{'x'}); + assert(find_first_of_char_none == str::npos); + + const auto find_first_of_char_pos = input.find_first_of(CharType{'e'}, 4); + assert(find_first_of_char_pos == 17u); + + const string_view_convertible convertible; + const auto find_first_of_convertible = input.find_first_of(convertible); + assert(find_first_of_convertible == 0); + + const auto find_first_of_convertible_pos = input.find_first_of(convertible, 2); + assert(find_first_of_convertible_pos == 2u); + } + + { // find_first_not_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_first_not_of_str = input.find_first_not_of(needle); + assert(find_first_not_of_str == 0u); + + const auto find_first_not_of_str_none = input.find_first_not_of(input); + assert(find_first_not_of_str_none == str::npos); + + const auto find_first_not_of_str_pos = input.find_first_not_of(needle, 6); + assert(find_first_not_of_str_pos == 6u); + + const auto find_first_not_of_str_pos_none = input.find_first_not_of(input, 14); + assert(find_first_not_of_str_pos_none == str::npos); + + const auto find_first_not_of_str_overflow = input.find_first_not_of(needle, 50); + assert(find_first_not_of_str_overflow == str::npos); + + const auto find_first_not_of_literal = input.find_first_not_of(get_cat()); + assert(find_first_not_of_literal == 0u); + + const auto find_first_not_of_literal_none = input.find_first_not_of(get_literal_input()); + assert(find_first_not_of_literal_none == str::npos); + + const auto find_first_not_of_literal_pos = input.find_first_not_of(get_cat(), 6); + assert(find_first_not_of_literal_pos == 6u); + + const auto find_first_not_of_literal_pos_none = input.find_first_not_of(get_literal_input(), 2); + assert(find_first_not_of_literal_pos_none == str::npos); + + const auto find_first_not_of_literal_overflow = input.find_first_not_of(get_cat(), 50); + assert(find_first_not_of_literal_overflow == str::npos); + + const auto find_first_not_of_literal_pos_count = input.find_first_not_of(get_cat(), 6, 4); + assert(find_first_not_of_literal_pos_count == 6u); + + const auto find_first_not_of_literal_pos_count_none = + input.find_first_not_of(get_literal_input(), 14, 20); + assert(find_first_not_of_literal_pos_count_none == str::npos); + + const auto find_first_not_of_char = input.find_first_not_of(CharType{'H'}); + assert(find_first_not_of_char == 1u); + + const auto find_first_not_of_char_pos = input.find_first_not_of(CharType{'e'}, 1); + assert(find_first_not_of_char_pos == 2u); + + const string_view_convertible convertible; + const auto find_first_not_of_convertible = input.find_first_not_of(convertible); + assert(find_first_not_of_convertible == str::npos); + + const auto find_first_not_of_convertible_pos = input.find_first_not_of(convertible, 2); + assert(find_first_not_of_convertible_pos == str::npos); + } + + { // find_last_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_last_of_str = input.find_last_of(needle); + assert(find_last_of_str == 18u); + + const auto find_last_of_str_none = input.find_last_of(no_needle); + assert(find_last_of_str_none == str::npos); + + const auto find_last_of_str_pos = input.find_last_of(needle, 6); + assert(find_last_of_str_pos == 1u); + + const auto find_last_of_str_pos_none = input.find_last_of(no_needle, 14); + assert(find_last_of_str_pos_none == str::npos); + + const auto find_last_of_str_overflow = input.find_last_of(needle, 50); + assert(find_last_of_str_overflow == 18u); + + const auto find_last_of_literal = input.find_last_of(get_cat()); + assert(find_last_of_literal == 18u); + + const auto find_last_of_literal_none = input.find_last_of(get_no_needle()); + assert(find_last_of_literal_none == str::npos); + + const auto find_last_of_literal_pos = input.find_last_of(get_cat(), 6); + assert(find_last_of_literal_pos == 1u); + + const auto find_last_of_literal_pos_none = input.find_last_of(get_no_needle(), 14); + assert(find_last_of_literal_pos_none == str::npos); + + const auto find_last_of_literal_overflow = input.find_last_of(get_cat(), 50); + assert(find_last_of_literal_overflow == 18u); + + const auto find_last_of_literal_pos_count = input.find_last_of(get_cat(), 6, 7); + assert(find_last_of_literal_pos_count == 1u); + + const auto find_last_of_literal_pos_count_none = input.find_last_of(get_no_needle(), 14, 4); + assert(find_last_of_literal_pos_count_none == str::npos); + + const auto find_last_of_char = input.find_last_of(CharType{'e'}); + assert(find_last_of_char == 17u); + + const auto find_last_of_char_none = input.find_last_of(CharType{'x'}); + assert(find_last_of_char_none == str::npos); + + const auto find_last_of_char_pos = input.find_last_of(CharType{'e'}, 4); + assert(find_last_of_char_pos == 1u); + + const string_view_convertible convertible; + const auto find_last_of_convertible = input.find_last_of(convertible); + assert(find_last_of_convertible == 19u); + + const auto find_last_of_convertible_pos = input.find_last_of(convertible, 4); + assert(find_last_of_convertible_pos == 4u); + } + + { // find_last_not_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_last_not_of_str = input.find_last_not_of(needle); + assert(find_last_not_of_str == 19u); + + const auto find_last_not_of_str_none = input.find_last_not_of(input); + assert(find_last_not_of_str_none == str::npos); + + const auto find_last_not_of_str_pos = input.find_last_not_of(needle, 6); + assert(find_last_not_of_str_pos == 6u); + + const auto find_last_not_of_str_pos_none = input.find_last_not_of(input, 14); + assert(find_last_not_of_str_pos_none == str::npos); + + const auto find_last_not_of_str_overflow = input.find_last_not_of(needle, 50); + assert(find_last_not_of_str_overflow == 19u); + + const auto find_last_not_of_literal = input.find_last_not_of(get_cat()); + assert(find_last_not_of_literal == 19u); + + const auto find_last_not_of_literal_none = input.find_last_not_of(get_literal_input()); + assert(find_last_not_of_literal_none == str::npos); + + const auto find_last_not_of_literal_pos = input.find_last_not_of(get_cat(), 6); + assert(find_last_not_of_literal_pos == 6u); + + const auto find_last_not_of_literal_pos_none = input.find_last_not_of(get_literal_input(), 2); + assert(find_last_not_of_literal_pos_none == str::npos); + + const auto find_last_not_of_literal_overflow = input.find_last_not_of(get_cat(), 50); + assert(find_last_not_of_literal_overflow == 19u); + + const auto find_last_not_of_literal_pos_count = input.find_last_not_of(get_cat(), 6, 4); + assert(find_last_not_of_literal_pos_count == 6u); + + const auto find_last_not_of_literal_pos_count_none = + input.find_last_not_of(get_literal_input(), 14, 20); + assert(find_last_not_of_literal_pos_count_none == str::npos); + + const auto find_last_not_of_char = input.find_last_not_of(CharType{'H'}); + assert(find_last_not_of_char == 19u); + + const auto find_last_not_of_char_pos = input.find_last_not_of(CharType{'e'}, 2); + assert(find_last_not_of_char_pos == 2u); + + const string_view_convertible convertible; + const auto find_last_not_of_convertible = input.find_last_not_of(convertible); + assert(find_last_not_of_convertible == str::npos); + + const auto find_last_not_of_convertible_pos = input.find_last_not_of(convertible, 2); + assert(find_last_not_of_convertible_pos == str::npos); + } + + { // operator+ + const str first = get_cat(); + const str second = get_dog(); + + const str op_str_str = first + second; + assert(equalRanges(op_str_str, "kittendog"sv)); + + const str op_str_literal = first + get_dog(); + assert(equalRanges(op_str_literal, "kittendog"sv)); + + const str op_str_char = first + CharType{'!'}; + assert(equalRanges(op_str_char, "kitten!"sv)); + + const str op_literal_str = get_cat() + second; + assert(equalRanges(op_literal_str, "kittendog"sv)); + + const str op_char_str = CharType{'!'} + second; + assert(equalRanges(op_char_str, "!dog"sv)); + + const str op_rstr_rstr = str{get_cat()} + str{get_dog()}; + assert(equalRanges(op_rstr_rstr, "kittendog"sv)); + + const str op_rstr_str = str{get_cat()} + second; + assert(equalRanges(op_rstr_str, "kittendog"sv)); + + const str op_rstr_literal = str{get_cat()} + get_dog(); + assert(equalRanges(op_rstr_literal, "kittendog"sv)); + + const str op_rstr_char = str{get_cat()} + CharType{'!'}; + assert(equalRanges(op_rstr_char, "kitten!"sv)); + + const str op_str_rstr = first + str{get_dog()}; + assert(equalRanges(op_str_rstr, "kittendog"sv)); + + const str op_literal_rstr = get_cat() + str{get_dog()}; + assert(equalRanges(op_literal_rstr, "kittendog"sv)); + + const str op_char_rstr = CharType{'!'} + str{get_dog()}; + assert(equalRanges(op_char_rstr, "!dog"sv)); + } + + { // comparison + str first(get_view_input()); + str second(get_view_input()); + str third{get_cat()}; + + const bool eq_str_str = first == second; + assert(eq_str_str); + + const bool ne_str_str = first != third; + assert(ne_str_str); + + const bool less_str_str = first < third; + assert(less_str_str); + + const bool less_eq_str_str = first <= third; + assert(less_eq_str_str); + + const bool greater_str_str = first > third; + assert(!greater_str_str); + + const bool greater_eq_str_str = first >= third; + assert(!greater_eq_str_str); + + const bool eq_str_literal = first == get_view_input(); + assert(eq_str_literal); + + const bool ne_str_literal = first != get_cat(); + assert(ne_str_literal); + + const bool less_str_literal = first < get_cat(); + assert(less_str_literal); + + const bool less_eq_str_literal = first <= get_cat(); + assert(less_eq_str_literal); + + const bool greater_str_literal = first > get_cat(); + assert(!greater_str_literal); + + const bool greater_eq_str_literal = first >= get_cat(); + assert(!greater_eq_str_literal); + + const bool eq_literal_str = get_view_input() == second; + assert(eq_literal_str); + + const bool ne_literal_str = get_view_input() != third; + assert(ne_literal_str); + + const bool less_literal_str = get_view_input() < third; + assert(less_literal_str); + + const bool less_eq_literal_str = get_view_input() <= third; + assert(less_eq_literal_str); + + const bool greater_literal_str = get_view_input() > third; + assert(!greater_literal_str); + + const bool greater_eq_literal_str = get_view_input() >= third; + assert(!greater_eq_literal_str); + + const strong_ordering spaceship_str_str_eq = first <=> second; + assert(spaceship_str_str_eq == strong_ordering::equal); + + const strong_ordering spaceship_str_str_less = first <=> third; + assert(spaceship_str_str_less == strong_ordering::less); + + const strong_ordering spaceship_str_str_greater = third <=> first; + assert(spaceship_str_str_greater == strong_ordering::greater); + + const strong_ordering spaceship_str_literal_eq = first <=> get_view_input(); + assert(spaceship_str_literal_eq == strong_ordering::equal); + + const strong_ordering spaceship_str_literal_less = first <=> get_cat(); + assert(spaceship_str_literal_less == strong_ordering::less); + + const strong_ordering spaceship_str_literal_greater = third <=> get_dog(); + assert(spaceship_str_literal_greater == strong_ordering::greater); + } + + { // basic_string_view conversion + str s = get_literal_input(); + basic_string_view sv = s; + assert(equalRanges(sv, "Hello fluffy kittens"sv)); + } +#endif // __EDG__ + return true; +} + +_CONSTEXPR20_CONTAINER bool test_udls() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 + assert(equalRanges("purr purr"s, "purr purr"sv)); +#ifdef __cpp_char8_t + assert(equalRanges(u8"purr purr"s, "purr purr"sv)); +#endif // __cpp_char8_t + assert(equalRanges(u"purr purr"s, "purr purr"sv)); + assert(equalRanges(U"purr purr"s, "purr purr"sv)); + assert(equalRanges(L"purr purr"s, "purr purr"sv)); +#endif // __EDG__ + return true; +} + +template +struct CharLikeType { + constexpr CharLikeType() = default; + constexpr CharLikeType(CharType cc) : c(cc) {} + CharType c; +}; + +template +_CONSTEXPR20_CONTAINER bool test_iterators() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 + using str = basic_string; + str literal_constructed = get_literal_input(); + + { // assignment + auto it = literal_constructed.begin(); + auto it2 = literal_constructed.end(); + auto cit = literal_constructed.cbegin(); + auto cit2 = literal_constructed.cend(); + + it = it2; + cit = cit2; + } + + { // op-> + basic_string> bs{CharType{'x'}}; + auto it = bs.begin(); + auto c = it->c; + assert(c == CharType{'x'}); + + auto cit = bs.cbegin(); + auto cc = cit->c; + assert(cc == CharType{'x'}); + } + + { // increment + auto it = literal_constructed.begin(); + assert(*++it == CharType{'e'}); + assert(*it++ == CharType{'e'}); + assert(*it == CharType{'l'}); + + auto cit = literal_constructed.cbegin(); + assert(*++cit == CharType{'e'}); + assert(*cit++ == CharType{'e'}); + assert(*cit == CharType{'l'}); + } + + { // advance + auto it = literal_constructed.begin() + 2; + assert(*it == CharType{'l'}); + it += 2; + assert(*it == CharType{'o'}); + it = 2 + it; + assert(*it == CharType{'f'}); + + auto cit = literal_constructed.cbegin() + 2; + assert(*cit == CharType{'l'}); + cit += 2; + assert(*cit == CharType{'o'}); + cit = 2 + cit; + assert(*cit == CharType{'f'}); + } + + { // decrement + auto it = literal_constructed.end(); + assert(*--it == CharType{'s'}); + assert(*it-- == CharType{'s'}); + assert(*it == CharType{'n'}); + + auto cit = literal_constructed.cend(); + assert(*--cit == CharType{'s'}); + assert(*cit-- == CharType{'s'}); + assert(*cit == CharType{'n'}); + } + + { // advance back + auto it = literal_constructed.end() - 2; + assert(*it == CharType{'n'}); + it -= 2; + assert(*it == CharType{'t'}); + + auto cit = literal_constructed.cend() - 2; + assert(*cit == CharType{'n'}); + cit -= 2; + assert(*cit == CharType{'t'}); + } + + { // difference + const auto it1 = literal_constructed.begin(); + const auto it2 = literal_constructed.end(); + assert(it2 - it1 == ssize(get_view_input())); + + const auto cit1 = literal_constructed.cbegin(); + const auto cit2 = literal_constructed.cend(); + assert(cit2 - cit1 == ssize(get_view_input())); + + assert(it2 - cit1 == ssize(get_view_input())); + assert(cit2 - it1 == ssize(get_view_input())); + } + + { // comparison + const auto it1 = literal_constructed.begin(); + const auto it2 = literal_constructed.begin(); + const auto it3 = literal_constructed.end(); + + assert(it1 == it2); + assert(it1 != it3); + assert(it1 < it3); + assert(it1 <= it3); + assert(it3 > it1); + assert(it3 >= it1); + + assert((it1 <=> it2) == strong_ordering::equal); + assert((it1 <=> it3) == strong_ordering::less); + assert((it3 <=> it1) == strong_ordering::greater); + } + + { // access + const auto it = literal_constructed.begin() + 2; + it[2] = CharType{'l'}; + assert(literal_constructed[4] == CharType{'l'}); + + const auto cit = literal_constructed.cbegin() + 2; + assert(cit[2] == CharType{'l'}); + } +#endif // __EDG__ + return true; +} + +template +_CONSTEXPR20_CONTAINER bool test_growth() { + using str = basic_string; +#ifndef __EDG__ // TRANSITION, VSO-1273296 + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.resize(1008); + + assert(v.size() == 1008); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.resize(8007); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.push_back(CharType{'b'}); + + assert(v.size() == 1008); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + str l(3, CharType{'b'}); + + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 1010); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + str l(7000, CharType{'b'}); + + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.insert(v.end(), 3, CharType{'b'}); + + assert(v.size() == 1010); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.insert(v.end(), 7000, CharType{'b'}); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } +#endif // __EDG__ + return true; +} + +int main() { + test_interface(); +#ifdef __cpp_char8_t + test_interface(); +#endif // __cpp_char8_t + test_interface(); + test_interface(); + test_interface(); + + test_udls(); + + test_iterators(); +#ifdef __cpp_char8_t + test_iterators(); +#endif // __cpp_char8_t + test_iterators(); + test_iterators(); + test_iterators(); + + test_growth(); +#ifdef __cpp_char8_t + test_growth(); +#endif // __cpp_char8_t + test_growth(); + test_growth(); + test_growth(); + +#ifdef __cpp_lib_constexpr_string + static_assert(test_interface()); +#ifdef __cpp_char8_t + static_assert(test_interface()); +#endif // __cpp_char8_t + static_assert(test_interface()); + static_assert(test_interface()); + static_assert(test_interface()); + + static_assert(test_udls()); + + static_assert(test_iterators()); +#ifdef __cpp_char8_t + static_assert(test_iterators()); +#endif // __cpp_char8_t + static_assert(test_iterators()); + static_assert(test_iterators()); + static_assert(test_iterators()); + + static_assert(test_growth()); +#ifdef __cpp_char8_t + static_assert(test_growth()); +#endif // __cpp_char8_t + static_assert(test_growth()); + static_assert(test_growth()); + static_assert(test_growth()); +#endif // __cpp_lib_constexpr_string +} diff --git a/tests/std/tests/P1004R2_constexpr_vector/env.lst b/tests/std/tests/P1004R2_constexpr_vector/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1004R2_constexpr_vector/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/P1004R2_constexpr_vector/test.cpp b/tests/std/tests/P1004R2_constexpr_vector/test.cpp new file mode 100644 index 00000000000..eaee72eec1f --- /dev/null +++ b/tests/std/tests/P1004R2_constexpr_vector/test.cpp @@ -0,0 +1,723 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 +static constexpr int input[] = {0, 1, 2, 3, 4, 5}; +#endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) + +template +struct soccc_allocator { + using value_type = T; + + _CONSTEXPR20_CONTAINER soccc_allocator() noexcept = default; + _CONSTEXPR20_CONTAINER explicit soccc_allocator(const int id_) noexcept : id(id_), soccc_generation(0) {} + _CONSTEXPR20_CONTAINER explicit soccc_allocator(const int id_, const int soccc_generation_) noexcept + : id(id_), soccc_generation(soccc_generation_) {} + template + _CONSTEXPR20_CONTAINER soccc_allocator(const soccc_allocator& other) noexcept + : id(other.id), soccc_generation(other.soccc_generation) {} + _CONSTEXPR20_CONTAINER soccc_allocator(const soccc_allocator& other) noexcept + : id(other.id + 1), soccc_generation(other.soccc_generation) {} + + _CONSTEXPR20_CONTAINER soccc_allocator& operator=(const soccc_allocator&) noexcept { + return *this; + } + + _CONSTEXPR20_CONTAINER soccc_allocator select_on_container_copy_construction() const noexcept { + return soccc_allocator(id, soccc_generation + 1); + } + + template + _CONSTEXPR20_CONTAINER bool operator==(const soccc_allocator&) const noexcept { + return true; + } + + _CONSTEXPR20_CONTAINER T* allocate(const size_t n) { + return allocator{}.allocate(n); + } + + _CONSTEXPR20_CONTAINER void deallocate(T* const p, const size_t n) noexcept { + allocator{}.deallocate(p, n); + } + + template + _CONSTEXPR20_CONTAINER void construct(T* const p, Args&&... args) { + construct_at(p, forward(args)...); + } + + int id = 0; + int soccc_generation = 0; +}; + +using vec = vector>; + +_CONSTEXPR20_CONTAINER bool test_interface() { +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 + { // constructors + + // Non allocator constructors + vec size_default_constructed(5); + assert(size_default_constructed.size() == 5); +#ifndef __EDG__ // TRANSITION, VSO-1273296 + assert(all_of( + size_default_constructed.begin(), size_default_constructed.end(), [](const int val) { return val == 0; })); +#endif // __EDG__ + + vec size_value_constructed(5, 7); + assert(size_value_constructed.size() == 5); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 + assert(all_of( + size_value_constructed.begin(), size_value_constructed.end(), [](const int val) { return val == 7; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + vec range_constructed(begin(input), end(input)); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal(range_constructed.begin(), range_constructed.end(), begin(input), end(input))); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + vec initializer_list_constructed({2, 3, 4, 5}); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal( + initializer_list_constructed.begin(), initializer_list_constructed.end(), begin(input) + 2, end(input))); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + // special member functions + vec default_constructed; + assert(default_constructed.empty()); + vec copy_constructed(size_default_constructed); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal(copy_constructed.begin(), copy_constructed.end(), size_default_constructed.begin(), + size_default_constructed.end())); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + vec move_constructed(move(copy_constructed)); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal(move_constructed.begin(), move_constructed.end(), size_default_constructed.begin(), + size_default_constructed.end())); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + assert(copy_constructed.empty()); // implementation-specific assumption that moved-from is empty + + vec copy_assigned = range_constructed; +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal(copy_assigned.begin(), copy_assigned.end(), range_constructed.begin(), range_constructed.end())); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + vec move_assigned = move(copy_assigned); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal(move_assigned.begin(), move_assigned.end(), range_constructed.begin(), range_constructed.end())); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(copy_assigned.empty()); // implementation-specific assumption that moved-from is empty + + // allocator constructors + soccc_allocator alloc(2, 3); + assert(alloc.id == 2); + assert(alloc.soccc_generation == 3); + + vec al_default_constructed(alloc); + assert(al_default_constructed.empty()); + assert(al_default_constructed.get_allocator().id == 4); + assert(al_default_constructed.get_allocator().soccc_generation == 3); + + vec al_copy_constructed(size_value_constructed, alloc); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 + assert(all_of(al_copy_constructed.begin(), al_copy_constructed.end(), [](const int val) { return val == 7; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_copy_constructed.get_allocator().id == 4); + assert(al_copy_constructed.get_allocator().soccc_generation == 3); + + vec al_move_constructed(move(al_copy_constructed), alloc); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 + assert(all_of(al_move_constructed.begin(), al_move_constructed.end(), [](const int val) { return val == 7; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_copy_constructed.empty()); // implementation-specific assumption that moved-from is empty + assert(al_move_constructed.get_allocator().id == 4); + assert(al_move_constructed.get_allocator().soccc_generation == 3); + + vec al_size_default_constructed(5, alloc); + assert(al_size_default_constructed.size() == 5); +#ifndef __EDG__ // TRANSITION, VSO-1273296 + assert(all_of(al_size_default_constructed.begin(), al_size_default_constructed.end(), + [](const int val) { return val == 0; })); +#endif // __EDG__ + assert(al_size_default_constructed.get_allocator().id == 4); + assert(al_size_default_constructed.get_allocator().soccc_generation == 3); + + vec al_size_value_constructed(5, 7, alloc); + assert(al_size_value_constructed.size() == 5); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 + assert(all_of(al_size_value_constructed.begin(), al_size_value_constructed.end(), + [](const int val) { return val == 7; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_size_value_constructed.get_allocator().id == 4); + assert(al_size_value_constructed.get_allocator().soccc_generation == 3); + + vec al_range_constructed(begin(input), end(input), alloc); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal(al_range_constructed.begin(), al_range_constructed.end(), begin(input), end(input))); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_range_constructed.get_allocator().id == 4); + assert(al_range_constructed.get_allocator().soccc_generation == 3); + + vec al_initializer_list_constructed({2, 3, 4, 5}, alloc); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + assert(equal(al_initializer_list_constructed.begin(), al_initializer_list_constructed.end(), begin(input) + 2, + end(input))); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_initializer_list_constructed.get_allocator().id == 4); + assert(al_initializer_list_constructed.get_allocator().soccc_generation == 3); + } + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + { // assignment + vec range_constructed(begin(input), end(input)); + + vec copy_constructed; + copy_constructed = range_constructed; + assert(equal( + copy_constructed.begin(), copy_constructed.end(), range_constructed.begin(), range_constructed.end())); + + vec move_constructed; + move_constructed = move(copy_constructed); + assert(equal( + move_constructed.begin(), move_constructed.end(), range_constructed.begin(), range_constructed.end())); + assert(copy_constructed.empty()); // implementation-specific assumption that moved-from is empty + + vec initializer_list_constructed; + initializer_list_constructed = {0, 1, 2, 3, 4, 5}; + assert( + equal(initializer_list_constructed.begin(), initializer_list_constructed.end(), begin(input), end(input))); + + vec assigned; + constexpr int expected_assign_value[] = {4, 4, 4, 4, 4}; + assigned.assign(5, 4); + assert(equal(assigned.begin(), assigned.end(), begin(expected_assign_value), end(expected_assign_value))); + + assigned.assign(begin(input), end(input)); + assert(equal(assigned.begin(), assigned.end(), begin(input), end(input))); + + constexpr int expected_assign_initializer[] = {2, 3, 4, 5}; + assigned.assign({2, 3, 4, 5}); + assert(equal( + assigned.begin(), assigned.end(), begin(expected_assign_initializer), end(expected_assign_initializer))); + } +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + { // allocator + vec default_constructed; + const auto alloc = default_constructed.get_allocator(); + static_assert(is_same_v, soccc_allocator>); + assert(alloc.id == 1); + assert(alloc.soccc_generation == 0); + } + + { // iterators + vec range_constructed(begin(input), end(input)); + const vec const_range_constructed(begin(input), end(input)); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 + const auto b = range_constructed.begin(); + static_assert(is_same_v, vec::iterator>); + assert(*b == 0); + + const auto cb = range_constructed.cbegin(); + static_assert(is_same_v, vec::const_iterator>); + assert(*cb == 0); + + const auto cb2 = const_range_constructed.begin(); + static_assert(is_same_v, vec::const_iterator>); + assert(*cb2 == 0); + + const auto e = range_constructed.end(); + static_assert(is_same_v, vec::iterator>); + assert(*prev(e) == 5); + + const auto ce = range_constructed.cend(); + static_assert(is_same_v, vec::const_iterator>); + assert(*prev(ce) == 5); + + const auto ce2 = const_range_constructed.end(); + static_assert(is_same_v, vec::const_iterator>); + assert(*prev(ce2) == 5); + + const auto rb = range_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*rb == 5); + + const auto crb = range_constructed.crbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb == 5); + + const auto crb2 = const_range_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb2 == 5); + + const auto re = range_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(re) == 0); + + const auto cre = range_constructed.crend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre) == 0); + + const auto cre2 = const_range_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre2) == 0); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + } + + { // access + vec range_constructed(begin(input), end(input)); + const vec const_range_constructed(begin(input), end(input)); + + const auto at = range_constructed.at(2); + static_assert(is_same_v, int>); + assert(at == 2); + + range_constructed.at(2) = 3; + + const auto at2 = range_constructed.at(2); + static_assert(is_same_v, int>); + assert(at2 == 3); + + const auto cat = const_range_constructed.at(2); + static_assert(is_same_v, int>); + assert(cat == 2); + + const auto op = range_constructed[3]; + static_assert(is_same_v, int>); + assert(op == 3); + + range_constructed[3] = 4; + const auto op2 = range_constructed[3]; + static_assert(is_same_v, int>); + assert(op2 == 4); + + const auto cop = const_range_constructed[3]; + static_assert(is_same_v, int>); + assert(cop == 3); + + const auto f = range_constructed.front(); + static_assert(is_same_v, int>); + assert(f == 0); + + const auto cf = const_range_constructed.front(); + static_assert(is_same_v, int>); + assert(cf == 0); + + const auto b = range_constructed.back(); + static_assert(is_same_v, int>); + assert(b == 5); + + const auto cb = const_range_constructed.back(); + static_assert(is_same_v, int>); + assert(cb == 5); + + const auto d = range_constructed.data(); + static_assert(is_same_v, int*>); + assert(*d == 0); + + const auto cd = const_range_constructed.data(); + static_assert(is_same_v, const int*>); + assert(*cd == 0); + } + + { // capacity + vec range_constructed(begin(input), end(input)); + + const auto e = range_constructed.empty(); + static_assert(is_same_v, bool>); + assert(!e); + + const auto s = range_constructed.size(); + static_assert(is_same_v, size_t>); + assert(s == size(input)); + + const auto ms = range_constructed.max_size(); + static_assert(is_same_v, size_t>); + assert(ms == static_cast(-1) / sizeof(int)); + + range_constructed.reserve(20); + + const auto c = range_constructed.capacity(); + static_assert(is_same_v, size_t>); + assert(c == 20); + + range_constructed.shrink_to_fit(); + + const auto c2 = range_constructed.capacity(); + static_assert(is_same_v, size_t>); + assert(c2 == 6); + } + + { // modifiers + vec range_constructed(begin(input), end(input)); + + vec cleared = range_constructed; + cleared.clear(); + assert(cleared.empty()); + assert(cleared.capacity() == range_constructed.capacity()); + + vec inserted; + +#ifndef __EDG__ // TRANSITION, VSO-1273386, VSO-1274387 + const int to_be_inserted = 3; + inserted.insert(inserted.begin(), to_be_inserted); + assert(inserted.size() == 1); + assert(inserted.front() == 3); + + const int to_be_inserted2 = 4; + inserted.insert(inserted.cbegin(), to_be_inserted2); + assert(inserted.size() == 2); + assert(inserted.front() == 4); + + inserted.insert(inserted.begin(), 1); + assert(inserted.size() == 3); + assert(inserted.front() == 1); + + inserted.insert(inserted.cbegin(), 2); + assert(inserted.size() == 4); + assert(inserted.front() == 2); +#endif // __EDG__ + +#ifndef __EDG__ // TRANSITION, VSO-1273381, VSO-1273296 + const auto it = inserted.insert(inserted.begin(), begin(input), end(input)); + assert(inserted.size() == 10); + assert(it == inserted.begin()); + + const auto it2 = inserted.insert(inserted.cbegin(), begin(input), end(input)); + assert(inserted.size() == 16); + assert(it2 == inserted.begin()); + + const auto it3 = inserted.insert(inserted.begin(), {2, 3, 4}); + assert(inserted.size() == 19); + assert(it3 == inserted.begin()); +#endif // __EDG__ + +#ifndef __EDG__ // TRANSITION, VSO-1274387, VSO-1273296 + inserted.insert(inserted.cbegin(), {2, 3, 4}); + assert(inserted.size() == 22); + + inserted.insert(inserted.begin(), 4, 11); + assert(inserted.size() == 26); + + vec emplaced; + emplaced.emplace(emplaced.cbegin(), 42); + assert(emplaced.size() == 1); + assert(emplaced.front() == 42); + + emplaced.emplace_back(43); + assert(emplaced.size() == 2); + assert(emplaced.back() == 43); + + emplaced.push_back(44); + assert(emplaced.size() == 3); + assert(emplaced.back() == 44); + + const int to_be_pushed = 45; + emplaced.push_back(to_be_pushed); + assert(emplaced.size() == 4); + assert(emplaced.back() == 45); + + emplaced.pop_back(); + assert(emplaced.size() == 3); + assert(emplaced.back() == 44); + + emplaced.resize(1); + assert(emplaced.size() == 1); + assert(emplaced.front() == 42); + + emplaced.swap(inserted); + assert(inserted.size() == 1); + assert(inserted.front() == 42); + assert(emplaced.size() == 26); + + emplaced.erase(emplaced.end() - 1); + assert(emplaced.size() == 25); + + emplaced.erase(emplaced.begin(), emplaced.begin() + 2); + assert(emplaced.size() == 23); +#endif // __EDG__ + } + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 + { // swap + vec first{2, 3, 4}; + vec second{5, 6, 7, 8}; + swap(first, second); + + constexpr int expected_first[] = {5, 6, 7, 8}; + constexpr int expected_second[] = {2, 3, 4}; + assert(equal(first.begin(), first.end(), begin(expected_first), end(expected_first))); + assert(equal(second.begin(), second.end(), begin(expected_second), end(expected_second))); + } +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 + { // erase + vec erased{1, 2, 3, 4, 2, 3, 2}; + erase(erased, 2); + constexpr int expected_erased[] = {1, 3, 4, 3}; + assert(equal(erased.begin(), erased.end(), begin(expected_erased), end(expected_erased))); + + erase_if(erased, [](const int val) { return val < 4; }); + constexpr int expected_erase_if[] = {4}; + assert(equal(erased.begin(), erased.end(), begin(expected_erase_if), end(expected_erase_if))); + } +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + { // comparison + vec first(begin(input), end(input)); + vec second(begin(input), end(input)); + vec third{2, 3, 4}; + + const auto e = first == second; + static_assert(is_same_v, bool>); + assert(e); + + const auto ne = first != third; + static_assert(is_same_v, bool>); + assert(ne); + + const auto l = first < third; + static_assert(is_same_v, bool>); + assert(l); + + const auto le = first <= third; + static_assert(is_same_v, bool>); + assert(le); + + const auto g = first > third; + static_assert(is_same_v, bool>); + assert(!g); + + const auto ge = first >= third; + static_assert(is_same_v, bool>); + assert(!ge); + } +#endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) + return true; +} + +_CONSTEXPR20_CONTAINER bool test_iterators() { +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 + vec range_constructed(begin(input), end(input)); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 + { // increment + auto it = range_constructed.begin(); + assert(*++it == 1); + assert(*it++ == 1); + assert(*it == 2); + + auto cit = range_constructed.cbegin(); + assert(*++cit == 1); + assert(*cit++ == 1); + assert(*cit == 2); + } + + { // advance + auto it = range_constructed.begin() + 2; + assert(*it == 2); + it += 2; + assert(*it == 4); + + auto cit = range_constructed.cbegin() + 2; + assert(*cit == 2); + cit += 2; + assert(*cit == 4); + } + + { // decrement + auto it = range_constructed.end(); + assert(*--it == 5); + assert(*it-- == 5); + assert(*it == 4); + + auto cit = range_constructed.cend(); + assert(*--cit == 5); + assert(*cit-- == 5); + assert(*cit == 4); + } + + { // advance back + auto it = range_constructed.end() - 2; + assert(*it == 4); + it -= 2; + assert(*it == 2); + + auto cit = range_constructed.cend() - 2; + assert(*cit == 4); + cit -= 2; + assert(*cit == 2); + } + + { // difference + const auto it1 = range_constructed.begin(); + const auto it2 = range_constructed.end(); + assert(it2 - it1 == ssize(input)); + + const auto cit1 = range_constructed.cbegin(); + const auto cit2 = range_constructed.cend(); + assert(cit2 - cit1 == ssize(input)); + + assert(it2 - cit1 == ssize(input)); + assert(cit2 - it1 == ssize(input)); + } + + { // comparison + const auto it1 = range_constructed.begin(); + const auto it2 = range_constructed.begin(); + const auto it3 = range_constructed.end(); + + assert(it1 == it2); + assert(it1 != it3); + assert(it1 < it3); + assert(it1 <= it3); + assert(it3 > it1); + assert(it3 >= it1); + } + + { // access + const auto it = range_constructed.begin() + 2; + it[2] = 3; + assert(range_constructed[4] == 3); + + const auto cit = range_constructed.cbegin() + 2; + assert(cit[2] == 3); + + vector> vec2 = {{1, 2}, {2, 3}}; + const auto it2 = vec2.begin(); + assert(it2->second == 2); + + const auto cit2 = vec2.cbegin(); + assert(cit2->first == 1); + } + +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 +#endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) + return true; +} + +_CONSTEXPR20_CONTAINER bool test_growth() { +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 + { + vector v(1000, 1729); + + assert(v.size() == 1000); + assert(v.capacity() == 1000); + + v.resize(1003); + + assert(v.size() == 1003); + assert(v.capacity() == 1500); + } + + { + vector v(1000, 1729); + + assert(v.size() == 1000); + assert(v.capacity() == 1000); + + v.resize(8000); + + assert(v.size() == 8000); + assert(v.capacity() == 8000); + } + + { + vector v(1000, 1729); + + assert(v.size() == 1000); + assert(v.capacity() == 1000); + + v.push_back(47); + + assert(v.size() == 1001); + assert(v.capacity() == 1500); + } + + { + vector v(1000, 1729); + + assert(v.size() == 1000); + assert(v.capacity() == 1000); + + vector l(3, 47); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 1003); + assert(v.capacity() == 1500); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + } + + { + vector v(1000, 1729); + + assert(v.size() == 1000); + assert(v.capacity() == 1000); + + vector l(7000, 47); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1274387 + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 8000); + assert(v.capacity() == 8000); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + } + + { + vector v(1000, 1729); + + assert(v.size() == 1000); + assert(v.capacity() == 1000); + +#ifndef __EDG__ // TRANSITION, VSO-1273386, VSO-1274387 + v.insert(v.end(), 3, 47); + + assert(v.size() == 1003); + assert(v.capacity() == 1500); +#endif // __EDG__ + } + + { + vector v(1000, 1729); + + assert(v.size() == 1000); + assert(v.capacity() == 1000); + +#ifndef __EDG__ // TRANSITION, VSO-1273386, VSO-1274387 + v.insert(v.end(), 7000, 47); + + assert(v.size() == 8000); + assert(v.capacity() == 8000); +#endif // __EDG__ + } +#endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) + return true; +} + +int main() { + test_interface(); + test_iterators(); + test_growth(); +#ifdef __cpp_lib_constexpr_vector + static_assert(test_interface()); + static_assert(test_iterators()); + static_assert(test_growth()); +#endif // __cpp_lib_constexpr_vector +} diff --git a/tests/std/tests/P1004R2_constexpr_vector_bool/env.lst b/tests/std/tests/P1004R2_constexpr_vector_bool/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1004R2_constexpr_vector_bool/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/P1004R2_constexpr_vector_bool/test.cpp b/tests/std/tests/P1004R2_constexpr_vector_bool/test.cpp new file mode 100644 index 00000000000..1124ad5e15a --- /dev/null +++ b/tests/std/tests/P1004R2_constexpr_vector_bool/test.cpp @@ -0,0 +1,643 @@ +// 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 defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1270433 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-const-variable" // TRANSITION, LLVM-48606 +#endif // __clang__ +static constexpr bool input[] = {true, false, true, true, false, true}; +static constexpr bool input_flipped[] = {false, true, false, false, true, false}; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + +template +struct soccc_allocator { + using value_type = T; + + _CONSTEXPR20_DYNALLOC soccc_allocator() noexcept = default; + _CONSTEXPR20_DYNALLOC explicit soccc_allocator(const int id_) noexcept : id(id_), soccc_generation(0) {} + _CONSTEXPR20_DYNALLOC explicit soccc_allocator(const int id_, const int soccc_generation_) noexcept + : id(id_), soccc_generation(soccc_generation_) {} + template + _CONSTEXPR20_DYNALLOC soccc_allocator(const soccc_allocator& other) noexcept + : id(other.id), soccc_generation(other.soccc_generation) {} + _CONSTEXPR20_DYNALLOC soccc_allocator(const soccc_allocator& other) noexcept + : id(other.id + 1), soccc_generation(other.soccc_generation) {} + + _CONSTEXPR20_DYNALLOC soccc_allocator& operator=(const soccc_allocator&) noexcept { + return *this; + } + + _CONSTEXPR20_DYNALLOC soccc_allocator select_on_container_copy_construction() const noexcept { + return soccc_allocator(id, soccc_generation + 1); + } + + template + _CONSTEXPR20_DYNALLOC bool operator==(const soccc_allocator&) const noexcept { + return true; + } + + _CONSTEXPR20_DYNALLOC T* allocate(const size_t n) { + return allocator{}.allocate(n); + } + + _CONSTEXPR20_DYNALLOC void deallocate(T* const p, const size_t n) noexcept { + allocator{}.deallocate(p, n); + } + + template + _CONSTEXPR20_DYNALLOC void construct(T* const p, Args&&... args) { + construct_at(p, forward(args)...); + } + + int id = 0; + int soccc_generation = 0; +}; + +using vec = vector>; + +_CONSTEXPR20_CONTAINER bool test_interface() { +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 + { // constructors + +// Non allocator constructors +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec size_default_constructed(5); + assert(size_default_constructed.size() == 5); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert(all_of( + size_default_constructed.begin(), size_default_constructed.end(), [](const bool val) { return !val; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 +#endif // __EDG__ + +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec size_value_constructed(5, true); + assert(size_value_constructed.size() == 5); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert( + all_of(size_value_constructed.begin(), size_value_constructed.end(), [](const bool val) { return val; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 +#endif // __EDG__ + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec range_constructed(begin(input), end(input)); + assert(equal(range_constructed.begin(), range_constructed.end(), begin(input), end(input))); + + vec initializer_list_constructed({true, true, false, true}); + assert(equal( + initializer_list_constructed.begin(), initializer_list_constructed.end(), begin(input) + 2, end(input))); +#endif // __EDG__ +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + // special member functions +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec default_constructed; + assert(default_constructed.empty()); + vec copy_constructed(size_default_constructed); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert(equal(copy_constructed.begin(), copy_constructed.end(), size_default_constructed.begin(), + size_default_constructed.end())); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + vec move_constructed(move(copy_constructed)); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert(equal(move_constructed.begin(), move_constructed.end(), size_default_constructed.begin(), + size_default_constructed.end())); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(copy_constructed.empty()); // implementation-specific assumption that moved-from is empty +#endif // __EDG__ + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 +#ifndef __EDG__ // TRANSITION, VSO-1274387, VSO-1273296 + vec copy_assigned = range_constructed; + assert(equal(copy_assigned.begin(), copy_assigned.end(), range_constructed.begin(), range_constructed.end())); + + vec move_assigned = move(copy_assigned); + assert(equal(move_assigned.begin(), move_assigned.end(), range_constructed.begin(), range_constructed.end())); + assert(copy_assigned.empty()); // implementation-specific assumption that moved-from is empty +#endif // __EDG__ +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + // allocator constructors + soccc_allocator alloc(2, 3); + assert(alloc.id == 2); + assert(alloc.soccc_generation == 3); + +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec al_default_constructed(alloc); + assert(al_default_constructed.empty()); + assert(al_default_constructed.get_allocator().id == 4); + assert(al_default_constructed.get_allocator().soccc_generation == 3); + + vec al_copy_constructed(size_value_constructed, alloc); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert(all_of(al_copy_constructed.begin(), al_copy_constructed.end(), [](const bool val) { return val; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_copy_constructed.get_allocator().id == 4); + assert(al_copy_constructed.get_allocator().soccc_generation == 3); + + vec al_move_constructed(move(al_copy_constructed), alloc); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert(all_of(al_move_constructed.begin(), al_move_constructed.end(), [](const bool val) { return val; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_copy_constructed.empty()); // implementation-specific assumption that moved-from is empty + assert(al_move_constructed.get_allocator().id == 4); + assert(al_move_constructed.get_allocator().soccc_generation == 3); + + vec al_size_default_constructed(5, alloc); + assert(al_size_default_constructed.size() == 5); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert(all_of(al_size_default_constructed.begin(), al_size_default_constructed.end(), + [](const bool val) { return !val; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_size_default_constructed.get_allocator().id == 4); + assert(al_size_default_constructed.get_allocator().soccc_generation == 3); + + vec al_size_value_constructed(5, true, alloc); + assert(al_size_value_constructed.size() == 5); +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273296 + assert(all_of( + al_size_value_constructed.begin(), al_size_value_constructed.end(), [](const bool val) { return val; })); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + assert(al_size_value_constructed.get_allocator().id == 4); + assert(al_size_value_constructed.get_allocator().soccc_generation == 3); +#endif // __EDG__ + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273365 +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec al_range_constructed(begin(input), end(input), alloc); + assert(equal(al_range_constructed.begin(), al_range_constructed.end(), begin(input), end(input))); + assert(al_range_constructed.get_allocator().id == 4); + assert(al_range_constructed.get_allocator().soccc_generation == 3); +#endif // __EDG__ + +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec al_initializer_list_constructed({true, true, false, true}, alloc); + assert(equal(al_initializer_list_constructed.begin(), al_initializer_list_constructed.end(), begin(input) + 2, + end(input))); + assert(al_initializer_list_constructed.get_allocator().id == 4); + assert(al_initializer_list_constructed.get_allocator().soccc_generation == 3); +#endif // __EDG__ +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + } + + { // assignment +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec range_constructed(begin(input), end(input)); + + vec copy_constructed; + copy_constructed = range_constructed; + assert(equal( + copy_constructed.begin(), copy_constructed.end(), range_constructed.begin(), range_constructed.end())); + + vec move_constructed; + move_constructed = move(copy_constructed); + assert(equal( + move_constructed.begin(), move_constructed.end(), range_constructed.begin(), range_constructed.end())); + assert(copy_constructed.empty()); // implementation-specific assumption that moved-from is empty + + vec initializer_list_constructed; + initializer_list_constructed = {true, false, true, true, false, true}; + assert( + equal(initializer_list_constructed.begin(), initializer_list_constructed.end(), begin(input), end(input))); + + vec assigned; + constexpr bool expected_assign_value[] = {true, true, true, true, true}; + assigned.assign(5, true); + assert(equal(assigned.begin(), assigned.end(), begin(expected_assign_value), end(expected_assign_value))); + + assigned.assign(begin(input), end(input)); + assert(equal(assigned.begin(), assigned.end(), begin(input), end(input))); + + constexpr bool expected_assign_initializer[] = {true, false, true, true, false, true}; + assigned.assign({true, false, true, true, false, true}); + assert(equal( + assigned.begin(), assigned.end(), begin(expected_assign_initializer), end(expected_assign_initializer))); +#endif // __EDG__ + } + + { // allocator +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec default_constructed; + const auto alloc = default_constructed.get_allocator(); + static_assert(is_same_v, soccc_allocator>); + assert(alloc.id == 1); + assert(alloc.soccc_generation == 0); +#endif // __EDG__ + } + + { // iterators +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec range_constructed(begin(input), end(input)); + const vec const_range_constructed(begin(input), end(input)); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 + const auto b = range_constructed.begin(); + static_assert(is_same_v, vec::iterator>); + assert(*b); + + const auto cb = range_constructed.cbegin(); + static_assert(is_same_v, vec::const_iterator>); + assert(*cb); + + const auto cb2 = const_range_constructed.begin(); + static_assert(is_same_v, vec::const_iterator>); + assert(*cb2); + + const auto e = range_constructed.end(); + static_assert(is_same_v, vec::iterator>); + assert(*prev(e)); + + const auto ce = range_constructed.cend(); + static_assert(is_same_v, vec::const_iterator>); + assert(*prev(ce)); + + const auto ce2 = const_range_constructed.end(); + static_assert(is_same_v, vec::const_iterator>); + assert(*prev(ce2)); + + const auto rb = range_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*rb); + + const auto crb = range_constructed.crbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb); + + const auto crb2 = const_range_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb2); + + const auto re = range_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(re)); + + const auto cre = range_constructed.crend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre)); + + const auto cre2 = const_range_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre2)); +#endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 +#endif // __EDG__ + } + + { // access +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec range_constructed(begin(input), end(input)); + const vec const_range_constructed(begin(input), end(input)); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 + const auto at = range_constructed.at(2); + static_assert(is_same_v, _Iter_ref_t>); + assert(at); + + range_constructed.at(2) = false; + + const auto at2 = range_constructed.at(2); + static_assert(is_same_v, _Iter_ref_t>); + assert(at2 == false); + + const auto cat = const_range_constructed.at(2); + static_assert(is_same_v, _Iter_ref_t>); + assert(cat); + + const auto op = range_constructed[3]; + static_assert(is_same_v, _Iter_ref_t>); + assert(op); + + range_constructed[3] = true; + const auto op2 = range_constructed[3]; + static_assert(is_same_v, _Iter_ref_t>); + assert(op2); + + const auto cop = const_range_constructed[3]; + static_assert(is_same_v, _Iter_ref_t>); + assert(cop); + + const auto f = range_constructed.front(); + static_assert(is_same_v, _Iter_ref_t>); + assert(f); + + const auto cf = const_range_constructed.front(); + static_assert(is_same_v, _Iter_ref_t>); + assert(cf); + + const auto b = range_constructed.back(); + static_assert(is_same_v, _Iter_ref_t>); + assert(b); + + const auto cb = const_range_constructed.back(); + static_assert(is_same_v, _Iter_ref_t>); + assert(cb); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 +#endif // __EDG__ + } + + { // capacity +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec range_constructed(begin(input), end(input)); + + const auto e = range_constructed.empty(); + static_assert(is_same_v, bool>); + assert(e == false); + + const auto s = range_constructed.size(); + static_assert(is_same_v, size_t>); + assert(s == size(input)); + + const auto ms = range_constructed.max_size(); + static_assert(is_same_v, size_t>); + assert(ms == static_cast(numeric_limits::max())); + + range_constructed.reserve(20); + + const auto c = range_constructed.capacity(); + static_assert(is_same_v, size_t>); + assert(c == 32); + + range_constructed.shrink_to_fit(); + + const auto c2 = range_constructed.capacity(); + static_assert(is_same_v, size_t>); + assert(c2 == 32); +#endif // __EDG__ + } + + { // modifiers +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec range_constructed(begin(input), end(input)); + + vec flipped = range_constructed; + flipped.flip(); + assert(flipped.size() == 6); + assert(equal(flipped.begin(), flipped.end(), begin(input_flipped), end(input_flipped))); + // {true, false, true, true, false, true}; + + vec cleared = range_constructed; + cleared.clear(); + assert(cleared.empty()); + assert(cleared.capacity() == range_constructed.capacity()); + + vec inserted; + + const bool to_be_inserted = true; + inserted.insert(inserted.begin(), to_be_inserted); + assert(inserted.size() == 1); + assert(inserted.front()); + + const bool to_be_inserted2 = false; + inserted.insert(inserted.cbegin(), to_be_inserted2); + assert(inserted.size() == 2); + assert(inserted.front() == false); + + inserted.insert(inserted.begin(), true); + assert(inserted.size() == 3); + assert(inserted.front()); + + inserted.insert(inserted.cbegin(), false); + assert(inserted.size() == 4); + assert(inserted.front() == false); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 + const auto it = inserted.insert(inserted.begin(), begin(input), end(input)); + assert(inserted.size() == 10); + assert(it == inserted.begin()); + + const auto it2 = inserted.insert(inserted.cbegin(), begin(input), end(input)); + assert(inserted.size() == 16); + assert(it2 == inserted.begin()); + + const auto it3 = inserted.insert(inserted.begin(), {true, false, true}); + assert(inserted.size() == 19); + assert(it3 == inserted.begin()); +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + + inserted.insert(inserted.cbegin(), {false, true, false}); + assert(inserted.size() == 22); + + inserted.insert(inserted.cbegin(), 3, true); + assert(inserted.size() == 25); + + vec emplaced; + emplaced.emplace(emplaced.cbegin(), false); + assert(emplaced.size() == 1); + assert(emplaced.front() == false); + + emplaced.emplace_back(true); + assert(emplaced.size() == 2); + assert(emplaced.back()); + + emplaced.push_back(false); + assert(emplaced.size() == 3); + assert(emplaced.back() == false); + + const bool to_be_pushed = true; + emplaced.push_back(to_be_pushed); + assert(emplaced.size() == 4); + assert(emplaced.back()); + + emplaced.pop_back(); + assert(emplaced.size() == 3); + assert(emplaced.back() == false); + + emplaced.resize(1); + assert(emplaced.size() == 1); + assert(emplaced.front() == false); + + emplaced.swap(inserted); + assert(inserted.size() == 1); + assert(inserted.front() == false); + assert(emplaced.size() == 25); + + emplaced.erase(emplaced.end() - 1); + assert(emplaced.size() == 24); + + emplaced.erase(emplaced.begin(), emplaced.begin() + 2); + assert(emplaced.size() == 22); +#endif // __EDG__ + } + + { // swap +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec first{true, false, true}; + vec second{false, false, true, false}; + swap(first, second); + + constexpr bool expected_first[] = {false, false, true, false}; + constexpr bool expected_second[] = {true, false, true}; + assert(equal(first.begin(), first.end(), begin(expected_first), end(expected_first))); + assert(equal(second.begin(), second.end(), begin(expected_second), end(expected_second))); +#endif // __EDG__ + } + + { // erase +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec erased{false, false, true, false, true}; + erase(erased, false); + constexpr bool expected_erased[] = {true, true}; + assert(equal(erased.begin(), erased.end(), begin(expected_erased), end(expected_erased))); + + vec erased_if{false, false, true, false, true}; + erase_if(erased_if, [](const bool val) { return val; }); + constexpr bool expected_erase_if[] = {false, false, false}; + assert(equal(erased_if.begin(), erased_if.end(), begin(expected_erase_if), end(expected_erase_if))); +#endif // __EDG__ + } + + { // comparison +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec first(begin(input), end(input)); + vec second(begin(input), end(input)); + vec third{true, false, true}; + + const auto e = first == second; + static_assert(is_same_v, bool>); + assert(e); + + const auto ne = first != third; + static_assert(is_same_v, bool>); + assert(ne); + + const auto l = first < third; + static_assert(is_same_v, bool>); + assert(!l); + + const auto le = first <= third; + static_assert(is_same_v, bool>); + assert(!le); + + const auto g = first > third; + static_assert(is_same_v, bool>); + assert(g); + + const auto ge = first >= third; + static_assert(is_same_v, bool>); + assert(ge); +#endif // __EDG__ + } +#endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) + + return true; +} + +_CONSTEXPR20_CONTAINER bool test_iterators() { +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 +#ifndef __EDG__ // TRANSITION, VSO-1274387 + vec range_constructed(begin(input), end(input)); + +#if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 + { // increment + auto it = range_constructed.begin(); + assert(*++it == false); + assert(*it++ == false); + assert(*it); + + auto cit = range_constructed.cbegin(); + assert(*++cit == false); + assert(*cit++ == false); + assert(*cit); + } + + { // advance + auto it = range_constructed.begin() + 2; + assert(*it); + it += 2; + assert(*it == false); + + auto cit = range_constructed.cbegin() + 2; + assert(*cit); + cit += 2; + assert(*cit == false); + } + + { // decrement + auto it = range_constructed.end(); + assert(*--it); + assert(*it--); + assert(*it == false); + + auto cit = range_constructed.cend(); + assert(*--cit); + assert(*cit--); + assert(*cit == false); + } + + { // advance back + auto it = range_constructed.end() - 2; + assert(*it == false); + it -= 2; + assert(*it); + + auto cit = range_constructed.cend() - 2; + assert(*cit == false); + cit -= 2; + assert(*cit); + } + + { // difference + const auto it1 = range_constructed.begin(); + const auto it2 = range_constructed.end(); + assert(it2 - it1 == ssize(input)); + + const auto cit1 = range_constructed.cbegin(); + const auto cit2 = range_constructed.cend(); + assert(cit2 - cit1 == ssize(input)); + + assert(it2 - cit1 == ssize(input)); + assert(cit2 - it1 == ssize(input)); + } + + { // comparison + const auto it1 = range_constructed.begin(); + const auto it2 = range_constructed.begin(); + const auto it3 = range_constructed.end(); + + assert(it1 == it2); + assert(it1 != it3); + assert(it1 < it3); + assert(it1 <= it3); + assert(it3 > it1); + assert(it3 >= it1); + } + + { // access + const auto it = range_constructed.begin() + 2; + it[2] = false; + assert(range_constructed[4] == false); + + const auto cit = range_constructed.cbegin() + 2; + assert(cit[2] == false); + } +#endif // __EDG__ +#endif // !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 +#endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) + + return true; +} + +int main() { + test_interface(); + test_iterators(); +#ifdef __cpp_lib_constexpr_vector + static_assert(test_interface()); + static_assert(test_iterators()); +#endif // __cpp_lib_constexpr_vector +} diff --git a/tests/std/tests/P1208R6_source_location/env.lst b/tests/std/tests/P1208R6_source_location/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1208R6_source_location/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/P1208R6_source_location/header.h b/tests/std/tests/P1208R6_source_location/header.h new file mode 100644 index 00000000000..a16d321ee66 --- /dev/null +++ b/tests/std/tests/P1208R6_source_location/header.h @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#include +#include +#include + +constexpr void header_test() { + using namespace std; + const auto x = source_location::current(); + assert(x.line() == __LINE__ - 1); + assert(x.column() == 37); + assert(x.function_name() == "header_test"sv); + assert(string_view{x.file_name()}.ends_with("header.h"sv)); +} diff --git a/tests/std/tests/P1208R6_source_location/test.cpp b/tests/std/tests/P1208R6_source_location/test.cpp new file mode 100644 index 00000000000..547c0b7130a --- /dev/null +++ b/tests/std/tests/P1208R6_source_location/test.cpp @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#if defined(__cpp_consteval) && !defined(__EDG__) // TRANSITION, VSO-1285779 +#include "header.h" +#include +#include +#include +#include +using namespace std; + +static_assert(is_nothrow_default_constructible_v); +static_assert(is_nothrow_move_constructible_v); +static_assert(is_nothrow_move_assignable_v); +static_assert(is_nothrow_swappable_v); + +constexpr auto test_cpp = "test.cpp"sv; + +constexpr auto g = source_location::current(); +static_assert(g.line() == __LINE__ - 1); +static_assert(g.column() == 37); +static_assert(g.function_name() == ""sv); +static_assert(string_view{g.file_name()}.ends_with(test_cpp)); + +constexpr int s_int_line = __LINE__ + 3; +struct s { + constexpr s(const source_location x = source_location::current()) : loc(x) {} + constexpr s(int) {} + source_location loc = source_location::current(); +}; + +constexpr int s2_int_line = __LINE__ + 3; +struct s2 { + constexpr s2(const source_location l = source_location::current()) : x{l} {} + constexpr s2(int) {} + s x = source_location::current(); +}; + +constexpr void copy_test() { + const auto rhs = source_location::current(); + const auto lhs = rhs; + assert(lhs.line() == rhs.line()); + assert(lhs.column() == rhs.column()); + assert(string_view{lhs.function_name()} == string_view{rhs.function_name()}); + assert(string_view{lhs.file_name()} == string_view{rhs.file_name()}); +} + +constexpr void local_test() { + const auto x = source_location::current(); + assert(x.line() == __LINE__ - 1); + assert(x.column() == 37); + assert(x.function_name() == "local_test"sv); + assert(string_view{x.file_name()}.ends_with(test_cpp)); +} + +constexpr void argument_test( + const unsigned int line, const unsigned int column, const source_location x = source_location::current()) { + assert(x.line() == line); + assert(x.column() == column); + assert(x.function_name() == "test"sv); + assert(string_view{x.file_name()}.ends_with(test_cpp)); +} + +constexpr void sloc_constructor_test() { + const s x; + assert(x.loc.line() == __LINE__ - 1); +#ifdef _PREFAST_ + assert(x.loc.column() == 14); +#else // _PREFAST_ + assert(x.loc.column() == 13); +#endif // _PREFAST_ + if (is_constant_evaluated()) { + assert(x.loc.function_name() == "main"sv); // TRANSITION, VSO-1285783 + } else { + assert(x.loc.function_name() == "sloc_constructor_test"sv); + } + assert(string_view{x.loc.file_name()}.ends_with(test_cpp)); +} + +constexpr void different_constructor_test() { + const s x{1}; + assert(x.loc.line() == s_int_line); + assert(x.loc.column() == 5); + assert(x.loc.function_name() == "s"sv); + assert(string_view{x.loc.file_name()}.ends_with(test_cpp)); +} + +constexpr void sub_member_test() { + const s2 s; + assert(s.x.loc.line() == __LINE__ - 1); +#ifdef _PREFAST_ + assert(s.x.loc.column() == 15); +#else // _PREFAST_ + assert(s.x.loc.column() == 14); +#endif // _PREFAST_ + if (is_constant_evaluated()) { + assert(s.x.loc.function_name() == "main"sv); // TRANSITION, VSO-1285783 + } else { + assert(s.x.loc.function_name() == "sub_member_test"sv); + } + assert(string_view{s.x.loc.file_name()}.ends_with(test_cpp)); + + const s2 s_i{1}; + assert(s_i.x.loc.line() == s2_int_line); + assert(s_i.x.loc.column() == 5); + assert(s_i.x.loc.function_name() == "s2"sv); + assert(string_view{s_i.x.loc.file_name()}.ends_with(test_cpp)); +} + +constexpr void lambda_test() { + const auto l = [loc = source_location::current()] { return loc; }; + const auto x = l(); + assert(x.line() == __LINE__ - 2); + assert(x.column() == 51); + assert(x.function_name() == "lambda_test"sv); + assert(string_view{x.file_name()}.ends_with(test_cpp)); +} + +template +constexpr source_location function_template() { + return source_location::current(); +} + +constexpr void function_template_test() { + const auto x1 = function_template(); + assert(x1.line() == __LINE__ - 5); + assert(x1.column() == 29); + assert(x1.function_name() == "function_template"sv); + assert(string_view{x1.file_name()}.ends_with(test_cpp)); + + const auto x2 = function_template(); + assert(x1.line() == x2.line()); + assert(x1.column() == x2.column()); + assert(string_view{x1.function_name()} == string_view{x2.function_name()}); + assert(string_view{x1.file_name()} == string_view{x2.file_name()}); +} + +constexpr bool test() { + copy_test(); + local_test(); + argument_test(__LINE__, 5); + const auto loc = source_location::current(); + argument_test(__LINE__ - 1, 39, loc); + sloc_constructor_test(); + different_constructor_test(); + sub_member_test(); + lambda_test(); + function_template_test(); + header_test(); + return true; +} + +int main() { + test(); + static_assert(test()); + return 0; +} +#else // ^^^ defined(__cpp_consteval) && !defined(__EDG__) / !defined(__cpp_consteval) || defined(__EDG__) vvv +int main() {} +#endif // ^^^ !defined(__cpp_consteval) || defined(__EDG__) ^^^ diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py index 8fe322f324d..d86678b8fd0 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py +++ b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py @@ -63,7 +63,7 @@ def getBuildSteps(self, test, litConfig, shared): 'semaphore', 'set', 'shared_mutex', - # 'source_location', + 'source_location', 'span', 'sstream', 'stack', @@ -73,7 +73,7 @@ def getBuildSteps(self, test, litConfig, shared): 'string_view', 'string', 'strstream', - # 'syncstream', + 'syncstream', 'system_error', 'thread', 'tuple', diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl index b0b03595c17..37dff666bd4 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl +++ b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl @@ -62,7 +62,7 @@ () "semaphore", "set", "shared_mutex", - # "source_location", + "source_location", "span", "sstream", "stack", @@ -72,7 +72,7 @@ () "string_view", "string", "strstream", - # "syncstream", + "syncstream", "system_error", "thread", "tuple", @@ -99,8 +99,7 @@ () $header_unit_options .= " $_.obj"; } - # TRANSITION, remove /DMSVC_INTERNAL_TESTING after all compiler bugs are fixed - Run::ExecuteCL("/DMSVC_INTERNAL_TESTING $export_header_options"); - Run::ExecuteCL("/DMSVC_INTERNAL_TESTING test.cpp /Fe$cwd.exe $header_unit_options"); + Run::ExecuteCL("$export_header_options"); + Run::ExecuteCL("test.cpp /Fe$cwd.exe $header_unit_options"); } 1 diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index d660f96e77b..2102a025ea9 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -63,7 +63,7 @@ import ; import ; import ; import ; -// import ; +import ; import ; import ; import ; @@ -73,7 +73,7 @@ import ; import ; import ; import ; -// import ; +import ; import ; import ; import ; @@ -94,6 +94,17 @@ import ; #include using namespace std; +constexpr bool test_source_location() { +#ifdef __cpp_lib_source_location + const auto sl = source_location::current(); + assert(sl.line() == __LINE__ - 1); + assert(sl.column() == 1); + assert(sl.function_name() == "test_source_location"sv); + assert(string_view{sl.file_name()}.ends_with("test.cpp"sv)); +#endif // __cpp_lib_source_location + return true; +} + int main() { { puts("Testing ."); @@ -115,7 +126,7 @@ int main() { { puts("Testing ."); -#if 0 // TRANSITION, VSO-1088552 (deduction guides) +#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1088552 (deduction guides) constexpr array arr{10, 20, 30, 40, 50}; #else // ^^^ no workaround / workaround vvv constexpr array arr{10, 20, 30, 40, 50}; @@ -570,7 +581,7 @@ int main() { { puts("Testing ."); constexpr int arr[]{11, 0, 22, 0, 33, 0, 44, 0, 55}; -#if 0 // TRANSITION, VSO-1088552 (deduction guides) +#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1088552 (deduction guides) assert(ranges::distance(views::filter(arr, [](int x) { return x == 0; })) == 4); static_assert(ranges::distance(views::filter(arr, [](int x) { return x != 0; })) == 5); #else // ^^^ no workaround / workaround vvv @@ -685,7 +696,8 @@ int main() { { puts("Testing ."); - puts("(TRANSITION, not yet implemented.)"); + assert(test_source_location()); + static_assert(test_source_location()); } { @@ -804,7 +816,13 @@ int main() { { puts("Testing ."); - puts("(TRANSITION, not yet implemented.)"); + syncbuf sync_buf{nullptr}; + assert(sync_buf.get_wrapped() == nullptr); + assert(sync_buf.get_allocator() == allocator{}); + assert(sync_buf.emit() == false); + osyncstream sync_str{cout}; + sync_str << "Testing P1502R1_standard_library_header_units.\n"; + assert(sync_str.rdbuf()->get_wrapped() == cout.rdbuf()); } { diff --git a/tests/std/tests/P1614R2_spaceship/env.lst b/tests/std/tests/P1614R2_spaceship/env.lst new file mode 100644 index 00000000000..20ea5fe3426 --- /dev/null +++ b/tests/std/tests/P1614R2_spaceship/env.lst @@ -0,0 +1,7 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst +RUNALL_CROSSLIST +PM_CL="/D_STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS=0" +PM_CL="/D_STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS=1" diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp new file mode 100644 index 00000000000..2b2e73691e5 --- /dev/null +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -0,0 +1,1144 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +concept HasSpaceshipWith = requires { + std::declval() <=> std::declval(); +}; + +using PartiallyOrdered = double; + +struct WeaklyOrdered { + [[nodiscard]] constexpr bool operator==(const WeaklyOrdered&) const { + return true; + } + + [[nodiscard]] constexpr std::weak_ordering operator<=>(const WeaklyOrdered&) const { + return std::weak_ordering::equivalent; + } +}; + +using StronglyOrdered = int; + +// Activates synth-three-way in N4861 16.4.2.1 [expos.only.func]/2. +struct SynthOrdered { + int val; + + constexpr SynthOrdered(const int x) : val{x} {} + + [[nodiscard]] constexpr bool operator==(const SynthOrdered& other) const { + return val == other.val; + } + + [[nodiscard]] constexpr bool operator<(const SynthOrdered& other) const { + return val < other.val; + } +}; + +struct OrderedChar { + OrderedChar() = default; + OrderedChar(const char other) : c(other) {} + + OrderedChar& operator=(const char& other) { + c = other; + return *this; + } + + auto operator<=>(const OrderedChar&) const = default; + + operator char() const { + return c; + } + + char c; +}; + +struct WeaklyOrderedChar : OrderedChar {}; +struct WeaklyOrderedByOmissionChar : OrderedChar {}; +struct PartiallyOrderedChar : OrderedChar {}; + +namespace std { + template <> + struct char_traits : char_traits { + using char_type = OrderedChar; + + static int compare(const char_type* first1, const char_type* first2, size_t count) { + for (; 0 < count; --count, ++first1, ++first2) { + if (*first1 != *first2) { + return *first1 < *first2 ? -1 : +1; + } + } + + return 0; + } + + static bool eq(const char_type l, const char_type r) { + return l.c == r.c; + } + }; + + template <> + struct char_traits : char_traits { + using char_type = WeaklyOrderedChar; + using comparison_category = weak_ordering; + }; + + template <> + struct char_traits : char_traits { + using char_type = WeaklyOrderedByOmissionChar; + + private: + using comparison_category = strong_ordering; + }; + + template <> + struct char_traits : char_traits { + using char_type = PartiallyOrderedChar; + using comparison_category = partial_ordering; + }; +} // namespace std + +struct dummy_diagnostic : std::error_category { + const char* name() const noexcept override { + return "dummy"; + } + std::string message(int) const override { + return ""; + } +}; + +template +constexpr bool spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { + assert(smaller == smaller_equal); + assert(smaller_equal == smaller); + assert(smaller != larger); + assert(larger != smaller); + assert(smaller < larger); + assert(!(larger < smaller)); + assert(larger > smaller); + assert(!(smaller > larger)); + assert(smaller <= larger); + assert(!(larger <= smaller)); + assert(larger >= smaller); + assert(!(smaller >= larger)); + assert((smaller <=> larger) < 0); + assert((larger <=> smaller) > 0); + assert((smaller <=> smaller_equal) == 0); + + static_assert(std::is_same_v larger), ReturnType>); + + return true; +} + +template +inline constexpr bool has_synth_ordered = false; +template +inline constexpr bool has_synth_ordered> = true; +template <> +inline constexpr bool has_synth_ordered = true; + +template +void ordered_containers_test(const Container& smaller, const Container& smaller_equal, const Container& larger) { + using Elem = typename Container::value_type; + + if constexpr (has_synth_ordered) { + spaceship_test(smaller, smaller_equal, larger); + } else { + spaceship_test(smaller, smaller_equal, larger); + } +} + +template +void unordered_containers_test( + const Container& something, const Container& something_equal, const Container& different) { + assert(something == something_equal); + assert(something != different); +} + +template +void ordered_iterator_test(const Iter& smaller, const Iter& smaller_equal, const Iter& larger, + const ConstIter& const_smaller, const ConstIter& const_smaller_equal, const ConstIter& const_larger) { + spaceship_test(smaller, smaller_equal, larger); + spaceship_test(const_smaller, const_smaller_equal, const_larger); + spaceship_test(const_smaller, smaller_equal, larger); +} + +template +void unordered_iterator_test(const Iter& something, const Iter& something_equal, const Iter& different, + const ConstIter& const_something, const ConstIter& const_something_equal, const ConstIter& const_different) { + assert(something == something_equal); + assert(something != different); + + assert(const_something == const_something_equal); + assert(const_something != const_different); + + assert(something == const_something_equal); + assert(something != const_different); +} + +template +void diagnostics_test() { + dummy_diagnostic c_mem[2]; + { + ErrorType e_smaller(0, c_mem[0]); + ErrorType e_equal(0, c_mem[0]); + ErrorType e_larger(1, c_mem[1]); + + spaceship_test(e_smaller, e_equal, e_larger); + } + { + ErrorType e_smaller(0, c_mem[0]); + ErrorType e_larger(0, c_mem[1]); + + assert(e_smaller < e_larger); + assert(!(e_larger < e_smaller)); + assert((e_smaller <=> e_larger) < 0); + assert((e_larger <=> e_smaller) > 0); + } + { + ErrorType e_smaller(0, c_mem[0]); + ErrorType e_larger(1, c_mem[0]); + + assert(e_smaller < e_larger); + assert(!(e_larger < e_smaller)); + assert((e_smaller <=> e_larger) < 0); + assert((e_larger <=> e_smaller) > 0); + } +} + +template