diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -75,7 +75,7 @@ | `ranges::next `_ | `ranges::prev `_",[iterator.concepts],Christopher Di Bella and Arthur O'Dwyer,✅ `[predef.iterators] `_," -| Updates to reverse_iterator +| `Updates to reverse_iterator `_ | `Updates to back_insert_iterator `_ | `Updates to front_insert_iterator `_ | `Updates to move_iterator `_","| [iterator.concepts] diff --git a/libcxx/include/__iterator/iterator_traits.h b/libcxx/include/__iterator/iterator_traits.h --- a/libcxx/include/__iterator/iterator_traits.h +++ b/libcxx/include/__iterator/iterator_traits.h @@ -141,8 +141,8 @@ #if !defined(_LIBCPP_HAS_NO_CONCEPTS) -// The `cpp17-*-iterator` exposition-only concepts are easily confused with the Cpp17*Iterator tables, -// so they've been banished to a namespace that makes it obvious they have a niche use-case. +// The `cpp17-*-iterator` exposition-only concepts are easily confused with the Cpp17*Iterator tables, so the +// exposition-only concepts have been banished to a namespace that makes it obvious they have a niche use-case. namespace __iterator_traits_detail { template concept __cpp17_iterator = diff --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h --- a/libcxx/include/__iterator/reverse_iterator.h +++ b/libcxx/include/__iterator/reverse_iterator.h @@ -12,7 +12,11 @@ #include <__compare/compare_three_way_result.h> #include <__compare/three_way_comparable.h> +#include <__concepts/convertible_to.h> #include <__config> +#include <__iterator/concepts.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> #include <__iterator/iterator.h> #include <__iterator/iterator_traits.h> #include <__memory/addressof.h> @@ -41,22 +45,39 @@ _Iter __t; // no longer used as of LWG #2360, not removed due to ABI break #endif +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + static_assert(__is_cpp17_bidirectional_iterator<_Iter>::value || bidirectional_iterator<_Iter>, "The Iterator " + "template argument must be a bidirectional iterator."); +#endif + protected: _Iter current; public: - typedef _Iter iterator_type; - typedef typename iterator_traits<_Iter>::difference_type difference_type; - typedef typename iterator_traits<_Iter>::reference reference; - typedef typename iterator_traits<_Iter>::pointer pointer; - typedef _If<__is_cpp17_random_access_iterator<_Iter>::value, - random_access_iterator_tag, - typename iterator_traits<_Iter>::iterator_category> iterator_category; - typedef typename iterator_traits<_Iter>::value_type value_type; + using iterator_type = _Iter; #if _LIBCPP_STD_VER > 17 - typedef _If<__is_cpp17_random_access_iterator<_Iter>::value, - random_access_iterator_tag, - bidirectional_iterator_tag> iterator_concept; + using iterator_concept = _If<__is_cpp17_random_access_iterator<_Iter>::value, + random_access_iterator_tag, + bidirectional_iterator_tag>; +#endif + using iterator_category = _If<__is_cpp17_random_access_iterator<_Iter>::value, + random_access_iterator_tag, + typename iterator_traits<_Iter>::iterator_category>; +#if _LIBCPP_STD_VER > 17 + using value_type = iter_value_t<_Iter>; +#else + using value_type = typename iterator_traits<_Iter>::value_type; +#endif +#if _LIBCPP_STD_VER > 17 + using difference_type = iter_difference_t<_Iter>; +#else + using difference_type = typename iterator_traits<_Iter>::difference_type; +#endif + using pointer = typename iterator_traits<_Iter>::pointer; +#if _LIBCPP_STD_VER > 17 + using reference = iter_reference_t<_Iter>; +#else + using reference = typename iterator_traits<_Iter>::reference; #endif #ifndef _LIBCPP_ABI_NO_ITERATOR_BASES @@ -115,7 +136,14 @@ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 reference operator*() const {_Iter __tmp = current; return *--__tmp;} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 - pointer operator->() const {return _VSTD::addressof(operator*());} + pointer operator->() const +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + requires is_pointer_v<_Iter> + || requires(const _Iter i) { i.operator->(); } +#endif + { + return _VSTD::addressof(operator*()); + } _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 reverse_iterator& operator++() {--current; return *this;} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 @@ -124,22 +152,53 @@ reverse_iterator& operator--() {++current; return *this;} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 reverse_iterator operator--(int) {reverse_iterator __tmp(*this); ++current; return __tmp;} + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 - reverse_iterator operator+ (difference_type __n) const {return reverse_iterator(current - __n);} + reverse_iterator operator+ (difference_type __n) const { return reverse_iterator(current - __n); } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 - reverse_iterator& operator+=(difference_type __n) {current -= __n; return *this;} + reverse_iterator& operator+=(difference_type __n) { current -= __n; return *this; } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 - reverse_iterator operator- (difference_type __n) const {return reverse_iterator(current + __n);} + reverse_iterator operator- (difference_type __n) const { return reverse_iterator(current + __n); } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 - reverse_iterator& operator-=(difference_type __n) {current += __n; return *this;} + reverse_iterator& operator-=(difference_type __n) { current += __n; return *this; } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 - reference operator[](difference_type __n) const {return *(*this + __n);} + reference operator[](difference_type __n) const { return *(*this + __n); } + +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + _LIBCPP_HIDE_FROM_ABI friend constexpr + iter_rvalue_reference_t<_Iter> iter_move(const reverse_iterator& __i) + noexcept(is_nothrow_copy_constructible_v<_Iter> + && noexcept(ranges::iter_move(--declval<_Iter&>()))) { + auto __tmp = __i.base(); + return ranges::iter_move(--__tmp); + } + + template _Iter2> + _LIBCPP_HIDE_FROM_ABI friend constexpr + void iter_swap(const reverse_iterator& __x, const reverse_iterator<_Iter2>& __y) + noexcept(is_nothrow_copy_constructible_v<_Iter> + && is_nothrow_copy_constructible_v<_Iter2> + && noexcept(ranges::iter_swap(--declval<_Iter&>(), --declval<_Iter2&>()))) { + auto __xtmp = __x.base(); + auto __ytmp = __y.base(); + ranges::iter_swap(--__xtmp, --__ytmp); + } +#endif }; template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 bool operator==(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y) +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + requires requires { + { __x.base() == __y.base() } -> convertible_to; + } +#endif { return __x.base() == __y.base(); } @@ -148,6 +207,11 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 bool operator<(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y) +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + requires requires { + { __x.base() > __y.base() } -> convertible_to; + } +#endif { return __x.base() > __y.base(); } @@ -156,6 +220,11 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 bool operator!=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y) +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + requires requires { + { __x.base() != __y.base() } -> convertible_to; + } +#endif { return __x.base() != __y.base(); } @@ -164,6 +233,11 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 bool operator>(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y) +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + requires requires { + { __x.base() < __y.base() } -> convertible_to; + } +#endif { return __x.base() < __y.base(); } @@ -172,6 +246,11 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 bool operator>=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y) +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + requires requires { + { __x.base() <= __y.base() } -> convertible_to; + } +#endif { return __x.base() <= __y.base(); } @@ -180,6 +259,11 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 bool operator<=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y) +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + requires requires { + { __x.base() >= __y.base() } -> convertible_to; + } +#endif { return __x.base() >= __y.base(); } @@ -221,6 +305,12 @@ return reverse_iterator<_Iter>(__x.base() - __n); } +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) +template + requires (!sized_sentinel_for<_Iter1, _Iter2>) +inline constexpr bool disable_sized_sentinel_for, reverse_iterator<_Iter2>> = true; +#endif + #if _LIBCPP_STD_VER > 11 template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -216,10 +216,17 @@ protected: Iterator current; public: - typedef Iterator iterator_type; - typedef typename iterator_traits::difference_type difference_type; - typedef typename iterator_traits::reference reference; - typedef typename iterator_traits::pointer pointer; + using iterator_type = Iterator; + using iterator_concept = see below; // since C++20 + using iterator_category = typename iterator_traits::iterator_category; // since C++17, until C++20 + using iterator_category = see below; // since C++20 + using value_type = typename iterator_traits::value_typTESTED e; // since C++17, until C++20 + using value_type = iter_value_t; // since C++20 + using difference_type = typename iterator_traits::difference_type; // until C++20 + using difference_type = iter_difference_t; // since C++20 + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; // until C++20 + using reference = iter_reference_t; // sinceC++20 constexpr reverse_iterator(); constexpr explicit reverse_iterator(Iterator x); @@ -227,7 +234,8 @@ template constexpr reverse_iterator& operator=(const reverse_iterator& u); constexpr Iterator base() const; constexpr reference operator*() const; - constexpr pointer operator->() const; + constexpr pointer operator->() const; // until C++20 + constexpr pointer operator->() const requires see below; // since C++20 constexpr reverse_iterator& operator++(); constexpr reverse_iterator operator++(int); constexpr reverse_iterator& operator--(); @@ -236,7 +244,14 @@ constexpr reverse_iterator& operator+=(difference_type n); constexpr reverse_iterator operator- (difference_type n) const; constexpr reverse_iterator& operator-=(difference_type n); - constexpr reference operator[](difference_type n) const; + constexpr unspecified operator[](difference_type n) const; + + friend constexpr iter_rvalue_reference_t + iter_move(const reverse_iterator& i) noexcept(see below); + template Iterator2> + friend constexpr void + iter_swap(const reverse_iterator& x, + const reverse_iterator& y) noexcept(see below); }; template @@ -245,11 +260,11 @@ template constexpr bool // constexpr in C++17 -operator<(const reverse_iterator& x, const reverse_iterator& y); +operator!=(const reverse_iterator& x, const reverse_iterator& y); template constexpr bool // constexpr in C++17 -operator!=(const reverse_iterator& x, const reverse_iterator& y); +operator<(const reverse_iterator& x, const reverse_iterator& y); template constexpr bool // constexpr in C++17 @@ -257,11 +272,16 @@ template constexpr bool // constexpr in C++17 -operator>=(const reverse_iterator& x, const reverse_iterator& y); +operator<=(const reverse_iterator& x, const reverse_iterator& y); template constexpr bool // constexpr in C++17 -operator<=(const reverse_iterator& x, const reverse_iterator& y); +operator>=(const reverse_iterator& x, const reverse_iterator& y); + +template Iterator2> + constexpr compare_three_way_result_t + operator<=>(const reverse_iterator& x, + const reverse_iterator& y); template constexpr auto @@ -276,6 +296,11 @@ template constexpr reverse_iterator make_reverse_iterator(Iterator i); // C++14, constexpr in C++17 +template + requires (!sized_sentinel_for) + inline constexpr bool disable_sized_sentinel_for, + reverse_iterator> = true; + template class back_insert_iterator : public iterator // until C++17 diff --git a/libcxx/test/libcxx/iterators/predef.iterators/reverse.iterators/bad_template_argument.fail.cpp b/libcxx/test/libcxx/iterators/predef.iterators/reverse.iterators/bad_template_argument.fail.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/iterators/predef.iterators/reverse.iterators/bad_template_argument.fail.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// reverse_iterator + +#include + +#include "test_iterators.h" + +int main(int, char**) { + { + using BadIter = std::reverse_iterator>; + BadIter i; //expected-error-re@*:* {{static_assert failed{{.*}} "The Iterator template argument must be a bidirectional iterator."}} + } + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp @@ -19,8 +19,6 @@ constexpr bool common_reverse_iterator_checks() { static_assert(std::indirectly_writable); static_assert(std::sentinel_for); - static_assert(std::sentinel_for>); - static_assert(!std::sized_sentinel_for>); return true; } @@ -56,3 +54,12 @@ static_assert( std::indirectly_copyable); static_assert( std::indirectly_copyable_storable); static_assert( std::indirectly_swappable); + +using sized_iterator = random_access_iterator; +static_assert( std::sized_sentinel_for); +static_assert( std::sized_sentinel_for, std::reverse_iterator>); +// Check that `sized_sentinel_for` is false for `reverse_iterator`s if it is false for the underlying iterators. +using unsized_iterator = bidirectional_iterator; +static_assert(!std::sized_sentinel_for); +static_assert(!std::sized_sentinel_for, + std::reverse_iterator>); diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/greater.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/greater.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/greater.compile.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// reverse_iterator + +// template +// requires HasGreater +// bool operator>(const reverse_iterator& x, const reverse_iterator& y); // constexpr in C++17 + +#include +#include + +#include "test_macros.h" + +template +std::false_type SFINAEs_away_impl(int); + +template +std::true_type SFINAEs_away_impl(...); + +template +constexpr bool SFINAEs_away = decltype(SFINAEs_away_impl(0))::value; + diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/sfinae.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/sfinae.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/sfinae.compile.pass.cpp @@ -0,0 +1,185 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// +// +// reverse_iterator +// +// template +// constexpr bool // constexpr in C++17 +// operator==(const reverse_iterator& x, const reverse_iterator& y); +// +// template +// constexpr bool // constexpr in C++17 +// operator!=(const reverse_iterator& x, const reverse_iterator& y); +// +// template +// constexpr bool // constexpr in C++17 +// operator<(const reverse_iterator& x, const reverse_iterator& y); +// +// template +// constexpr bool // constexpr in C++17 +// operator>(const reverse_iterator& x, const reverse_iterator& y); +// +// template +// constexpr bool // constexpr in C++17 +// operator<=(const reverse_iterator& x, const reverse_iterator& y); +// +// template +// constexpr bool // constexpr in C++17 +// operator>=(const reverse_iterator& x, const reverse_iterator& y); + +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +struct IterBase { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = int; + using difference_type = ptrdiff_t; + using pointer = int*; + using reference = int&; + + reference operator*() const; + pointer operator->() const; +}; + +// operator == + +struct NoEqualityCompIter : IterBase { + bool operator!=(NoEqualityCompIter) const; + bool operator<(NoEqualityCompIter) const; + bool operator>(NoEqualityCompIter) const; + bool operator<=(NoEqualityCompIter) const; + bool operator>=(NoEqualityCompIter) const; +}; + +template () == std::declval())> +std::false_type equality_comp_SFINAEs_away_impl(int); + +template +std::true_type equality_comp_SFINAEs_away_impl(...); + +template +constexpr bool equality_comp_SFINAEs_away = decltype(equality_comp_SFINAEs_away_impl(0))::value; + +static_assert(!equality_comp_SFINAEs_away>); +static_assert( equality_comp_SFINAEs_away>); + +// operator != + +struct NoInequalityCompIter : IterBase { + bool operator<(NoInequalityCompIter) const; + bool operator>(NoInequalityCompIter) const; + bool operator<=(NoInequalityCompIter) const; + bool operator>=(NoInequalityCompIter) const; +}; + +template () != std::declval())> +std::false_type inequality_comp_SFINAEs_away_impl(int); + +template +std::true_type inequality_comp_SFINAEs_away_impl(...); + +template +constexpr bool inequality_comp_SFINAEs_away = decltype(inequality_comp_SFINAEs_away_impl(0))::value; + +static_assert(!inequality_comp_SFINAEs_away>); +static_assert( inequality_comp_SFINAEs_away>); + +// operator < + +struct NoGreaterCompIter : IterBase { + bool operator==(NoGreaterCompIter) const; + bool operator!=(NoGreaterCompIter) const; + bool operator<(NoGreaterCompIter) const; + bool operator<=(NoGreaterCompIter) const; + bool operator>=(NoGreaterCompIter) const; +}; + +template () < std::declval())> +std::false_type less_comp_SFINAEs_away_impl(int); + +template +std::true_type less_comp_SFINAEs_away_impl(...); + +template +constexpr bool less_comp_SFINAEs_away = decltype(less_comp_SFINAEs_away_impl(0))::value; + +static_assert(!less_comp_SFINAEs_away>); +static_assert( less_comp_SFINAEs_away>); + +// operator > + +struct NoLessCompIter : IterBase { + bool operator==(NoLessCompIter) const; + bool operator!=(NoLessCompIter) const; + bool operator>(NoLessCompIter) const; + bool operator<=(NoLessCompIter) const; + bool operator>=(NoLessCompIter) const; +}; + +template () > std::declval())> +std::false_type greater_comp_SFINAEs_away_impl(int); + +template +std::true_type greater_comp_SFINAEs_away_impl(...); + +template +constexpr bool greater_comp_SFINAEs_away = decltype(greater_comp_SFINAEs_away_impl(0))::value; + +static_assert(!greater_comp_SFINAEs_away>); +static_assert( greater_comp_SFINAEs_away>); + +// operator <= + +struct NoGreaterOrEqualCompIter : IterBase { + bool operator==(NoGreaterOrEqualCompIter) const; + bool operator!=(NoGreaterOrEqualCompIter) const; + bool operator<(NoGreaterOrEqualCompIter) const; + bool operator>(NoGreaterOrEqualCompIter) const; + bool operator<=(NoGreaterOrEqualCompIter) const; +}; + +template () <= std::declval())> +std::false_type less_or_equal_comp_SFINAEs_away_impl(int); + +template +std::true_type less_or_equal_comp_SFINAEs_away_impl(...); + +template +constexpr bool less_or_equal_comp_SFINAEs_away = decltype(less_or_equal_comp_SFINAEs_away_impl(0))::value; + +static_assert(!less_or_equal_comp_SFINAEs_away>); +static_assert( less_or_equal_comp_SFINAEs_away>); + +// operator >= + +struct NoLessOrEqualCompIter : IterBase { + bool operator==(NoLessOrEqualCompIter) const; + bool operator!=(NoLessOrEqualCompIter) const; + bool operator>(NoLessOrEqualCompIter) const; + bool operator>=(NoLessOrEqualCompIter) const; +}; + +template () >= std::declval())> +std::false_type greater_or_equal_comp_SFINAEs_away_impl(int); + +template +std::true_type greater_or_equal_comp_SFINAEs_away_impl(...); + +template +constexpr bool greater_or_equal_comp_SFINAEs_away = decltype(greater_or_equal_comp_SFINAEs_away_impl(0))::value; + +static_assert(!greater_or_equal_comp_SFINAEs_away>); +static_assert( greater_or_equal_comp_SFINAEs_away>); diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp @@ -22,6 +22,7 @@ #include #include +#include "test_iterators.h" #include "test_macros.h" class A @@ -74,45 +75,81 @@ TEST_CONSTEXPR C gC; -int main(int, char**) -{ - A a; - test(&a+1, A()); +template )> +std::false_type bad_arrow_SFINAEs_away_impl(int); - { +template +std::true_type bad_arrow_SFINAEs_away_impl(...); + +template +constexpr bool bad_arrow_SFINAEs_away = decltype(bad_arrow_SFINAEs_away_impl(0))::value; + +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) +using PtrRI = std::reverse_iterator; +static_assert(!bad_arrow_SFINAEs_away); + +using PtrLikeRI = std::reverse_iterator>; +static_assert(!bad_arrow_SFINAEs_away); + +// Is a bidirectional iterator but doesn't define `operator->`. +struct NonPtr { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = int; + using difference_type = int; + using pointer = int*; + using reference = int&; + + int& operator*() const { static int x; return x; } + NonPtr& operator++(); + NonPtr operator++(int); + NonPtr& operator--(); + NonPtr operator--(int); + bool operator==(const NonPtr&) const { return false; } +}; +static_assert( std::bidirectional_iterator); + +using NonPtrRI = std::reverse_iterator; +static_assert( bad_arrow_SFINAEs_away); +#endif + +int main(int, char**) { + A a; + test(&a+1, A()); + + { std::list l; l.push_back(B(0)); l.push_back(B(1)); l.push_back(B(2)); { - std::list::const_iterator i = l.begin(); - assert ( i->get() == 0 ); ++i; - assert ( i->get() == 1 ); ++i; - assert ( i->get() == 2 ); ++i; - assert ( i == l.end ()); + std::list::const_iterator i = l.begin(); + assert ( i->get() == 0 ); ++i; + assert ( i->get() == 1 ); ++i; + assert ( i->get() == 2 ); ++i; + assert ( i == l.end ()); } { - std::list::const_reverse_iterator ri = l.rbegin(); - assert ( ri->get() == 2 ); ++ri; - assert ( ri->get() == 1 ); ++ri; - assert ( ri->get() == 0 ); ++ri; - assert ( ri == l.rend ()); - } + std::list::const_reverse_iterator ri = l.rbegin(); + assert ( ri->get() == 2 ); ++ri; + assert ( ri->get() == 1 ); ++ri; + assert ( ri->get() == 0 ); ++ri; + assert ( ri == l.rend ()); } + } #if TEST_STD_VER > 14 - { - typedef std::reverse_iterator RI; - constexpr RI it1 = std::make_reverse_iterator(&gC+1); + { + typedef std::reverse_iterator RI; + constexpr RI it1 = std::make_reverse_iterator(&gC+1); - static_assert(it1->get() == gC.get(), ""); - } + static_assert(it1->get() == gC.get(), ""); + } #endif - { - ((void)gC); - } + { + ((void)gC); + } return 0; } diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_move.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_move.pass.cpp @@ -0,0 +1,163 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// +// +// reverse_iterator +// +// friend constexpr iter_rvalue_reference_t +// iter_move(const reverse_iterator& i) noexcept(see below); + +#include + +#include +#include +#include +#include "test_macros.h" + +template +struct BidirectionalIteratorTraits { + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = const T&; + using iterator_category = std::bidirectional_iterator_tag; +}; + +namespace adl { + +static int iter_move_invocations = 0; + +template +struct Iterator : BidirectionalIteratorTraits { + T* ptr = nullptr; + + Iterator() = default; + explicit Iterator(int* p) : ptr(p) {} + + T& operator*() const { return *ptr; } + + Iterator& operator++() { ++ptr; return *this; } + Iterator operator++(int) { + Iterator prev = *this; + ++ptr; + return prev; + } + + Iterator& operator--() { --ptr; return *this; } + Iterator operator--(int) { + Iterator prev = *this; + --ptr; + return prev; + } + + friend T&& iter_move(Iterator iter) { + ++iter_move_invocations; + return std::move(*iter); + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr == rhs.ptr; } +}; + +} // namespace adl + +int main(int, char**) { + // Can use `iter_move` with a regular array. + { + constexpr int N = 3; + int a[N] = {0, 1, 2}; + + std::reverse_iterator ri(a + N); + static_assert(std::same_as); + assert(iter_move(ri) == 2); + + ++ri; + assert(iter_move(ri) == 1); + } + + // Ensure the `iter_move` customization point is being used. + { + constexpr int N = 3; + int a[N] = {0, 1, 2}; + + adl::Iterator i(a + N); + std::reverse_iterator> ri(i); + int x = iter_move(ri); + assert(x == 2); + assert(adl::iter_move_invocations == 1); + + ++ri; + x = iter_move(ri); + assert(x == 1); + assert(adl::iter_move_invocations == 2); + } + + // Check the `noexcept` specification. + { + { + struct ThrowingCopyNoexceptMove : BidirectionalIteratorTraits { + ThrowingCopyNoexceptMove(); + ThrowingCopyNoexceptMove(const ThrowingCopyNoexceptMove&); + + int& operator*() const noexcept { static int x; return x; } + + ThrowingCopyNoexceptMove& operator++(); + ThrowingCopyNoexceptMove operator++(int); + ThrowingCopyNoexceptMove& operator--() noexcept; + ThrowingCopyNoexceptMove operator--(int) noexcept; + }; + + static_assert(!std::is_nothrow_copy_constructible_v); + ASSERT_NOEXCEPT(std::ranges::iter_move(--std::declval())); + using RI = std::reverse_iterator; + ASSERT_NOT_NOEXCEPT(iter_move(std::declval())); + } + + { + struct NoexceptCopyThrowingMove : BidirectionalIteratorTraits { + NoexceptCopyThrowingMove(); + NoexceptCopyThrowingMove(const NoexceptCopyThrowingMove&) noexcept; + + int& operator*() const { static int x; return x; } + + NoexceptCopyThrowingMove& operator++(); + NoexceptCopyThrowingMove operator++(int); + NoexceptCopyThrowingMove& operator--(); + NoexceptCopyThrowingMove operator--(int); + }; + + static_assert( std::is_nothrow_copy_constructible_v); + ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(--std::declval())); + using RI = std::reverse_iterator; + ASSERT_NOT_NOEXCEPT(iter_move(std::declval())); + } + + { + struct NoexceptCopyAndMove : BidirectionalIteratorTraits { + NoexceptCopyAndMove(); + NoexceptCopyAndMove(const NoexceptCopyAndMove&) noexcept; + + int& operator*() const noexcept { static int x; return x; } + + NoexceptCopyAndMove& operator++(); + NoexceptCopyAndMove operator++(int); + NoexceptCopyAndMove& operator--() noexcept; + NoexceptCopyAndMove operator--(int) noexcept; + }; + + static_assert( std::is_nothrow_copy_constructible_v); + ASSERT_NOEXCEPT(std::ranges::iter_move(--std::declval())); + using RI = std::reverse_iterator; + ASSERT_NOEXCEPT(iter_move(std::declval())); + } + } + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_swap.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_swap.pass.cpp @@ -0,0 +1,170 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// +// +// reverse_iterator +// +// template Iterator2> +// friend constexpr void +// iter_swap(const reverse_iterator& x, +// const reverse_iterator& y) noexcept(see below); + +#include + +#include +#include +#include +#include "test_macros.h" + +template +struct BidirectionalIteratorTraits { + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = const T&; + using iterator_category = std::bidirectional_iterator_tag; +}; + +namespace adl { + +static int iter_swap_invocations = 0; + +template +struct Iterator : BidirectionalIteratorTraits { + T* ptr = nullptr; + + Iterator() = default; + explicit Iterator(int* p) : ptr(p) {} + + T& operator*() const { return *ptr; } + + Iterator& operator++() { ++ptr; return *this; } + Iterator operator++(int) { + Iterator prev = *this; + ++ptr; + return prev; + } + + Iterator& operator--() { --ptr; return *this; } + Iterator operator--(int) { + Iterator prev = *this; + --ptr; + return prev; + } + + friend void iter_swap(Iterator, Iterator) { + ++iter_swap_invocations; + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr == rhs.ptr; } +}; + +} // namespace adl + +int main(int, char**) { + // Can use `iter_swap` with a regular array. + { + constexpr int N = 3; + int a[N] = {0, 1, 2}; + + std::reverse_iterator rb(a + N); + std::reverse_iterator re(a + 1); + assert(a[0] == 0); + assert(a[2] == 2); + + static_assert(std::same_as); + iter_swap(rb, re); + assert(a[0] == 2); + assert(a[2] == 0); + } + + // Ensure the `iter_swap` customization point is being used. + { + adl::Iterator i1, i2; + std::reverse_iterator> ri1(i1), ri2(i2); + iter_swap(i1, i2); + assert(adl::iter_swap_invocations == 1); + + iter_swap(i2, i1); + assert(adl::iter_swap_invocations == 2); + } + + // Check the `noexcept` specification. + { + { + struct ThrowingCopyNoexceptSwap : BidirectionalIteratorTraits { + ThrowingCopyNoexceptSwap(); + ThrowingCopyNoexceptSwap(const ThrowingCopyNoexceptSwap&); + + int& operator*() const noexcept { static int x; return x; } + + ThrowingCopyNoexceptSwap& operator++(); + ThrowingCopyNoexceptSwap operator++(int); + ThrowingCopyNoexceptSwap& operator--() noexcept; + ThrowingCopyNoexceptSwap operator--(int) noexcept; + }; + + static_assert(!std::is_nothrow_copy_constructible_v); + static_assert( std::is_nothrow_copy_constructible_v); + ASSERT_NOEXCEPT(std::ranges::iter_swap(--std::declval(), --std::declval())); + using RI1 = std::reverse_iterator; + using RI2 = std::reverse_iterator; + ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + + { + struct NoexceptCopyThrowingSwap : BidirectionalIteratorTraits { + NoexceptCopyThrowingSwap(); + NoexceptCopyThrowingSwap(const NoexceptCopyThrowingSwap&) noexcept; + + int& operator*() const { static int x; return x; } + + NoexceptCopyThrowingSwap& operator++(); + NoexceptCopyThrowingSwap operator++(int); + NoexceptCopyThrowingSwap& operator--(); + NoexceptCopyThrowingSwap operator--(int); + }; + + static_assert( std::is_nothrow_copy_constructible_v); + static_assert( std::is_nothrow_copy_constructible_v); + ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap(--std::declval(), --std::declval())); + using RI1 = std::reverse_iterator; + using RI2 = std::reverse_iterator; + ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + + { + struct NoexceptCopyAndSwap : BidirectionalIteratorTraits { + NoexceptCopyAndSwap(); + NoexceptCopyAndSwap(const NoexceptCopyAndSwap&) noexcept; + + int& operator*() const noexcept { static int x; return x; } + + NoexceptCopyAndSwap& operator++(); + NoexceptCopyAndSwap operator++(int); + NoexceptCopyAndSwap& operator--() noexcept; + NoexceptCopyAndSwap operator--(int) noexcept; + }; + + static_assert( std::is_nothrow_copy_constructible_v); + static_assert( std::is_nothrow_copy_constructible_v); + ASSERT_NOEXCEPT(std::ranges::iter_swap(--std::declval(), --std::declval())); + using RI1 = std::reverse_iterator; + using RI2 = std::reverse_iterator; + ASSERT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + ASSERT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + } + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp @@ -38,46 +38,83 @@ }; template -void -test() -{ - typedef std::reverse_iterator R; - typedef std::iterator_traits T; - find_current q; q.test(); // Just test that we can access `.current` from derived classes - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::pointer>::value), ""); +void test() { + typedef std::reverse_iterator R; + typedef std::iterator_traits T; + find_current q; q.test(); // Just test that we can access `.current` from derived classes + static_assert((std::is_same::value), ""); + static_assert((std::is_same::value), ""); + static_assert((std::is_same::value), ""); + static_assert((std::is_same::value), ""); + static_assert((std::is_same::pointer>::value), ""); #if TEST_STD_VER <= 14 - typedef std::iterator iterator_base; - static_assert((std::is_base_of::value), ""); + typedef std::iterator iterator_base; + static_assert((std::is_base_of::value), ""); #endif #if TEST_STD_VER > 17 - if constexpr (std::is_same_v) { - static_assert((std::is_same::value), ""); - } else { - static_assert((std::is_same::value), ""); - } -#else + if constexpr (std::is_same_v) { + static_assert((std::is_same::value), ""); + } else { static_assert((std::is_same::value), ""); + } +#else + static_assert((std::is_same::value), ""); #endif } -int main(int, char**) -{ - test >(); - test >(); - test(); +struct Foo {}; +using FooIter = bidirectional_iterator; #if TEST_STD_VER > 17 - test>(); - static_assert(std::is_same_v>::iterator_concept, std::bidirectional_iterator_tag>); - static_assert(std::is_same_v>::iterator_concept, std::random_access_iterator_tag>); - static_assert(std::is_same_v>::iterator_concept, std::random_access_iterator_tag>); - static_assert(std::is_same_v::iterator_concept, std::random_access_iterator_tag>); +template <> +struct std::indirectly_readable_traits { + using value_type = int; +}; + +template <> +struct std::incrementable_traits { + using difference_type = char; +}; +#endif + +struct BarIter { + bool& operator*() const; +}; + +namespace std { +template <> +struct iterator_traits { + using difference_type = char; + using value_type = char; + using pointer = char*; + using reference = char&; + using iterator_category = std::bidirectional_iterator_tag; +}; +} // namespace std + +int main(int, char**) { + test >(); + test >(); + test(); + +#if TEST_STD_VER > 17 + test>(); + static_assert(std::is_same_v>::iterator_concept, std::bidirectional_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, std::random_access_iterator_tag>); +#endif + +#if TEST_STD_VER > 17 + static_assert(std::is_same_v::value_type, int>); + static_assert(std::is_same_v::difference_type, char>); + static_assert(std::is_same_v::reference, bool&>); +#else + static_assert(std::is_same_v::value_type, Foo>); + static_assert(std::is_same_v::difference_type, ptrdiff_t>); + static_assert(std::is_same_v::reference, char&>); #endif - return 0; + return 0; }