diff --git a/stl/inc/iterator b/stl/inc/iterator index 6c36d930059..fa219671771 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -513,6 +513,511 @@ private: }; #ifdef __cpp_lib_concepts +// CLASS TEMPLATE common_iterator +enum class _Variantish_state : unsigned char { _Nothing, _Holds_iter, _Holds_sentinel }; + +struct _Common_iterator_construct_tag { + explicit _Common_iterator_construct_tag() = default; +}; + +template _Se> +class _Variantish { +public: + constexpr explicit _Variantish(_Common_iterator_construct_tag) noexcept : _Contains{_Variantish_state::_Nothing} {} + + constexpr _Variantish() noexcept(is_nothrow_default_constructible_v<_It>) + : _Iterator{}, _Contains{_Variantish_state::_Holds_iter} {} + + template + constexpr explicit _Variantish(in_place_type_t<_It>, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_It, _Types...>) + : _Iterator(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_iter} {} + + template + constexpr explicit _Variantish(in_place_type_t<_Se>, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_Se, _Types...>) + : _Sentinel(_STD forward<_Types>(_Args)...), _Contains{_Variantish_state::_Holds_sentinel} {} + + // clang-format off + template + requires _Not_same_as<_Variantish<_OIter, _OSe>, _Variantish> + _Variantish(const _Variantish<_OIter, _OSe>& _That) noexcept( + is_nothrow_constructible_v<_It, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&>) + : _Contains{_That._Contains} { + // clang-format on + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish(const _Variantish&) requires is_trivially_copy_constructible_v<_It> + && is_trivially_copy_constructible_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish(const _Variantish& _That) noexcept( + noexcept(is_nothrow_copy_constructible_v<_It>&& is_nothrow_copy_constructible_v<_Se>)) + : _Contains{_That._Contains} { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish(_Variantish&&) requires is_trivially_move_constructible_v<_It> + && is_trivially_move_constructible_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish(_Variantish&& _That) noexcept( + is_nothrow_move_constructible_v<_It>&& is_nothrow_move_constructible_v<_Se>) + : _Contains{_That._Contains} { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _STD move(_That._Iterator)); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _STD move(_That._Sentinel)); + break; + case _Variantish_state::_Nothing: + break; + } + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + ~_Variantish() requires is_trivially_destructible_v<_It> && is_trivially_destructible_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + ~_Variantish() { + _Raw_clear(); + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish& operator=(const _Variantish&) requires is_trivially_destructible_v<_It> + && is_trivially_destructible_v<_Se> + && is_trivially_copy_constructible_v<_It> + && is_trivially_copy_constructible_v<_Se> + && is_trivially_copy_assignable_v<_It> + && is_trivially_copy_assignable_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish& operator=(const _Variantish& _That) noexcept( + is_nothrow_copy_constructible_v<_It>&& is_nothrow_copy_constructible_v<_Se>&& + is_nothrow_copy_assignable_v<_It>&& is_nothrow_copy_assignable_v<_Se>) { + if (_Contains == _That._Contains) { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator = _That._Iterator; + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel = _That._Sentinel; + break; + case _Variantish_state::_Nothing: + break; + } + + return *this; + } + + _Clear(); + + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + _Contains = _That._Contains; + + return *this; + } + +#if 0 // TRANSITION, VSO-1174090 + // clang-format off + _Variantish& operator=(_Variantish&&) requires is_trivially_destructible_v<_It> + && is_trivially_destructible_v<_Se> + && is_trivially_move_constructible_v<_It> + && is_trivially_move_constructible_v<_Se> + && is_trivially_move_assignable_v<_It> + && is_trivially_move_assignable_v<_Se> = default; + // clang-format on +#endif // TRANSITION, VSO-1174090 + + _Variantish& operator=(_Variantish&& _That) noexcept( + is_nothrow_move_constructible_v<_It>&& is_nothrow_move_constructible_v<_Se>&& + is_nothrow_move_assignable_v<_It>&& is_nothrow_move_assignable_v<_Se>) { + if (_Contains == _That._Contains) { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator = _STD move(_That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel = _STD move(_That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + return *this; + } + + _Clear(); + + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _STD move(_That._Iterator)); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _STD move(_That._Sentinel)); + break; + case _Variantish_state::_Nothing: + break; + } + + _Contains = _That._Contains; + + return *this; + } + + // clang-format off + template + requires _Not_same_as<_Variantish<_OIter, _OSe>, _Variantish> + _Variantish& operator=(const _Variantish<_OIter, _OSe>& _That) noexcept( + is_nothrow_constructible_v<_It, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&> + && is_nothrow_assignable_v<_It&, const _OIter&> && is_nothrow_assignable_v<_Se&, const _OSe&>) { + // clang-format on + if (_Contains == _That._Contains) { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator = _That._Iterator; + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel = _That._Sentinel; + break; + case _Variantish_state::_Nothing: + break; + } + + return *this; + } + + _Clear(); + + switch (_That._Contains) { + case _Variantish_state::_Holds_iter: + _Construct_in_place(_Iterator, _That._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _Construct_in_place(_Sentinel, _That._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + _Contains = _That._Contains; + + return *this; + } + + // clang-format off + constexpr friend void swap(_Variantish& _Left, _Variantish& _Right) noexcept( + is_nothrow_move_constructible_v<_It> && is_nothrow_move_constructible_v<_Se> + && is_nothrow_swappable_v<_It> && is_nothrow_swappable_v<_Se>) + requires (!_Is_trivially_swappable_v<_It> || !_Is_trivially_swappable_v<_Se>) { + // clang-format on + if (_Left._Contains == _Right._Contains) { + switch (_Left._Contains) { + case _Variantish_state::_Holds_iter: + _RANGES swap(_Left._Iterator, _Right._Iterator); + break; + case _Variantish_state::_Holds_sentinel: + _RANGES swap(_Left._Sentinel, _Right._Sentinel); + break; + case _Variantish_state::_Nothing: + break; + } + + return; + } + + auto _Tmp = _STD move(_Left); + _Left = _STD move(_Right); + _Right = _STD move(_Tmp); + } + + void _Raw_clear() noexcept { + switch (_Contains) { + case _Variantish_state::_Holds_iter: + _Iterator.~_It(); + break; + case _Variantish_state::_Holds_sentinel: + _Sentinel.~_Se(); + break; + case _Variantish_state::_Nothing: + break; + } + } + + void _Clear() noexcept { + _Raw_clear(); + _Contains = _Variantish_state::_Nothing; + } + + union { + _It _Iterator; + _Se _Sentinel; + }; + + _Variantish_state _Contains; +}; + +// clang-format off +template _Se> + requires (!same_as<_Iter, _Se> && copyable<_Iter>) +class common_iterator { + // clang-format on +private: + class _Proxy { + private: + iter_value_t<_Iter> _Keep; + + public: + explicit _Proxy(iter_reference_t<_Iter>&& _Right) noexcept( + is_nothrow_move_constructible_v>) // strengthened + : _Keep(_STD move(_Right)) {} + + _NODISCARD const iter_value_t<_Iter>* operator->() const noexcept /* strengthened */ { + return _STD addressof(_Keep); + } + }; + +public: + constexpr common_iterator() = default; + + constexpr common_iterator(_Iter _Right) noexcept(is_nothrow_move_constructible_v<_Iter>) // strengthened + : _Val{in_place_type<_Iter>, _STD move(_Right)} {} + + constexpr common_iterator(_Se _Right) noexcept(is_nothrow_move_constructible_v<_Se>) // strengthened + : _Val{in_place_type<_Se>, _STD move(_Right)} {} + + constexpr explicit common_iterator(_Common_iterator_construct_tag _Tag) noexcept : _Val{_Tag} {} + + // clang-format off + template + requires convertible_to && convertible_to + constexpr common_iterator(const common_iterator<_OIter, _OSe>& _Right) noexcept( + is_nothrow_constructible_v<_Iter, const _OIter&>&& is_nothrow_constructible_v<_Se, const _OSe&>) // strengthened + : _Val{_Right._Val} {} + + template + requires convertible_to && convertible_to + && assignable_from<_Iter&, const _OIter&> && assignable_from<_Se&, const _OSe&> + common_iterator& operator=(const common_iterator<_OIter, _OSe>& _Right) noexcept( + is_nothrow_constructible_v<_Iter, const _OIter&> && is_nothrow_constructible_v<_Se, const _OSe&> + && is_nothrow_assignable_v<_Iter&, const _OIter&> + && is_nothrow_assignable_v<_Se&, const _OSe&>) /* strengthened */ { + // clang-format on + _Val = _Right._Val; + return *this; + } + + _NODISCARD decltype(auto) operator*() { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be dereferenced if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Val._Iterator; + } + + _NODISCARD decltype(auto) operator*() const requires _Dereferenceable { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be dereferenced if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Val._Iterator; + } + + // clang-format off + _NODISCARD decltype(auto) operator->() const + requires indirectly_readable + && (_Has_member_arrow<_Iter> || is_reference_v> + || constructible_from, iter_reference_t<_Iter>>) { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be dereferenced if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (is_pointer_v<_Iter> || _Has_member_arrow<_Iter>) { + return (_Val._Iterator); // NB: () are necessary for decltype(auto) + } else if constexpr (is_reference_v>) { + auto&& _Tmp = *_Val._Iterator; + return _STD addressof(_Tmp); + } else { + return _Proxy{*_Val._Iterator}; + } + } + + common_iterator& operator++() { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be incremented if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + ++_Val._Iterator; + return *this; + } + + decltype(auto) operator++(int) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Val._Contains == _Variantish_state::_Holds_iter, + "common_iterator can only be incremented if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (forward_iterator<_Iter>) { + common_iterator _Tmp = *this; + ++_Val._Iterator; + return _Tmp; + } else { + return _Val._Iterator++; + } + } + + // clang-format off + template _OSe> + requires sentinel_for<_Se, _OIter> + _NODISCARD friend bool operator==(const common_iterator& _Left, const common_iterator<_OIter, _OSe>& _Right) { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Left._Val._Contains != _Variantish_state::_Nothing && _Right._Val._Contains != _Variantish_state::_Nothing, + "common_iterators can only be compared if both hold a value"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + if (_Left._Val._Contains == _Variantish_state::_Holds_iter) { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + if constexpr (equality_comparable_with<_Iter, _OIter>) { + return _Left._Val._Iterator == _Right._Val._Iterator; + } else { + return true; + } + } else { + return _Left._Val._Iterator == _Right._Val._Sentinel; + } + } else { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + return _Left._Val._Sentinel == _Right._Val._Iterator; + } else { + return true; + } + } + } + + // clang-format off + template _OIter, sized_sentinel_for<_Iter> _OSe> + requires sized_sentinel_for<_Se, _OIter> + _NODISCARD friend iter_difference_t<_OIter> operator-( + const common_iterator& _Left, const common_iterator<_OIter, _OSe>& _Right) { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Left._Val._Contains != _Variantish_state::_Nothing && _Right._Val._Contains != _Variantish_state::_Nothing, + "Cannot take difference of valueless common_iterators"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + if (_Left._Val._Contains == _Variantish_state::_Holds_iter) { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + return _Left._Val._Iterator - _Right._Val._Iterator; + } else { + return _Left._Val._Iterator - _Right._Val._Sentinel; + } + } else { + if (_Right._Val._Contains == _Variantish_state::_Holds_iter) { + return _Left._Val._Sentinel - _Right._Val._Iterator; + } else { + return 0; + } + } + } + + _NODISCARD friend iter_rvalue_reference_t<_Iter> iter_move(const common_iterator& _Right) noexcept( + noexcept(_RANGES iter_move(_Right._Val._Iterator))) requires input_iterator<_Iter> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Right._Val._Contains == _Variantish_state::_Holds_iter, + "can only iter_move from common_iterator if it holds an iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_move(_Right._Val._Iterator); + } + + template _OIter, class _OSe> + friend void iter_swap(const common_iterator& _Left, const common_iterator<_OIter, _OSe>& _Right) noexcept( + noexcept(_RANGES iter_swap(_Left._Val._Iterator, _Right._Val._Iterator))) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Left._Val._Contains == _Variantish_state::_Holds_iter + && _Right._Val._Contains == _Variantish_state::_Holds_iter, + "can only iter_swap common_iterators if both hold iterators"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_swap(_Left._Val._Iterator, _Right._Val._Iterator); + } + +private: + // clang-format off + template _OSe> + requires (!same_as<_OIter, _OSe> && copyable<_OIter>) + friend class common_iterator; + // clang-format on + + _Variantish<_Iter, _Se> _Val; +}; + +template +struct incrementable_traits> { + using difference_type = iter_difference_t<_Iter>; +}; + +template +struct _Common_iterator_pointer_type { + using pointer = void; +}; + +template <_Has_member_arrow _Iter> +struct _Common_iterator_pointer_type<_Iter> { + using pointer = decltype(_STD declval<_Iter&>().operator->()); +}; + +template +struct iterator_traits> { + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using iterator_category = + conditional_t, forward_iterator_tag>, forward_iterator_tag, input_iterator_tag>; + using value_type = iter_value_t<_Iter>; + using difference_type = iter_difference_t<_Iter>; + using pointer = typename _Common_iterator_pointer_type<_Iter>::pointer; + using reference = iter_reference_t<_Iter>; +}; + // CLASS TEMPLATE counted_iterator template class counted_iterator { diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 0a375c4f177..c723ab4d262 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -223,13 +223,6 @@ void _Deallocate(void* _Ptr, size_t _Bytes) noexcept { #undef _HAS_ALIGNED_NEW -// FUNCTION TEMPLATE _Construct_in_place -template -void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { - ::new (const_cast(static_cast(_STD addressof(_Obj)))) - _Ty(_STD forward<_Types>(_Args)...); -} - // FUNCTION TEMPLATE _Global_new template _Ty* _Global_new(_Types&&... _Args) { // acts as "new" while disallowing user overload selection diff --git a/stl/inc/xutility b/stl/inc/xutility index f35a2ace52c..9658711f8ac 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -119,6 +119,13 @@ struct _Get_rebind_alias<_Ty, _Other, void_t; }; +// FUNCTION TEMPLATE _Construct_in_place +template +void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { + ::new (const_cast(static_cast(_STD addressof(_Obj)))) + _Ty(_STD forward<_Types>(_Args)...); +} + // STRUCT TEMPLATE pointer_traits template struct pointer_traits { @@ -2329,6 +2336,10 @@ _NODISCARD _Ty _Fake_decay_copy(_Ty) noexcept; // (2) is well-formed if and only if E is implicitly convertible to T and T is destructible, and // (3) is non-throwing if and only if both conversion from decltype((E)) to T and destruction of T are non-throwing. +// CONCEPT _Not_same_as +template +concept _Not_same_as = !same_as, remove_cvref_t<_Ty2>>; + namespace ranges { // VARIABLE TEMPLATE _Has_complete_elements template @@ -3426,10 +3437,6 @@ namespace ranges { using is_transparent = int; }; - // CONCEPT _Not_same_as - template - concept _Not_same_as = !same_as, remove_cvref_t<_Ty2>>; - // CONCEPT ranges::common_range // clang-format off template diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index ac70301e98f..901f64822b6 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -110,7 +110,16 @@ namespace test { using _Prevent_inheriting_unwrap = sentinel; - using unwrap = sentinel; + using unwrap = sentinel; + using Constinel = sentinel; + + constexpr operator Constinel() && noexcept { + return Constinel{exchange(ptr_, nullptr)}; + } + + constexpr operator Constinel() const& noexcept { + return Constinel{ptr_}; + } // clang-format off [[nodiscard]] constexpr auto _Unwrapped() const noexcept requires (to_bool(Wrapped)) { diff --git a/tests/std/test.lst b/tests/std/test.lst index da7a77478ed..2585db8b27f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -242,6 +242,8 @@ tests\P0768R1_spaceship_operator tests\P0769R2_shift_left_shift_right tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp +tests\P0896R4_common_iterator +tests\P0896R4_common_iterator_death tests\P0896R4_counted_iterator tests\P0896R4_counted_iterator_death tests\P0896R4_P1614R2_comparisons diff --git a/tests/std/tests/P0896R4_common_iterator/env.lst b/tests/std/tests/P0896R4_common_iterator/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_common_iterator/test.cpp b/tests/std/tests/P0896R4_common_iterator/test.cpp new file mode 100644 index 00000000000..317118c1144 --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator/test.cpp @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// clang-format off +template +concept CanDifference = requires(Iter it) { + { it - it }; +}; + +template +concept HasProxy = !is_reference_v>; +// clang-format on + +struct instantiator { + template + static constexpr void call() { + if constexpr (copyable) { + using ConstIter = typename Iter::Consterator; + using Sen = test::sentinel>; + using OSen = test::sentinel>; + using Cit = common_iterator; + using OCit = common_iterator; + P input[3] = {{0, 1}, {0, 2}, {0, 3}}; + + // [common.iter.types] + { + using iconcept = typename iterator_traits::iterator_concept; + if constexpr (forward_iterator) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as::iterator_concept, input_iterator_tag>); + } + + using icat = typename iterator_traits::iterator_category; + if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as); + } + + using ipointer = typename iterator_traits::pointer; + if constexpr (_Has_member_arrow) { + STATIC_ASSERT(same_as().operator->())>); + } else { + STATIC_ASSERT(same_as); + } + } + + { // [common.iter.const] + Cit defaultConstructed{}; + Cit iterConstructed{Iter{input}}; + Cit sentinelConstructed(Sen{}); + Cit copyConstructed{defaultConstructed}; + copyConstructed = iterConstructed; + + OCit conversionConstructed{defaultConstructed}; + conversionConstructed = iterConstructed; + + OCit conversionConstructedSentinel{sentinelConstructed}; + conversionConstructed = iterConstructed; + } + + { // [common.iter.access] + Cit iter{Iter{input}}; + assert(*iter == P(0, 1)); + assert(iter->first == 0); + assert(iter->second == 1); + if constexpr (HasProxy) { + // We return a proxy class here + static_assert(is_class_v())>); + } else { + // Either a pointer or the wrapped iterator + static_assert(!is_class_v())>); + } + + const Cit constIter{Iter{input}}; + assert(*constIter == P(0, 1)); + assert(constIter->first == 0); + assert(constIter->second == 1); + if constexpr (HasProxy) { + // We return a proxy class here + static_assert(is_class_v())>); + } else { + // Either a pointer or the wrapped iterator + static_assert(!is_class_v())>); + } + } + + { // [common.iter.nav] + Cit iter{Iter{input}}; + ++iter; + assert(*iter == P(0, 2)); + + assert(*iter++ == P(0, 2)); + assert(*iter == P(0, 3)); + } + + { // [common.iter.cmp] + // Compare iterator / iterator + assert(Cit{Iter{input}} == Cit{Iter{input}}); + assert(Cit{Iter{input}} != Cit{Iter{input + 1}}); + + // Compare iterator / sentinel + assert(Cit{Iter{input}} == Cit{Sen{input}}); + assert(Cit{Sen{input}} != Cit{Iter{input + 1}}); + + // Compare sentinel / sentinel + assert(Cit{Sen{input}} == Cit{Sen{input}}); + assert(Cit{Sen{input}} == Cit{Sen{input + 1}}); + + if constexpr (CanDifference) { + // Difference iterator / iterator + const same_as> auto diff_it_it = Cit{Iter{input}} - Cit{Iter{input + 1}}; + assert(diff_it_it == -1); + + // Difference iterator / sentinel + const same_as> auto diff_it_sen = Cit{Iter{input}} - Cit{Sen{input + 1}}; + const same_as> auto diff_sen_it = Cit{Sen{input + 1}} - Cit{Iter{input}}; + assert(diff_it_sen == -1); + assert(diff_sen_it == 1); + + // Difference sentinel / sentinel + const same_as> auto diff_sen_sen = Cit{Sen{input}} - Cit{Sen{input + 1}}; + assert(diff_sen_sen == 0); + + // Difference iterator / other iterator + const same_as> auto diff_it_oit = Cit{Iter{input}} - OCit{Iter{input + 1}}; + assert(diff_it_oit == -1); + + // Difference iterator / other sentinel + const same_as> auto diff_it_osen = Cit{Iter{input}} - OCit{OSen{input + 1}}; + assert(diff_it_osen == -1); + + // Difference other iterator / sentinel + const same_as> auto diff_sen_oit = Cit{Sen{input + 1}} - OCit{Iter{input}}; + assert(diff_sen_oit == 1); + + // Difference sentinel / other sentinel + const same_as> auto diff_sen_osen = Cit{Sen{input}} - OCit{OSen{input + 1}}; + assert(diff_sen_osen == 0); + } + } + + { // [common.iter.cust] + if constexpr (input_iterator) { // iter_move + Cit iter1{Iter{input}}; + + const same_as> auto element1 = ranges::iter_move(iter1); + assert(element1 == P(0, 1)); + } + + if constexpr (indirectly_swappable) { // iter_swap + Cit iter1{Iter{input}}; + Cit iter2{Iter{input + 1}}; + + ranges::iter_swap(iter1, iter2); + assert(*iter1 == P(0, 2)); + assert(*iter2 == P(0, 1)); + } + } + } + } +}; + +bool test_operator_arrow() { + P input[3] = {{0, 1}, {0, 2}, {0, 3}}; + + using pointerTest = common_iterator; + pointerTest pointerIter{input}; + + assert(*pointerIter == P(0, 1)); + assert(pointerIter->first == 0); + assert(pointerIter->second == 1); + static_assert(is_same_v()), P* const&>); + + using countedTest = common_iterator, default_sentinel_t>; + countedTest countedIter{counted_iterator{input, 3}}; + + assert(*countedIter == P(0, 1)); + assert(countedIter->first == 0); + assert(countedIter->second == 1); + static_assert(is_same_v()), P*>); + + return true; +} + +int main() { + with_writable_iterators::call(); + + test_operator_arrow(); +} diff --git a/tests/std/tests/P0896R4_common_iterator_death/env.lst b/tests/std/tests/P0896R4_common_iterator_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_common_iterator_death/test.cpp b/tests/std/tests/P0896R4_common_iterator_death/test.cpp new file mode 100644 index 00000000000..131b1fdf6cf --- /dev/null +++ b/tests/std/tests/P0896R4_common_iterator_death/test.cpp @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include + +#include +using namespace std; + +struct simple_input_iter { + using value_type = int; + using difference_type = int; + + value_type operator*() const { + return 0; + } + value_type operator->() const { + return 0; + } + simple_input_iter& operator++() { + return *this; + } + simple_input_iter operator++(int) { + return *this; + } + + bool operator==(const simple_input_iter&) const = default; + bool operator==(const default_sentinel_t&) const { + return true; + } + + difference_type operator-(const simple_input_iter&) const { + return 42; + } + friend difference_type operator-(const simple_input_iter&, const default_sentinel_t&) { + return 42; + } + friend difference_type operator-(const default_sentinel_t&, const simple_input_iter&) { + return 42; + } + + friend void iter_swap(const simple_input_iter&, const simple_input_iter&) {} +}; + +using CIT = common_iterator; + +void test_case_operator_dereference_sentinel() { + CIT cit{default_sentinel}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_dereference_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_dereference_const_sentinel() { + const CIT cit{default_sentinel}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_dereference_const_valueless() { + const CIT cit{_Common_iterator_construct_tag{}}; + (void) (*cit); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_arrow_sentinel() { + CIT cit{default_sentinel}; + (void) (cit.operator->()); // common_iterator can only be dereferenced if it holds an iterator +} +void test_case_operator_arrow_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + (void) (cit.operator->()); // common_iterator can only be dereferenced if it holds an iterator +} + +void test_case_operator_preincrement_sentinel() { + CIT cit{default_sentinel}; + ++cit; // common_iterator can only be preincremented if it holds an iterator +} + +void test_case_operator_preincrement_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + ++cit; // common_iterator can only be preincremented if it holds an iterator +} + +void test_case_operator_postincrement_sentinel() { + CIT cit{default_sentinel}; + cit++; // common_iterator can only be postincremented if it holds an iterator +} + +void test_case_operator_postincrement_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + cit++; // common_iterator can only be postincremented if it holds an iterator +} + +void test_case_equality_left_valueless() { + CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit2{}; + (void) (cit1 == cit2); // common_iterator can only be compared if it holds a value +} + +void test_case_equality_right_valueless() { + CIT cit1{}; + CIT cit2{_Common_iterator_construct_tag{}}; + (void) (cit1 == cit2); // common_iterator can only be compared if it holds a value +} + +void test_case_difference_left_valueless() { + CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit2{}; + (void) (cit1 - cit2); // common_iterator can only be subtracted if it holds a value +} + +void test_case_difference_right_valueless() { + CIT cit1{}; + CIT cit2{_Common_iterator_construct_tag{}}; + (void) (cit1 - cit2); // common_iterator can only be subtracted if it holds a value +} + +void test_case_iter_move_sentinel() { + CIT cit{default_sentinel}; + (void) ranges::iter_move(cit); // can only iter_move from common_iterator if it holds an iterator +} + +void test_case_iter_move_valueless() { + CIT cit{_Common_iterator_construct_tag{}}; + (void) ranges::iter_move(cit); // can only iter_move from common_iterator if it holds an iterator +} + +void test_case_iter_swap_sentinel_left_sentinel() { + CIT cit1{default_sentinel}; + CIT cit2{}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +void test_case_iter_swap_sentinel_left_valueless() { + CIT cit1{_Common_iterator_construct_tag{}}; + CIT cit2{}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +void test_case_iter_swap_sentinel_right_sentinel() { + CIT cit1{}; + CIT cit2{default_sentinel}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +void test_case_iter_swap_sentinel_right_valueless() { + CIT cit1{}; + CIT cit2{_Common_iterator_construct_tag{}}; + (void) ranges::iter_swap(cit1, cit2); // can only iter_swap common_iterators if both hold iterators +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec([] {}); + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_case_operator_dereference_sentinel, + test_case_operator_dereference_valueless, + test_case_operator_dereference_const_sentinel, + test_case_operator_dereference_const_valueless, + test_case_operator_arrow_sentinel, + test_case_operator_arrow_valueless, + test_case_operator_preincrement_sentinel, + test_case_operator_preincrement_valueless, + test_case_operator_postincrement_sentinel, + test_case_operator_postincrement_valueless, + test_case_equality_left_valueless, + test_case_equality_right_valueless, + test_case_difference_left_valueless, + test_case_difference_right_valueless, + test_case_iter_move_sentinel, + test_case_iter_move_valueless, + test_case_iter_swap_sentinel_left_sentinel, + test_case_iter_swap_sentinel_left_valueless, + test_case_iter_swap_sentinel_right_sentinel, + test_case_iter_swap_sentinel_right_valueless, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +}