diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c035796ec0e..3df57af6952 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -833,11 +833,6 @@ _NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _Fw #ifdef __cpp_lib_concepts namespace ranges { - template - concept _Bidi_common = is_same_v<_It, _Se> && bidirectional_iterator<_It>; - template - concept _Bidi_common_range = common_range<_Rng> && bidirectional_iterator>; - class _Is_permutation_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; diff --git a/stl/inc/exception b/stl/inc/exception index 63dad042a71..be67bf26ab5 100644 --- a/stl/inc/exception +++ b/stl/inc/exception @@ -389,6 +389,27 @@ template void rethrow_if_nested(const _Ty&) = delete; // requires /GR option #endif // _CPPRTTI +class bad_variant_access + : public exception { // exception for visit of a valueless variant or get on a variant with index() != I +public: + bad_variant_access() noexcept = default; + + _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { + return "bad variant access"; + } + +#if !_HAS_EXCEPTIONS +protected: + void _Doraise() const override { // perform class-specific exception handling + _RAISE(*this); + } +#endif // !_HAS_EXCEPTIONS +}; + +[[noreturn]] inline void _Throw_bad_variant_access() { + _THROW(bad_variant_access{}); +} + _STD_END #pragma pop_macro("new") diff --git a/stl/inc/iterator b/stl/inc/iterator index 703e07eee26..7b1fbd1608f 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -535,43 +535,44 @@ private: }; #ifdef __cpp_lib_concepts -enum class _Variantish_state : unsigned char { _Nothing, _Holds_iter, _Holds_sentinel }; +enum class _Variantish_state : unsigned char { _Nothing, _Holds_first, _Holds_second }; -struct _Common_iterator_construct_tag { - explicit _Common_iterator_construct_tag() = default; +struct _Variantish_empty_tag { + explicit _Variantish_empty_tag() = default; }; -template _Se> +template class _Variantish { public: - constexpr explicit _Variantish(_Common_iterator_construct_tag) noexcept : _Contains{_Variantish_state::_Nothing} {} + constexpr explicit _Variantish(_Variantish_empty_tag) noexcept : _Contains{_Variantish_state::_Nothing} {} - constexpr _Variantish() noexcept(is_nothrow_default_constructible_v<_It>) requires default_initializable<_It> - : _Iterator{}, _Contains{_Variantish_state::_Holds_iter} {} + constexpr _Variantish() noexcept(is_nothrow_default_constructible_v<_Ty1>) requires default_initializable<_Ty1> + : _First{}, _Contains{_Variantish_state::_Holds_first} {} template - constexpr explicit _Variantish(in_place_type_t<_It>, _Types&&... _Args) noexcept( - is_nothrow_constructible_v<_It, _Types...>) - : _Iterator(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_iter} {} + constexpr explicit _Variantish(in_place_type_t<_Ty1>, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_Ty1, _Types...>) + : _First(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_first} {} template - constexpr explicit _Variantish(in_place_type_t<_Se>, _Types&&... _Args) noexcept( - is_nothrow_constructible_v<_Se, _Types...>) - : _Sentinel(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_sentinel} {} + requires _Not_same_as<_Ty1, _Ty2> + constexpr explicit _Variantish(in_place_type_t<_Ty2>, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_Ty2, _Types...>) + : _Second(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_second} {} // clang-format off - template - requires _Not_same_as<_Variantish<_OIter, _OSe>, _Variantish> - constexpr _Variantish(const _Variantish<_OIter, _OSe>& _That) noexcept( - is_nothrow_constructible_v<_It, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&>) + template + requires _Not_same_as<_Variantish<_Uty1, _Uty2>, _Variantish> + constexpr _Variantish(const _Variantish<_Uty1, _Uty2>& _That) noexcept( + is_nothrow_constructible_v<_Ty1, const _Uty1&> && is_nothrow_constructible_v<_Ty2, const _Uty2&>) : _Contains{_That._Contains} { // clang-format on switch (_That._Contains) { - case _Variantish_state::_Holds_iter: - _Construct_in_place(_Iterator, _That._Iterator); + case _Variantish_state::_Holds_first: + _Construct_in_place(_First, _That._First); break; - case _Variantish_state::_Holds_sentinel: - _Construct_in_place(_Sentinel, _That._Sentinel); + case _Variantish_state::_Holds_second: + _Construct_in_place(_Second, _That._Second); break; case _Variantish_state::_Nothing: break; @@ -579,19 +580,19 @@ public: } // clang-format off - constexpr _Variantish(const _Variantish&) requires is_trivially_copy_constructible_v<_It> - && is_trivially_copy_constructible_v<_Se> = default; + constexpr _Variantish(const _Variantish&) requires is_trivially_copy_constructible_v<_Ty1> + && is_trivially_copy_constructible_v<_Ty2> = default; // clang-format on constexpr _Variantish(const _Variantish& _That) noexcept( - is_nothrow_copy_constructible_v<_It>&& is_nothrow_copy_constructible_v<_Se>) + is_nothrow_copy_constructible_v<_Ty1>&& is_nothrow_copy_constructible_v<_Ty2>) : _Contains{_That._Contains} { switch (_Contains) { - case _Variantish_state::_Holds_iter: - _Construct_in_place(_Iterator, _That._Iterator); + case _Variantish_state::_Holds_first: + _Construct_in_place(_First, _That._First); break; - case _Variantish_state::_Holds_sentinel: - _Construct_in_place(_Sentinel, _That._Sentinel); + case _Variantish_state::_Holds_second: + _Construct_in_place(_Second, _That._Second); break; case _Variantish_state::_Nothing: break; @@ -599,19 +600,19 @@ public: } // clang-format off - constexpr _Variantish(_Variantish&&) requires is_trivially_move_constructible_v<_It> - && is_trivially_move_constructible_v<_Se> = default; + constexpr _Variantish(_Variantish&&) requires is_trivially_move_constructible_v<_Ty1> + && is_trivially_move_constructible_v<_Ty2> = default; // clang-format on constexpr _Variantish(_Variantish&& _That) noexcept( - is_nothrow_move_constructible_v<_It>&& is_nothrow_move_constructible_v<_Se>) + is_nothrow_move_constructible_v<_Ty1>&& is_nothrow_move_constructible_v<_Ty2>) : _Contains{_That._Contains} { switch (_Contains) { - case _Variantish_state::_Holds_iter: - _Construct_in_place(_Iterator, _STD move(_That._Iterator)); + case _Variantish_state::_Holds_first: + _Construct_in_place(_First, _STD move(_That._First)); break; - case _Variantish_state::_Holds_sentinel: - _Construct_in_place(_Sentinel, _STD move(_That._Sentinel)); + case _Variantish_state::_Holds_second: + _Construct_in_place(_Second, _STD move(_That._Second)); break; case _Variantish_state::_Nothing: break; @@ -624,26 +625,26 @@ public: // TRANSITION, LLVM-46269, destructor order is significant // clang-format off - constexpr ~_Variantish() requires is_trivially_destructible_v<_It> && is_trivially_destructible_v<_Se> = default; - - constexpr _Variantish& operator=(const _Variantish&) requires is_trivially_destructible_v<_It> - && is_trivially_destructible_v<_Se> - && is_trivially_copy_constructible_v<_It> - && is_trivially_copy_constructible_v<_Se> - && is_trivially_copy_assignable_v<_It> - && is_trivially_copy_assignable_v<_Se> = default; + constexpr ~_Variantish() requires is_trivially_destructible_v<_Ty1> && is_trivially_destructible_v<_Ty2> = default; + + constexpr _Variantish& operator=(const _Variantish&) requires is_trivially_destructible_v<_Ty1> + && is_trivially_destructible_v<_Ty2> + && is_trivially_copy_constructible_v<_Ty1> + && is_trivially_copy_constructible_v<_Ty2> + && is_trivially_copy_assignable_v<_Ty1> + && is_trivially_copy_assignable_v<_Ty2> = default; // clang-format on constexpr _Variantish& operator=(const _Variantish& _That) noexcept( - is_nothrow_copy_constructible_v<_It>&& is_nothrow_copy_constructible_v<_Se>&& - is_nothrow_copy_assignable_v<_It>&& is_nothrow_copy_assignable_v<_Se>) { + is_nothrow_copy_constructible_v<_Ty1>&& is_nothrow_copy_constructible_v<_Ty2>&& + is_nothrow_copy_assignable_v<_Ty1>&& is_nothrow_copy_assignable_v<_Ty2>) { if (_Contains == _That._Contains) { switch (_Contains) { - case _Variantish_state::_Holds_iter: - _Iterator = _That._Iterator; + case _Variantish_state::_Holds_first: + _First = _That._First; break; - case _Variantish_state::_Holds_sentinel: - _Sentinel = _That._Sentinel; + case _Variantish_state::_Holds_second: + _Second = _That._Second; break; case _Variantish_state::_Nothing: break; @@ -655,11 +656,11 @@ public: _Clear(); switch (_That._Contains) { - case _Variantish_state::_Holds_iter: - _Construct_in_place(_Iterator, _That._Iterator); + case _Variantish_state::_Holds_first: + _Construct_in_place(_First, _That._First); break; - case _Variantish_state::_Holds_sentinel: - _Construct_in_place(_Sentinel, _That._Sentinel); + case _Variantish_state::_Holds_second: + _Construct_in_place(_Second, _That._Second); break; case _Variantish_state::_Nothing: break; @@ -671,24 +672,24 @@ public: } // clang-format off - constexpr _Variantish& operator=(_Variantish&&) requires is_trivially_destructible_v<_It> - && is_trivially_destructible_v<_Se> - && is_trivially_move_constructible_v<_It> - && is_trivially_move_constructible_v<_Se> - && is_trivially_move_assignable_v<_It> - && is_trivially_move_assignable_v<_Se> = default; + constexpr _Variantish& operator=(_Variantish&&) requires is_trivially_destructible_v<_Ty1> + && is_trivially_destructible_v<_Ty2> + && is_trivially_move_constructible_v<_Ty1> + && is_trivially_move_constructible_v<_Ty2> + && is_trivially_move_assignable_v<_Ty1> + && is_trivially_move_assignable_v<_Ty2> = default; // clang-format on constexpr _Variantish& operator=(_Variantish&& _That) noexcept( - is_nothrow_move_constructible_v<_It>&& is_nothrow_move_constructible_v<_Se>&& - is_nothrow_move_assignable_v<_It>&& is_nothrow_move_assignable_v<_Se>) { + is_nothrow_move_constructible_v<_Ty1>&& is_nothrow_move_constructible_v<_Ty2>&& + is_nothrow_move_assignable_v<_Ty1>&& is_nothrow_move_assignable_v<_Ty2>) { if (_Contains == _That._Contains) { switch (_Contains) { - case _Variantish_state::_Holds_iter: - _Iterator = _STD move(_That._Iterator); + case _Variantish_state::_Holds_first: + _First = _STD move(_That._First); break; - case _Variantish_state::_Holds_sentinel: - _Sentinel = _STD move(_That._Sentinel); + case _Variantish_state::_Holds_second: + _Second = _STD move(_That._Second); break; case _Variantish_state::_Nothing: break; @@ -700,11 +701,11 @@ public: _Clear(); switch (_That._Contains) { - case _Variantish_state::_Holds_iter: - _Construct_in_place(_Iterator, _STD move(_That._Iterator)); + case _Variantish_state::_Holds_first: + _Construct_in_place(_First, _STD move(_That._First)); break; - case _Variantish_state::_Holds_sentinel: - _Construct_in_place(_Sentinel, _STD move(_That._Sentinel)); + case _Variantish_state::_Holds_second: + _Construct_in_place(_Second, _STD move(_That._Second)); break; case _Variantish_state::_Nothing: break; @@ -716,19 +717,19 @@ public: } // clang-format off - template - requires _Not_same_as<_Variantish<_OIter, _OSe>, _Variantish> - constexpr _Variantish& operator=(const _Variantish<_OIter, _OSe>& _That) noexcept( - is_nothrow_constructible_v<_It, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&> - && is_nothrow_assignable_v<_It&, const _OIter&> && is_nothrow_assignable_v<_Se&, const _OSe&>) { + template + requires _Not_same_as<_Variantish<_Uty1, _Uty2>, _Variantish> + constexpr _Variantish& operator=(const _Variantish<_Uty1, _Uty2>& _That) noexcept( + is_nothrow_constructible_v<_Ty1, const _Uty1&> && is_nothrow_constructible_v<_Ty2, const _Uty2&> + && is_nothrow_assignable_v<_Ty1&, const _Uty1&> && is_nothrow_assignable_v<_Ty2&, const _Uty2&>) { // clang-format on if (_Contains == _That._Contains) { switch (_Contains) { - case _Variantish_state::_Holds_iter: - _Iterator = _That._Iterator; + case _Variantish_state::_Holds_first: + _First = _That._First; break; - case _Variantish_state::_Holds_sentinel: - _Sentinel = _That._Sentinel; + case _Variantish_state::_Holds_second: + _Second = _That._Second; break; case _Variantish_state::_Nothing: break; @@ -740,11 +741,11 @@ public: _Clear(); switch (_That._Contains) { - case _Variantish_state::_Holds_iter: - _Construct_in_place(_Iterator, _That._Iterator); + case _Variantish_state::_Holds_first: + _Construct_in_place(_First, _That._First); break; - case _Variantish_state::_Holds_sentinel: - _Construct_in_place(_Sentinel, _That._Sentinel); + case _Variantish_state::_Holds_second: + _Construct_in_place(_Second, _That._Second); break; case _Variantish_state::_Nothing: break; @@ -757,17 +758,17 @@ public: // clang-format off friend constexpr void swap(_Variantish& _Left, _Variantish& _Right) noexcept( - is_nothrow_move_constructible_v<_It> && is_nothrow_move_constructible_v<_Se> - && is_nothrow_swappable_v<_It> && is_nothrow_swappable_v<_Se>) - requires (!_Is_trivially_swappable_v<_It> || !_Is_trivially_swappable_v<_Se>) { + is_nothrow_move_constructible_v<_Ty1> && is_nothrow_move_constructible_v<_Ty2> + && is_nothrow_swappable_v<_Ty1> && is_nothrow_swappable_v<_Ty2>) + requires (!_Is_trivially_swappable_v<_Ty1> || !_Is_trivially_swappable_v<_Ty2>) { // clang-format on if (_Left._Contains == _Right._Contains) { switch (_Left._Contains) { - case _Variantish_state::_Holds_iter: - _RANGES swap(_Left._Iterator, _Right._Iterator); + case _Variantish_state::_Holds_first: + _RANGES swap(_Left._First, _Right._First); break; - case _Variantish_state::_Holds_sentinel: - _RANGES swap(_Left._Sentinel, _Right._Sentinel); + case _Variantish_state::_Holds_second: + _RANGES swap(_Left._Second, _Right._Second); break; case _Variantish_state::_Nothing: break; @@ -783,11 +784,11 @@ public: constexpr void _Raw_clear() noexcept { switch (_Contains) { - case _Variantish_state::_Holds_iter: - _Iterator.~_It(); + case _Variantish_state::_Holds_first: + _First.~_Ty1(); break; - case _Variantish_state::_Holds_sentinel: - _Sentinel.~_Se(); + case _Variantish_state::_Holds_second: + _Second.~_Ty2(); break; case _Variantish_state::_Nothing: break; @@ -799,9 +800,25 @@ public: _Contains = _Variantish_state::_Nothing; } + template + constexpr void _Emplace_first(_Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty1, _Types...>) { + _Clear(); + + _Construct_in_place(_First, _STD forward<_Types>(_Args)...); + _Contains = _Variantish_state::_Holds_first; + } + + template + constexpr void _Emplace_second(_Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty2, _Types...>) { + _Clear(); + + _Construct_in_place(_Second, _STD forward<_Types>(_Args)...); + _Contains = _Variantish_state::_Holds_second; + } + union { - _It _Iterator; - _Se _Sentinel; + _Ty1 _First; + _Ty2 _Second; }; _Variantish_state _Contains; @@ -838,7 +855,7 @@ public: constexpr common_iterator(_Se _Right) noexcept(is_nothrow_move_constructible_v<_Se>) // strengthened : _Val{in_place_type<_Se>, _STD move(_Right)} {} - constexpr explicit common_iterator(_Common_iterator_construct_tag _Tag) noexcept : _Val{_Tag} {} + constexpr explicit common_iterator(_Variantish_empty_tag _Tag) noexcept : _Val{_Tag} {} // clang-format off template @@ -861,18 +878,18 @@ public: _NODISCARD constexpr decltype(auto) operator*() { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_first, "common_iterator can only be dereferenced if it holds an iterator"); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return *_Val._Iterator; + return *_Val._First; } _NODISCARD constexpr decltype(auto) operator*() const requires _Dereferenceable { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_first, "common_iterator can only be dereferenced if it holds an iterator"); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return *_Val._Iterator; + return *_Val._First; } // clang-format off @@ -882,13 +899,13 @@ public: || constructible_from, iter_reference_t<_Iter>>) { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_first, "common_iterator can only be dereferenced if it holds an iterator"); #endif // _ITERATOR_DEBUG_LEVEL != 0 if constexpr (_Has_member_arrow || is_pointer_v<_Iter>) { - return (_Val._Iterator); // NB: () are necessary for decltype(auto) + return (_Val._First); // NB: () are necessary for decltype(auto) } else if constexpr (is_reference_v>) { - auto&& _Tmp = *_Val._Iterator; + auto&& _Tmp = *_Val._First; return _STD addressof(_Tmp); } else { class _Arrow_proxy : private _Proxy_base { @@ -902,27 +919,27 @@ public: } }; - return _Arrow_proxy{*_Val._Iterator}; + return _Arrow_proxy{*_Val._First}; } } constexpr common_iterator& operator++() { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_first, "common_iterator can only be incremented if it holds an iterator"); #endif // _ITERATOR_DEBUG_LEVEL != 0 - ++_Val._Iterator; + ++_Val._First; return *this; } constexpr decltype(auto) operator++(int) { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_first, "common_iterator can only be incremented if it holds an iterator"); #endif // _ITERATOR_DEBUG_LEVEL != 0 if constexpr (forward_iterator<_Iter>) { common_iterator _Tmp = *this; - ++_Val._Iterator; + ++_Val._First; return _Tmp; } else if constexpr (_Use_postfix_proxy<_Iter>) { class _Postfix_proxy : private _Proxy_base { @@ -936,11 +953,11 @@ public: } }; - _Postfix_proxy _Tmp{*_Val._Iterator}; - ++_Val._Iterator; + _Postfix_proxy _Tmp{*_Val._First}; + ++_Val._First; return _Tmp; } else { - return _Val._Iterator++; + return _Val._First++; } } @@ -957,19 +974,19 @@ public: "common_iterators can only be compared if both hold a value"); #endif // _ITERATOR_DEBUG_LEVEL != 0 - if (_Left._Val._Contains == _Variantish_state::_Holds_iter) { - if (_Right_val._Contains == _Variantish_state::_Holds_iter) { + if (_Left._Val._Contains == _Variantish_state::_Holds_first) { + if (_Right_val._Contains == _Variantish_state::_Holds_first) { if constexpr (equality_comparable_with<_Iter, _OIter>) { - return _Left._Val._Iterator == _Right_val._Iterator; + return _Left._Val._First == _Right_val._First; } else { return true; } } else { - return _Left._Val._Iterator == _Right_val._Sentinel; + return _Left._Val._First == _Right_val._Second; } } else { - if (_Right_val._Contains == _Variantish_state::_Holds_iter) { - return _Left._Val._Sentinel == _Right_val._Iterator; + if (_Right_val._Contains == _Variantish_state::_Holds_first) { + return _Left._Val._Second == _Right_val._First; } else { return true; } @@ -989,15 +1006,15 @@ public: "Cannot take difference of valueless common_iterators"); #endif // _ITERATOR_DEBUG_LEVEL != 0 - if (_Left._Val._Contains == _Variantish_state::_Holds_iter) { - if (_Right_val._Contains == _Variantish_state::_Holds_iter) { - return _Left._Val._Iterator - _Right_val._Iterator; + if (_Left._Val._Contains == _Variantish_state::_Holds_first) { + if (_Right_val._Contains == _Variantish_state::_Holds_first) { + return _Left._Val._First - _Right_val._First; } else { - return _Left._Val._Iterator - _Right_val._Sentinel; + return _Left._Val._First - _Right_val._Second; } } else { - if (_Right_val._Contains == _Variantish_state::_Holds_iter) { - return _Left._Val._Sentinel - _Right_val._Iterator; + if (_Right_val._Contains == _Variantish_state::_Holds_first) { + return _Left._Val._Second - _Right_val._First; } else { return 0; } @@ -1005,24 +1022,24 @@ public: } _NODISCARD_FRIEND constexpr iter_rvalue_reference_t<_Iter> iter_move(const common_iterator& _Right) noexcept( - noexcept(_RANGES iter_move(_Right._Val._Iterator))) requires input_iterator<_Iter> { + noexcept(_RANGES iter_move(_Right._Val._First))) requires input_iterator<_Iter> { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Right._Val._Contains == _Variantish_state::_Holds_iter, + _STL_VERIFY(_Right._Val._Contains == _Variantish_state::_Holds_first, "can only iter_move from common_iterator if it holds an iterator"); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _RANGES iter_move(_Right._Val._Iterator); + return _RANGES iter_move(_Right._Val._First); } template _OIter, class _OSe> friend constexpr void iter_swap(const common_iterator& _Left, const common_iterator<_OIter, _OSe>& _Right) noexcept( - noexcept(_RANGES iter_swap(_Left._Val._Iterator, _Right._Get_val()._Iterator))) { + noexcept(_RANGES iter_swap(_Left._Val._First, _Right._Get_val()._First))) { auto& _Right_val = _Right._Get_val(); #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Left._Val._Contains == _Variantish_state::_Holds_iter - && _Right_val._Contains == _Variantish_state::_Holds_iter, + _STL_VERIFY(_Left._Val._Contains == _Variantish_state::_Holds_first + && _Right_val._Contains == _Variantish_state::_Holds_first, "can only iter_swap common_iterators if both hold iterators"); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _RANGES iter_swap(_Left._Val._Iterator, _Right_val._Iterator); + return _RANGES iter_swap(_Left._Val._First, _Right_val._First); } _NODISCARD constexpr _Variantish<_Iter, _Se>& _Get_val() noexcept { diff --git a/stl/inc/ranges b/stl/inc/ranges index 3ae0270bd0e..1cffc0d8a5f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3046,7 +3046,8 @@ namespace ranges { private: struct _Cache_wrapper { template - constexpr _Cache_wrapper(_Not_quite_object::_Construct_tag, const _Iter& _It) noexcept(noexcept(*_It)) + constexpr _Cache_wrapper(_Not_quite_object::_Construct_tag, const _Iter& _It) noexcept( + noexcept(static_cast(*_It))) : _Val(*_It) {} remove_cv_t> _Val; @@ -3427,6 +3428,479 @@ namespace ranges { inline constexpr _Join_fn join; } // namespace views +#if _HAS_CXX23 + template + concept _Compatible_joinable_ranges = common_with, range_value_t<_Pat>> // + && common_reference_with, range_reference_t<_Pat>> // + && common_reference_with, range_rvalue_reference_t<_Pat>>; + + template // TRANSITION, LLVM-47414 + concept _Can_const_join_with = + input_range && forward_range && is_reference_v>; + + template + requires view<_Vw> && input_range> && view<_Pat> // + && _Compatible_joinable_ranges, _Pat> + class join_with_view; + + template + class _Join_with_view_base : public view_interface> { + private: + struct _Cache_wrapper { + template + constexpr _Cache_wrapper(_Not_quite_object::_Construct_tag, const _Iter& _It) noexcept( + noexcept(static_cast(*_It))) + : _Val(*_It) {} + + remove_cv_t> _Val; + }; + + protected: + /* [[no_unique_address]] */ _Non_propagating_cache<_Cache_wrapper, false> _Inner{}; + }; + + template + requires is_reference_v> + class _Join_with_view_base<_Vw, _Pat> : public view_interface> { + }; + + template + requires view<_Vw> && input_range> && view<_Pat> // + && _Compatible_joinable_ranges, _Pat> + class join_with_view : public _Join_with_view_base<_Vw, _Pat> { + private: + template + using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; + + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Pat _Pattern{}; + + template + class _Sentinel; + + template + struct _Category_base {}; + + template + requires forward_range<_Maybe_const<_Const, _Vw>> && forward_range<_InnerRng<_Const>> // + && is_reference_v<_InnerRng<_Const>> + struct _Category_base<_Const> { + using _Outer = _Maybe_const<_Const, _Vw>; + using _Inner = _InnerRng<_Const>; + using _PatternBase = _Maybe_const<_Const, _Pat>; + + using iterator_category = conditional_t< + !is_lvalue_reference_v, range_reference_t<_PatternBase>>>, + input_iterator_tag, + conditional_t && common_range<_PatternBase> // + && derived_from<_Iter_cat_t>, bidirectional_iterator_tag> // + && derived_from<_Iter_cat_t>, bidirectional_iterator_tag> // + && derived_from<_Iter_cat_t>, bidirectional_iterator_tag>, + bidirectional_iterator_tag, + conditional_t>, forward_iterator_tag> // + && derived_from<_Iter_cat_t>, forward_iterator_tag> // + && derived_from<_Iter_cat_t>, forward_iterator_tag>, + forward_iterator_tag, input_iterator_tag>>>; + }; + + template + class _Iterator : public _Category_base<_Const> { + private: + friend join_with_view; + + using _Parent_t = _Maybe_const<_Const, join_with_view>; + using _Base = _Maybe_const<_Const, _Vw>; + using _PatternBase = _Maybe_const<_Const, _Pat>; + + using _OuterIter = iterator_t<_Base>; + using _InnerIter = iterator_t<_InnerRng<_Const>>; + using _PatternIter = iterator_t<_PatternBase>; + + // True if and only if the expression *i, where i is an iterator from the outer range, is a glvalue: + static constexpr bool _Deref_is_glvalue = is_reference_v<_InnerRng<_Const>>; + + _Parent_t* _Parent{}; + /* [[no_unique_address]] */ _OuterIter _Outer_it{}; + _Variantish<_PatternIter, _InnerIter> _Inner_it{}; + + constexpr _Iterator(_Parent_t& _Parent_, iterator_t<_Base> _Outer_) + : _Parent{_STD addressof(_Parent_)}, _Outer_it{_STD move(_Outer_)} { + if (_Outer_it != _RANGES end(_Parent->_Range)) { + auto&& _Inner = _Update_inner(); + _Inner_it._Emplace_second(_RANGES begin(_Inner)); + _Satisfy(); + } + } + + _NODISCARD constexpr auto&& _Update_inner() { + if constexpr (_Deref_is_glvalue) { + return *_Outer_it; + } else { + return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Outer_it)._Val; + } + } + + _NODISCARD constexpr auto&& _Get_inner() noexcept { + if constexpr (_Deref_is_glvalue) { + return *_Outer_it; + } else { + return (*_Parent->_Inner)._Val; + } + } + + template + constexpr _Ret _Visit_inner_it(auto&& _Func) const { + if (_Inner_it._Contains == _Variantish_state::_Holds_first) { + return _Func(_Inner_it._First); + } else if (_Inner_it._Contains == _Variantish_state::_Holds_second) { + return _Func(_Inner_it._Second); + } else { + _Throw_bad_variant_access(); + } + } + + constexpr void _Satisfy() { + for (;;) { + if (_Inner_it._Contains == _Variantish_state::_Holds_first) { + if (_Inner_it._First != _RANGES end(_Parent->_Pattern)) { + break; + } + + auto&& _Inner = _Update_inner(); + _Inner_it._Emplace_second(_RANGES begin(_Inner)); + } else { + _STL_INTERNAL_CHECK(_Inner_it._Contains == _Variantish_state::_Holds_second); + + auto&& _Inner = _Get_inner(); + if (_Inner_it._Second != _RANGES end(_Inner)) { + break; + } + + ++_Outer_it; + if (_Outer_it == _RANGES end(_Parent->_Range)) { + if constexpr (_Deref_is_glvalue) { + _Inner_it._Emplace_first(); + } + break; + } + + _Inner_it._Emplace_first(_RANGES begin(_Parent->_Pattern)); + } + } + } + + public: + using iterator_concept = // + conditional_t<_Deref_is_glvalue && bidirectional_range<_Base> // + && _Bidi_common_range<_InnerRng<_Const>> && _Bidi_common_range<_PatternBase>, + bidirectional_iterator_tag, + conditional_t<_Deref_is_glvalue && forward_range<_Base> && forward_range<_InnerRng<_Const>>, + forward_iterator_tag, input_iterator_tag>>; + using value_type = common_type_t, iter_value_t<_PatternIter>>; + using difference_type = _Common_diff_t<_OuterIter, _InnerIter, _PatternIter>; + + // clang-format off + _Iterator() requires default_initializable<_OuterIter> = default; + // clang-format on + + constexpr _Iterator(_Iterator _It) requires _Const // + && convertible_to, _OuterIter> // + && convertible_to>, _InnerIter> // + && convertible_to, _PatternIter> // + : _Outer_it{_STD move(_It._Outer_it)}, _Parent{_It._Parent} { + switch (_It._Inner_it._Contains) { + case _Variantish_state::_Holds_first: + _Inner_it._Emplace_first(_STD move(_It._Inner_it._First)); + break; + case _Variantish_state::_Holds_second: + _Inner_it._Emplace_second(_STD move(_It._Inner_it._Second)); + break; + case _Variantish_state::_Nothing: + _Throw_bad_variant_access(); + } + } + + _NODISCARD constexpr decltype(auto) operator*() const { + using _Ref = common_reference_t, iter_reference_t<_PatternIter>>; + return _Visit_inner_it<_Ref>([](auto&& _It) -> _Ref { return *_It; }); + } + + constexpr _Iterator& operator++() { + switch (_Inner_it._Contains) { + case _Variantish_state::_Holds_first: + ++_Inner_it._First; + break; + case _Variantish_state::_Holds_second: + ++_Inner_it._Second; + break; + case _Variantish_state::_Nothing: + _Throw_bad_variant_access(); + } + _Satisfy(); + return *this; + } + + constexpr void operator++(int) { + ++*this; + } + + constexpr _Iterator operator++(int) requires _Deref_is_glvalue + && forward_iterator<_OuterIter> && forward_iterator<_InnerIter> { + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr _Iterator& operator--() requires _Deref_is_glvalue && bidirectional_range<_Base> // + && _Bidi_common_range<_InnerRng<_Const>> && _Bidi_common_range<_PatternBase> { + if (_Outer_it == _RANGES end(_Parent->_Range)) { + --_Outer_it; + auto&& _Inner = *_Outer_it; + _Inner_it._Emplace_second(_RANGES end(_Inner)); + } + + for (;;) { + if (_Inner_it._Contains == _Variantish_state::_Holds_first) { + auto& _It = _Inner_it._First; + if (_It == _RANGES begin(_Parent->_Pattern)) { + --_Outer_it; + auto&& _Inner = *_Outer_it; + _Inner_it._Emplace_second(_RANGES end(_Inner)); + } else { + break; + } + } else if (_Inner_it._Contains == _Variantish_state::_Holds_second) { + auto& _It = _Inner_it._Second; + auto&& _Inner = *_Outer_it; + if (_It == _RANGES begin(_Inner)) { + _Inner_it._Emplace_first(_RANGES end(_Parent->_Pattern)); + } else { + break; + } + } else { + _Throw_bad_variant_access(); + } + } + + switch (_Inner_it._Contains) { + case _Variantish_state::_Holds_first: + --_Inner_it._First; + break; + case _Variantish_state::_Holds_second: + --_Inner_it._Second; + break; + case _Variantish_state::_Nothing: + _Throw_bad_variant_access(); + } + return *this; + } + + constexpr _Iterator operator--(int) requires _Deref_is_glvalue && bidirectional_range<_Base> // + && _Bidi_common_range<_InnerRng<_Const>> && _Bidi_common_range<_PatternBase> { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + _NODISCARD_FRIEND constexpr bool operator==( + const _Iterator& _Left, const _Iterator& _Right) requires _Deref_is_glvalue + && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { + if (_Left._Outer_it != _Right._Outer_it) { + return false; + } + + if (_Left._Inner_it._Contains != _Right._Inner_it._Contains) { + return false; + } + + switch (_Left._Inner_it._Contains) { + case _Variantish_state::_Holds_first: + return _Left._Inner_it._First == _Right._Inner_it._First; + case _Variantish_state::_Holds_second: + return _Left._Inner_it._Second == _Right._Inner_it._Second; + case _Variantish_state::_Nothing: + return true; + } + + _STL_UNREACHABLE; + } + + _NODISCARD_FRIEND constexpr decltype(auto) iter_move(const _Iterator& _It) { + using _Rvalue_ref = + common_reference_t, iter_rvalue_reference_t<_PatternIter>>; + return _It._Visit_inner_it<_Rvalue_ref>(_RANGES iter_move); + } + + friend constexpr void iter_swap(const _Iterator& _Left, + const _Iterator& _Right) requires indirectly_swappable<_InnerIter, _PatternIter> { + switch (_Left._Inner_it._Contains) { + case _Variantish_state::_Holds_first: + switch (_Right._Inner_it._Contains) { + case _Variantish_state::_Holds_first: + return _RANGES iter_swap(_Left._Inner_it._First, _Right._Inner_it._First); + case _Variantish_state::_Holds_second: + return _RANGES iter_swap(_Left._Inner_it._First, _Right._Inner_it._Second); + case _Variantish_state::_Nothing: + break; + } + break; + case _Variantish_state::_Holds_second: + switch (_Right._Inner_it._Contains) { + case _Variantish_state::_Holds_first: + return _RANGES iter_swap(_Left._Inner_it._Second, _Right._Inner_it._First); + case _Variantish_state::_Holds_second: + return _RANGES iter_swap(_Left._Inner_it._Second, _Right._Inner_it._Second); + case _Variantish_state::_Nothing: + break; + } + break; + case _Variantish_state::_Nothing: + break; + } + + _Throw_bad_variant_access(); + } + }; + + template + class _Sentinel { + private: + friend join_with_view; + + using _Parent_t = _Maybe_const<_Const, join_with_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + /* [[no_unique_address]] */ sentinel_t<_Base> _Last{}; + + constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept( + noexcept(_RANGES end(_Parent._Range)) + && is_nothrow_move_constructible_v>) // strengthened + : _Last(_RANGES end(_Parent._Range)) {} + + template + _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const + noexcept(noexcept(_Implicitly_convert_to(_It._Outer_it == _Last))) { + _STL_INTERNAL_STATIC_ASSERT( + sentinel_for, iterator_t<_Maybe_const<_OtherConst, _Vw>>>); + return _It._Outer_it == _Last; + } + + public: + _Sentinel() = default; + + constexpr _Sentinel(_Sentinel _Se) noexcept( + is_nothrow_constructible_v, sentinel_t<_Vw>>) // strengthened + requires _Const && convertible_to, sentinel_t<_Base>> // + : _Last{_STD move(_Se._Last)} {} + + template + requires sentinel_for, iterator_t<_Maybe_const<_OtherConst, _Vw>>> + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator<_OtherConst>& _Left, + const _Sentinel& _Right) noexcept(noexcept(_Right._Equal(_Left))) /* strengthened */ { + return _Right._Equal(_Left); + } + }; + + public: + // clang-format off + join_with_view() requires default_initializable<_Vw> && default_initializable<_Pat> = default; + // clang-format on + + constexpr join_with_view(_Vw _Range_, _Pat _Pattern_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Pat>) // strengthened + : _Range{_STD move(_Range_)}, _Pattern{_STD move(_Pattern_)} {} + + template + requires constructible_from<_Vw, views::all_t<_Rng>> && constructible_from<_Pat, + single_view>>> + constexpr join_with_view(_Rng&& _Range_, range_value_t<_InnerRng> _Elem) noexcept( + noexcept(_Vw(views::all(_STD forward<_Rng>(_Range_)))) && noexcept( + _Pat(views::single(_STD move(_Elem))))) // strengthened + : _Range(views::all(_STD forward<_Rng>(_Range_))), _Pattern(views::single(_STD move(_Elem))) {} + + _NODISCARD constexpr _Vw base() const& noexcept(is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ + requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr auto begin() { + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng> && _Simple_view<_Pat>; + return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + } + + _NODISCARD constexpr auto begin() const +#ifdef __clang__ // TRANSITION, LLVM-47414 + requires _Can_const_join_with<_Vw, _Pat> +#else // ^^^ workaround / no workaround vvv + requires input_range && forward_range && is_reference_v<_InnerRng> +#endif // TRANSITION, LLVM-47414 + { + return _Iterator{*this, _RANGES begin(_Range)}; + } + + _NODISCARD constexpr auto end() { + constexpr bool _Both_simple = _Simple_view<_Vw> && _Simple_view<_Pat>; + if constexpr (forward_range<_Vw> // + && is_reference_v<_InnerRng> && forward_range<_InnerRng> // + && common_range<_Vw> && common_range<_InnerRng>) { + return _Iterator<_Both_simple>{*this, _RANGES end(_Range)}; + } else { + return _Sentinel<_Both_simple>{*this}; + } + } + + _NODISCARD constexpr auto end() const +#ifdef __clang__ // TRANSITION, LLVM-47414 + requires _Can_const_join_with<_Vw, _Pat> +#else // ^^^ workaround / no workaround vvv + requires input_range && forward_range && is_reference_v<_InnerRng> +#endif // TRANSITION, LLVM-47414 + { + if constexpr (forward_range && forward_range<_InnerRng> // + && common_range<_Vw> && common_range<_InnerRng>) { + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{*this}; + } + } + }; + + template + join_with_view(_Rng&&, _Pat&&) -> join_with_view, views::all_t<_Pat>>; + + template + join_with_view(_Rng&&, range_value_t>) + -> join_with_view, single_view>>>; + + namespace views { + struct _Join_with_fn { + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pat&& _Pattern) const noexcept( + noexcept(join_with_view(_STD forward<_Rng>(_Range), _STD forward<_Pat>(_Pattern)))) requires requires { + join_with_view(_STD forward<_Rng>(_Range), _STD forward<_Pat>(_Pattern)); + } + { // clang-format on + return join_with_view(_STD forward<_Rng>(_Range), _STD forward<_Pat>(_Pattern)); + } + + // clang-format off + template + requires constructible_from, _Delim> + _NODISCARD constexpr auto operator()(_Delim&& _Delimiter) const + noexcept(is_nothrow_constructible_v, _Delim>) { + // clang-format on + return _Range_closure<_Join_with_fn, decay_t<_Delim>>{_STD forward<_Delim>(_Delimiter)}; + } + }; + + inline constexpr _Join_with_fn join_with; + } // namespace views +#endif // _HAS_CXX23 + template // _Require_constant is a valid template-id iff E is a constant expression of structural type struct _Require_constant; diff --git a/stl/inc/variant b/stl/inc/variant index 46e12983ca5..8b4845bf6f0 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -392,27 +392,6 @@ struct variant_alternative<_Idx, variant<_Types...>> { inline constexpr size_t variant_npos = _Meta_npos; -class bad_variant_access - : public exception { // exception for visit of a valueless variant or get on a variant with index() != I -public: - bad_variant_access() noexcept = default; - - _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { - return "bad variant access"; - } - -#if !_HAS_EXCEPTIONS -protected: - void _Doraise() const override { // perform class-specific exception handling - _RAISE(*this); - } -#endif // !_HAS_EXCEPTIONS -}; - -[[noreturn]] inline void _Throw_bad_variant_access() { - _THROW(bad_variant_access{}); -} - template class _Variant_storage_ {}; // empty storage (empty "_Types" case) diff --git a/stl/inc/xutility b/stl/inc/xutility index 6a198cd1931..c304fbc199c 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2683,6 +2683,11 @@ namespace ranges { template concept common_range = range<_Rng> && same_as, sentinel_t<_Rng>>; + template + concept _Bidi_common = is_same_v<_It, _Se> && bidirectional_iterator<_It>; + template + concept _Bidi_common_range = common_range<_Rng> && bidirectional_iterator>; + template concept _Can_empty = requires(_Ty __t) { _RANGES empty(__t); diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index f0f9f46f743..f4f117e8514 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -310,6 +310,7 @@ // P2321R2 zip // (changes to pair, tuple, and vector::reference only) // P2440R1 ranges::iota, ranges::shift_left, ranges::shift_right +// P2441R2 views::join_with // P2442R1 Windowing Range Adaptors: views::chunk, views::slide // P2443R1 views::chunk_by // P2549R0 unexpected::error() @@ -1470,6 +1471,7 @@ #define __cpp_lib_ranges_chunk 202202L #define __cpp_lib_ranges_chunk_by 202202L #define __cpp_lib_ranges_iota 202202L +#define __cpp_lib_ranges_join_with 202202L #define __cpp_lib_ranges_slide 202202L #define __cpp_lib_ranges_starts_ends_with 202106L #endif // __cpp_lib_concepts diff --git a/tests/std/test.lst b/tests/std/test.lst index 6b3827e7159..2be5a523ec5 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -490,6 +490,7 @@ tests\P2415R2_owning_view tests\P2440R1_ranges_alg_shift_left tests\P2440R1_ranges_alg_shift_right tests\P2440R1_ranges_numeric_iota +tests\P2441R2_views_join_with tests\P2442R1_views_chunk tests\P2442R1_views_chunk_death tests\P2442R1_views_slide diff --git a/tests/std/tests/P0896R4_common_iterator_death/test.cpp b/tests/std/tests/P0896R4_common_iterator_death/test.cpp index 9409bcfb9a2..20facf56200 100644 --- a/tests/std/tests/P0896R4_common_iterator_death/test.cpp +++ b/tests/std/tests/P0896R4_common_iterator_death/test.cpp @@ -54,7 +54,7 @@ void test_case_operator_dereference_sentinel() { } void test_case_operator_dereference_valueless() { - CIT cit{_Common_iterator_construct_tag{}}; + CIT cit{_Variantish_empty_tag{}}; (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator } @@ -64,7 +64,7 @@ void test_case_operator_dereference_const_sentinel() { } void test_case_operator_dereference_const_valueless() { - const CIT cit{_Common_iterator_construct_tag{}}; + const CIT cit{_Variantish_empty_tag{}}; (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator } @@ -73,7 +73,7 @@ void test_case_operator_arrow_sentinel() { (void) (cit.operator->()); // common_iterator can only be dereferenced if it holds an iterator } void test_case_operator_arrow_valueless() { - CIT cit{_Common_iterator_construct_tag{}}; + CIT cit{_Variantish_empty_tag{}}; (void) (cit.operator->()); // common_iterator can only be dereferenced if it holds an iterator } @@ -83,7 +83,7 @@ void test_case_operator_preincrement_sentinel() { } void test_case_operator_preincrement_valueless() { - CIT cit{_Common_iterator_construct_tag{}}; + CIT cit{_Variantish_empty_tag{}}; ++cit; // common_iterator can only be preincremented if it holds an iterator } @@ -93,31 +93,31 @@ void test_case_operator_postincrement_sentinel() { } void test_case_operator_postincrement_valueless() { - CIT cit{_Common_iterator_construct_tag{}}; + CIT cit{_Variantish_empty_tag{}}; cit++; // common_iterator can only be postincremented if it holds an iterator } void test_case_equality_left_valueless() { - CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit1{_Variantish_empty_tag{}}; CIT cit2{}; (void) (cit1 == cit2); // common_iterator can only be compared if it holds a value } void test_case_equality_right_valueless() { CIT cit1{}; - CIT cit2{_Common_iterator_construct_tag{}}; + CIT cit2{_Variantish_empty_tag{}}; (void) (cit1 == cit2); // common_iterator can only be compared if it holds a value } void test_case_difference_left_valueless() { - CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit1{_Variantish_empty_tag{}}; CIT cit2{}; (void) (cit1 - cit2); // common_iterator can only be subtracted if it holds a value } void test_case_difference_right_valueless() { CIT cit1{}; - CIT cit2{_Common_iterator_construct_tag{}}; + CIT cit2{_Variantish_empty_tag{}}; (void) (cit1 - cit2); // common_iterator can only be subtracted if it holds a value } @@ -127,7 +127,7 @@ void test_case_iter_move_sentinel() { } void test_case_iter_move_valueless() { - CIT cit{_Common_iterator_construct_tag{}}; + CIT cit{_Variantish_empty_tag{}}; (void) ranges::iter_move(cit); // can only iter_move from common_iterator if it holds an iterator } @@ -138,7 +138,7 @@ void test_case_iter_swap_sentinel_left_sentinel() { } void test_case_iter_swap_sentinel_left_valueless() { - CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit1{_Variantish_empty_tag{}}; CIT cit2{}; (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators } @@ -151,7 +151,7 @@ void test_case_iter_swap_sentinel_right_sentinel() { void test_case_iter_swap_sentinel_right_valueless() { CIT cit1{}; - CIT cit2{_Common_iterator_construct_tag{}}; + CIT cit2{_Variantish_empty_tag{}}; (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators } diff --git a/tests/std/tests/P2441R2_views_join_with/env.lst b/tests/std/tests/P2441R2_views_join_with/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P2441R2_views_join_with/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp new file mode 100644 index 00000000000..81d5b2bc846 --- /dev/null +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -0,0 +1,517 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +template +concept CanViewJoinWith = requires(Rng&& r, Delimiter&& d) { + views::join_with(forward(r), forward(d)); +}; + +template +struct delimiter_view_impl { + template + using apply = ranges::single_view>; +}; +template <> +struct delimiter_view_impl { + template + using apply = views::all_t; +}; +template +using delimiter_view_t = + typename delimiter_view_impl>>::template apply; + +template +constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) { + using ranges::join_with_view, ranges::begin, ranges::end, ranges::prev, ranges::input_range, ranges::forward_range, + ranges::bidirectional_range, ranges::common_range, ranges::iterator_t, ranges::sentinel_t, + ranges::range_value_t, ranges::range_reference_t; + + using Inner = range_value_t; + constexpr bool deref_is_glvalue = is_reference_v>; + + using V = views::all_t; + using DV = delimiter_view_t; + using R = join_with_view; + + // Validate type properties + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range); + STATIC_ASSERT(forward_range == (deref_is_glvalue && forward_range && forward_range) ); + // clang-format off + STATIC_ASSERT(bidirectional_range + == (deref_is_glvalue && bidirectional_range && bidirectional_range + && common_range && bidirectional_range && common_range) ); + // clang-format on + STATIC_ASSERT(!ranges::random_access_range); + + // Validate range adaptor object and range adaptor closure + constexpr bool is_view = ranges::view>; + const auto closure = views::join_with(delimiter); + + // ... with lvalue argument + STATIC_ASSERT(CanViewJoinWith == (!is_view || copy_constructible) ); + if constexpr (CanViewJoinWith) { + constexpr bool is_noexcept = + (!is_view || is_nothrow_copy_constructible_v) &&is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::join_with(rng, delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT( + CanViewJoinWith&, Delimiter&> == (!is_view || copy_constructible) ); + if constexpr (CanViewJoinWith&, Delimiter&>) { + using RC = join_with_view&>, DV>; + constexpr bool is_noexcept = + (!is_view || is_nothrow_copy_constructible_v) &&is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::join_with(as_const(rng), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT( + CanViewJoinWith, Delimiter&> == (is_view || movable>) ); + if constexpr (CanViewJoinWith, Delimiter&>) { + using RS = join_with_view>, DV>; + constexpr bool is_noexcept = is_nothrow_move_constructible_v && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::join_with(move(rng), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewJoinWith, Delimiter&> == (is_view && copy_constructible) ); + if constexpr (CanViewJoinWith, Delimiter&>) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::join_with(move(as_const(rng)), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); + } + + // Validate deduction guide + same_as auto r = join_with_view{forward(rng), forward(delimiter)}; + assert(ranges::equal(r, expected)); + + // Validate view_interface::empty and operator bool + const bool is_empty = ranges::empty(expected); + STATIC_ASSERT(CanEmpty == forward_range); + STATIC_ASSERT(CanMemberEmpty == CanEmpty); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + + STATIC_ASSERT(CanEmpty == forward_range); + STATIC_ASSERT(CanMemberEmpty == CanEmpty); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate join_with_view::begin + STATIC_ASSERT(CanMemberBegin); + // clang-format off + STATIC_ASSERT(CanMemberBegin + == (input_range && forward_range && is_reference_v>) ); + // clang-format on + if (forward_range) { // intentionally not if constexpr + const auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copy_constructible) { + auto r2 = r; + const auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + static_assert(CanMemberBegin == CanBegin); + if constexpr (CanMemberBegin) { + const same_as> auto ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *i); + } + + if constexpr (copy_constructible) { + const auto r2 = r; + const same_as> auto ci2 = r2.begin(); + if (!is_empty) { + assert(*ci2 == *i); + } + } + } + } + + // Validate join_with_view::end + static_assert(CanMemberEnd); + // clang-format off + static_assert(CanMemberEnd + == (input_range && forward_range && is_reference_v>) ); + static_assert(common_range + == (forward_range && is_reference_v> && common_range + && forward_range && common_range) ); + static_assert(common_range + == (forward_range && forward_range && is_reference_v> + && common_range && forward_range> + && common_range>) ); + // clang-format on + const same_as> auto s = r.end(); + if (!is_empty) { + if constexpr (bidirectional_range && common_range) { + assert(*prev(s) == *prev(end(expected))); + + if constexpr (copyable) { + auto r2 = r; + assert(*prev(r2.end()) == *prev(end(expected))); + } + } + + static_assert(CanMemberEnd == CanEnd); + if constexpr (CanMemberEnd) { + const same_as> auto cs = as_const(r).end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(cs) == *prev(end(expected))); + + if constexpr (copyable) { + const auto r2 = r; + const same_as> auto cs2 = r2.end(); + assert(*prev(cs2) == *prev(end(expected))); + } + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Validate view_interface::size + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + + // Validate view_interface::operator[] + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); + + // Validate view_interface::front and back + static_assert(CanMemberFront == forward_range); + static_assert(CanMemberFront == forward_range); + if (!is_empty) { + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected)); + } + + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + } + + static_assert(CanMemberBack == (bidirectional_range && common_range) ); + static_assert(CanMemberBack == (bidirectional_range && common_range) ); + if (!is_empty) { + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate join_with_view::base() const& + static_assert(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + static_assert(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + auto bi1 = b1.begin(); + if (!ranges::empty(*bi1)) { + auto&& inner_first = *bi1; + assert(*begin(inner_first) == *begin(expected)); + } + } + } + + // Validate join_view::base() && (NB: do this last since it leaves r moved-from) + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if constexpr (CanEmpty) { + if (!is_empty) { + auto bi2 = b2.begin(); + if (!ranges::empty(*bi2)) { + auto&& inner_first = *bi2; + assert(*begin(inner_first) == *begin(expected)); + } + } + } + } +} + +constexpr string_view input[] = {{}, "This"sv, "is"sv, {}, "a"sv, "test"sv, {}, {}}; +constexpr string_view expected_single = "*This*is**a*test**"; +constexpr string_view expected_range = "*#This*#is*#*#a*#test*#*#"; +constexpr string_view expected_empty = "Thisisatest"; + +struct instantiator { + template + static constexpr void call() { + static_assert(ranges::size(input) == 8); + + { // Single-element delimiter + Inner inner_ranges[] = {Inner{span{input[0]}}, Inner{span{input[1]}}, Inner{span{input[2]}}, + Inner{span{input[3]}}, Inner{span{input[4]}}, Inner{span{input[5]}}, Inner{span{input[6]}}, + Inner{span{input[7]}}}; + Outer r{inner_ranges}; + test_one(r, '*', expected_single); + + Outer empty{span{}}; + test_one(empty, '*', views::empty); + } + { // Empty delimiter + Inner inner_ranges[] = {Inner{span{input[0]}}, Inner{span{input[1]}}, Inner{span{input[2]}}, + Inner{span{input[3]}}, Inner{span{input[4]}}, Inner{span{input[5]}}, Inner{span{input[6]}}, + Inner{span{input[7]}}}; + Outer r{inner_ranges}; + test_one(r, views::empty, expected_empty); + + Outer empty{span{}}; + test_one(empty, views::empty, views::empty); + } + { // Range delimiter + Inner inner_ranges[] = {Inner{span{input[0]}}, Inner{span{input[1]}}, Inner{span{input[2]}}, + Inner{span{input[3]}}, Inner{span{input[4]}}, Inner{span{input[5]}}, Inner{span{input[6]}}, + Inner{span{input[7]}}}; + Outer r{inner_ranges}; + test_one(r, "*#"sv, expected_range); + + Outer empty{span{}}; + test_one(empty, "*#"sv, views::empty); + } + } +}; + +enum class RefOrView { reference, view }; + +template > +using inner_test_range = test::range || IsCommon == test::Common::yes}, + test::ProxyRef::no, IsView, test::Copyability::copyable>; + +template > +using outer_test_range = test::range || IsCommon == test::Common::yes}, + (RV == RefOrView::view ? test::ProxyRef::prvalue : test::ProxyRef::no), test::CanView::yes, + test::Copyability::copyable>; + +constexpr bool instantiation_test() { + // The adaptor is sensitive to: + // * inner, outer, and pattern range common category (input, forward, bidi) + // * outer range's reference type referenceness vs. value type viewness + // * if the inner range models common_range + // * if the outer range models common_range + // * if the pattern range models common_range + // * if both inner and outer iterators are equality_comparable (the defaults for input-non-common and forward + // suffice to get coverage here) + using test::CanView, test::Common; + + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::yes>>(); + + return true; +} + +struct throwing_iterator { + struct tag {}; + + throwing_iterator() = default; + throwing_iterator(int j) : i(j) {} + throwing_iterator(const throwing_iterator& other) : i(other.i) { + if (i == 1) { + throw tag{}; + } + } + throwing_iterator& operator=(const throwing_iterator&) = default; + + using difference_type = ptrdiff_t; + using value_type = int; + + throwing_iterator& operator++() { + ++i; + return *this; + } + throwing_iterator operator++(int) { + return throwing_iterator{i++}; + } + throwing_iterator& operator--() { + --i; + return *this; + } + throwing_iterator operator--(int) { + return throwing_iterator{i--}; + } + int operator*() const { + return i; + } + bool operator==(const throwing_iterator& other) const { + return i == other.i; + } + int i; +}; + +void test_valueless_iterator() { + auto r = vector{"0"sv, ""sv} | views::join_with(ranges::subrange{throwing_iterator{0}, throwing_iterator{2}}); + + auto it = r.begin(); + ++it; + ++it; + assert(*it == 1); + auto it2 = r.begin(); + try { + it2 = it; + assert(false); + } catch (throwing_iterator::tag&) { + } + + try { + (void) *it2; + assert(false); + } catch (bad_variant_access&) { + } + try { + (void) ++it2; + assert(false); + } catch (bad_variant_access&) { + } + try { + (void) --it2; + assert(false); + } catch (bad_variant_access&) { + } + try { + (void) ranges::iter_move(it2); + assert(false); + } catch (bad_variant_access&) { + } +} + +int main() { + { + auto filtered_and_joined = + vector>{} | views::filter([](auto) { return true; }) | views::join_with(0); + assert(ranges::empty(filtered_and_joined)); + } + + STATIC_ASSERT(instantiation_test()); + instantiation_test(); + + test_valueless_iterator(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index df1982a120c..79acad7c88f 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1408,6 +1408,20 @@ STATIC_ASSERT(__cpp_lib_ranges_iota == 202202L); #endif #endif +#if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support +#ifndef __cpp_lib_ranges_join_with +#error __cpp_lib_ranges_join_with is not defined +#elif __cpp_lib_ranges_join_with != 202202L +#error __cpp_lib_ranges_join_with is not 202202L +#else +STATIC_ASSERT(__cpp_lib_ranges_join_with == 202202L); +#endif +#else +#ifdef __cpp_lib_ranges_join_with +#error __cpp_lib_ranges_join_with is defined +#endif +#endif + #if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support #ifndef __cpp_lib_ranges_slide #error __cpp_lib_ranges_slide is not defined