diff --git a/libcxx/include/__iterator/move_iterator.h b/libcxx/include/__iterator/move_iterator.h --- a/libcxx/include/__iterator/move_iterator.h +++ b/libcxx/include/__iterator/move_iterator.h @@ -87,10 +87,13 @@ >::type reference; #endif // _LIBCPP_STD_VER > 17 -#if _LIBCPP_STD_VER > 17 - _LIBCPP_HIDE_FROM_ABI constexpr + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 explicit move_iterator(_Iter __i) : __current_(std::move(__i)) {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 + move_iterator& operator++() { ++__current_; return *this; } + +#if _LIBCPP_STD_VER > 17 _LIBCPP_HIDE_FROM_ABI constexpr move_iterator() requires is_constructible_v<_Iter> : __current_() {} @@ -117,9 +120,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](difference_type __n) const { return ranges::iter_move(__current_ + __n); } - _LIBCPP_HIDE_FROM_ABI constexpr - move_iterator& operator++() { ++__current_; return *this; } - _LIBCPP_HIDE_FROM_ABI constexpr auto operator++(int) requires forward_iterator<_Iter> @@ -130,9 +130,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++__current_; } #else - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 - explicit move_iterator(_Iter __i) : __current_(std::move(__i)) {} - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 move_iterator() : __current_() {} @@ -163,8 +160,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 reference operator[](difference_type __n) const { return static_cast(__current_[__n]); } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 - move_iterator& operator++() { ++__current_; return *this; } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 move_iterator operator++(int) { move_iterator __tmp(*this); ++__current_; return __tmp; } #endif // _LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__iterator/move_sentinel.h b/libcxx/include/__iterator/move_sentinel.h --- a/libcxx/include/__iterator/move_sentinel.h +++ b/libcxx/include/__iterator/move_sentinel.h @@ -31,7 +31,7 @@ move_sentinel() = default; _LIBCPP_HIDE_FROM_ABI constexpr - explicit move_sentinel(_Sent __s) : __last_(_VSTD::move(__s)) {} + explicit move_sentinel(_Sent __s) : __last_(std::move(__s)) {} template requires convertible_to diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -386,12 +386,13 @@ template class move_iterator { public: - typedef Iterator iterator_type; - typedef typename iterator_traits::difference_type difference_type; - typedef Iterator pointer; - typedef typename iterator_traits::value_type value_type; - typedef typename iterator_traits::iterator_category iterator_category; - typedef value_type&& reference; + using iterator_type = Iterator; + using iterator_concept = input_iterator_tag; // From C++20 + using iterator_category = see below; // not always present starting from C++20 + using value_type = iter_value_t; // Until C++20, iterator_traits::value_type + using difference_type = iter_difference_t; // Until C++20, iterator_traits::difference_type; + using pointer = Iterator; + using reference = iter_rvalue_reference_t; // Until C++20, value_type&& constexpr move_iterator(); // all the constexprs are in C++17 constexpr explicit move_iterator(Iterator i); @@ -399,18 +400,40 @@ constexpr move_iterator(const move_iterator& u); template constexpr move_iterator& operator=(const move_iterator& u); - constexpr iterator_type base() const; + + constexpr iterator_type base() const; // Until C++20 + constexpr const Iterator& base() const & noexcept; // From C++20 + constexpr Iterator base() &&; // From C++20 + constexpr reference operator*() const; - constexpr pointer operator->() const; + constexpr pointer operator->() const; // Removed in C++20 constexpr move_iterator& operator++(); - constexpr move_iterator operator++(int); + constexpr auto operator++(int); // Return type was move_iterator until C++20 constexpr move_iterator& operator--(); constexpr move_iterator operator--(int); constexpr move_iterator operator+(difference_type n) const; constexpr move_iterator& operator+=(difference_type n); constexpr move_iterator operator-(difference_type n) const; constexpr move_iterator& operator-=(difference_type n); - constexpr unspecified operator[](difference_type n) const; + constexpr reference operator[](difference_type n) const; // Return type unspecified until C++20 + + template S> + friend constexpr bool + operator==(const move_iterator& x, const move_sentinel& y); // Since C++20 + template S> + friend constexpr iter_difference_t + operator-(const move_sentinel& x, const move_iterator& y); // Since C++20 + template S> + friend constexpr iter_difference_t + operator-(const move_iterator& x, const move_sentinel& y); // Since C++20 + friend constexpr iter_rvalue_reference_t + iter_move(const move_iterator& i) + noexcept(noexcept(ranges::iter_move(i.current))); // Since C++20 + template Iterator2> + friend constexpr void + iter_swap(const move_iterator& x, const move_iterator& y) + noexcept(noexcept(ranges::iter_swap(x.current, y.current))); // Since C++20 + private: Iterator current; // exposition only }; @@ -452,6 +475,23 @@ template // constexpr in C++17 constexpr move_iterator make_move_iterator(const Iterator& i); +template +class move_sentinel { +public: + constexpr move_sentinel(); + constexpr explicit move_sentinel(S s); + template + requires convertible_to + constexpr move_sentinel(const move_sentinel& s); + template + requires assignable_from + constexpr move_sentinel& operator=(const move_sentinel& s); + + constexpr S base() const; +private: + S last; // exposition only +}; + // [default.sentinel], default sentinel struct default_sentinel_t; inline constexpr default_sentinel_t default_sentinel{}; diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.nonmember/iter_move.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.nonmember/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.nonmember/iter_move.pass.cpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// friend constexpr iter_rvalue_reference_t +// iter_move(const move_iterator& i) +// noexcept(noexcept(ranges::iter_move(i.current))); // Since C++20 + +#include + +#include +#include +#include +#include "test_iterators.h" +#include "test_macros.h" + +int global; + +template +struct MaybeNoexceptMove { + int x; + using value_type = int; + using difference_type = ptrdiff_t; + + constexpr friend value_type&& iter_move(MaybeNoexceptMove) noexcept(IsNoexcept) { + return std::move(global); + } + + int& operator*() const { static int a; return a; } + + MaybeNoexceptMove& operator++(); + MaybeNoexceptMove operator++(int); +}; +using ThrowingBase = MaybeNoexceptMove; +using NoexceptBase = MaybeNoexceptMove; +static_assert(std::input_iterator); +ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval())); +ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval())); + +constexpr bool test() { + // Can use `iter_move` with a regular array. + { + int a[] = {0, 1, 2}; + + std::move_iterator i(a); + static_assert(std::same_as); + assert(iter_move(i) == 0); + + ++i; + assert(iter_move(i) == 1); + } + + // Check that the `iter_move` customization point is being used. + { + int a[] = {0, 1, 2}; + + int iter_move_invocations = 0; + adl::Iterator base = adl::Iterator::TrackMoves(a, iter_move_invocations); + std::move_iterator i(base); + int x = iter_move(i); + assert(x == 0); + assert(iter_move_invocations == 1); + } + + // Check the `noexcept` specification. + { + using ThrowingIter = std::move_iterator; + ASSERT_NOT_NOEXCEPT(iter_move(std::declval())); + using NoexceptIter = std::move_iterator; + ASSERT_NOEXCEPT(iter_move(std::declval())); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.nonmember/iter_swap.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.nonmember/iter_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.nonmember/iter_swap.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// template Iterator2> +// friend constexpr void +// iter_swap(const move_iterator& x, const move_iterator& y) +// noexcept(noexcept(ranges::iter_swap(x.current, y.current))); // Since C++20 + +#include + +#include +#include +#include +#include "test_iterators.h" +#include "test_macros.h" + +template +struct MaybeNoexceptSwap { + using value_type = int; + using difference_type = ptrdiff_t; + + constexpr friend void iter_swap(MaybeNoexceptSwap, MaybeNoexceptSwap) noexcept(IsNoexcept) { + } + + int& operator*() const { static int x; return x; } + + MaybeNoexceptSwap& operator++(); + MaybeNoexceptSwap operator++(int); +}; +using ThrowingBase = MaybeNoexceptSwap; +using NoexceptBase = MaybeNoexceptSwap; +static_assert(std::input_iterator); +ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap(std::declval(), std::declval())); +ASSERT_NOEXCEPT(std::ranges::iter_swap(std::declval(), std::declval())); + +constexpr bool test() { + // Can use `iter_swap` with a regular array. + { + int a[] = {0, 1, 2}; + + std::move_iterator b(a); + std::move_iterator e(a + 2); + assert(a[0] == 0); + assert(a[2] == 2); + + static_assert(std::same_as); + iter_swap(b, e); + assert(a[0] == 2); + assert(a[2] == 0); + } + + // Check that the `iter_swap` customization point is being used. + { + int iter_swap_invocations = 0; + adl::Iterator base1 = adl::Iterator::TrackSwaps(iter_swap_invocations); + adl::Iterator base2 = adl::Iterator::TrackSwaps(iter_swap_invocations); + std::move_iterator i1(base1), i2(base2); + iter_swap(i1, i2); + assert(iter_swap_invocations == 1); + + iter_swap(i2, i1); + assert(iter_swap_invocations == 2); + } + + // Check the `noexcept` specification. + { + using ThrowingIter = std::move_iterator; + ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + using NoexceptIter = std::move_iterator; + ASSERT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.-/sentinel.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.-/sentinel.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.-/sentinel.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// move_iterator + +// template S> +// friend constexpr iter_difference_t +// operator-(const move_sentinel& x, const move_iterator& y); // Since C++20 +// template S> +// friend constexpr iter_difference_t +// operator-(const move_iterator& x, const move_sentinel& y); // Since C++20 + +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +// The `operator-` calls the underlying iterator and sentinel's `operator-`. + +struct CustomIt { + using value_type = int; + using difference_type = int; + using reference = int&; + using pointer = int*; + using iterator_category = std::input_iterator_tag; + + CustomIt() = default; + TEST_CONSTEXPR_CXX17 explicit CustomIt(int* p) : p_(p) {} + int& operator*() const; + CustomIt& operator++(); + CustomIt operator++(int); + constexpr friend difference_type operator-(const CustomIt& a, const CustomIt& b) { return a.p_ - b.p_; } + int *p_ = nullptr; +}; + +template > +constexpr void test_one() { + int arr[] = {3, 1, 4}; + + const std::move_iterator it_a{It(arr)}; + const std::move_iterator it_b{It(arr + 1)}; + + const std::move_sentinel sent_a{Sent(It(arr))}; + const std::move_sentinel sent_b{Sent(It(arr + 1))}; + const std::move_sentinel sent_c{Sent(It(arr + 2))}; + + ASSERT_SAME_TYPE(decltype(it_a - sent_a), std::iter_difference_t); + ASSERT_SAME_TYPE(decltype(sent_a - it_a), std::iter_difference_t); + + // it_a + assert(it_a - sent_a == 0); + assert(sent_a - it_a == 0); + + assert(it_a - sent_b == -1); + assert(sent_b - it_a == 1); + + assert(it_a - sent_c == -2); + assert(sent_c - it_a == 2); + + // it_b + assert(it_b - sent_a == 1); + assert(sent_a - it_b == -1); + + assert(it_b - sent_b == 0); + assert(sent_b - it_b == 0); + + assert(it_b - sent_c == -1); + assert(sent_c - it_b == 1); +} + +constexpr bool test() { + test_one(); + test_one>(); + test_one>(); + test_one>(); + test_one>(); + test_one(); + test_one(); + test_one>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/default.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/default.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/default.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/default.pass.cpp @@ -13,33 +13,42 @@ // move_iterator(); // // constexpr in C++17 +// +// requires the underlying iterator to be default-constructible (extension). #include +#include #include "test_macros.h" #include "test_iterators.h" +#if TEST_STD_VER > 17 +struct NoDefaultCtr : forward_iterator { + NoDefaultCtr() = delete; +}; + +LIBCPP_STATIC_ASSERT( std::is_default_constructible_v>>); +LIBCPP_STATIC_ASSERT(!std::is_default_constructible_v>); +#endif + template -void -test() -{ +void test() { std::move_iterator r; (void)r; } -int main(int, char**) -{ - // we don't have a test iterator that is both input and default-constructible, so not testing that case - test >(); - test >(); - test >(); - test(); +int main(int, char**) { + // we don't have a test iterator that is both input and default-constructible, so not testing that case + test >(); + test >(); + test >(); + test(); #if TEST_STD_VER > 14 - { + { constexpr std::move_iterator it; (void)it; - } + } #endif return 0; diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.conv/base.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.conv/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.conv/base.pass.cpp @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// +// +// constexpr iterator_type base() const; // Until C++20 +// constexpr const Iterator& base() const & noexcept; // From C++20 +// constexpr Iterator base() &&; // From C++20 + +#include + +#include +#include "test_iterators.h" +#include "test_macros.h" + +struct MoveOnlyIterator { + using It = int*; + + It it_; + + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = std::ptrdiff_t; + using reference = int&; + + TEST_CONSTEXPR explicit MoveOnlyIterator(It it) : it_(it) {} + MoveOnlyIterator(MoveOnlyIterator&&) = default; + MoveOnlyIterator& operator=(MoveOnlyIterator&&) = default; + MoveOnlyIterator(const MoveOnlyIterator&) = delete; + MoveOnlyIterator& operator=(const MoveOnlyIterator&) = delete; + + TEST_CONSTEXPR reference operator*() const { return *it_; } + + TEST_CONSTEXPR_CXX14 MoveOnlyIterator& operator++() { ++it_; return *this; } + TEST_CONSTEXPR_CXX14 MoveOnlyIterator operator++(int) { return MoveOnlyIterator(it_++); } + + friend TEST_CONSTEXPR bool operator==(const MoveOnlyIterator& x, const MoveOnlyIterator& y) {return x.it_ == y.it_;} + friend TEST_CONSTEXPR bool operator!=(const MoveOnlyIterator& x, const MoveOnlyIterator& y) {return x.it_ != y.it_;} + + friend TEST_CONSTEXPR It base(const MoveOnlyIterator& i) { return i.it_; } +}; + +#if TEST_STD_VER > 17 +static_assert( std::input_iterator); +#endif +static_assert(!std::is_copy_constructible::value, ""); + +template +TEST_CONSTEXPR_CXX14 void test_one() { + // Non-const lvalue. + { + int a[] = {1, 2, 3}; + + auto i = std::move_iterator(It(a)); +#if TEST_STD_VER > 17 + ASSERT_SAME_TYPE(decltype(i.base()), const It&); + ASSERT_NOEXCEPT(i.base()); +#else + ASSERT_SAME_TYPE(decltype(i.base()), It); +#endif + assert(i.base() == It(a)); + + ++i; + assert(i.base() == It(a + 1)); + } + + // Const lvalue. + { + int a[] = {1, 2, 3}; + + const auto i = std::move_iterator(It(a)); +#if TEST_STD_VER > 17 + ASSERT_SAME_TYPE(decltype(i.base()), const It&); + ASSERT_NOEXCEPT(i.base()); +#else + ASSERT_SAME_TYPE(decltype(i.base()), It); +#endif + assert(i.base() == It(a)); + } + + // Rvalue. + { + int a[] = {1, 2, 3}; + + auto i = std::move_iterator(It(a)); + ASSERT_SAME_TYPE(decltype(std::move(i).base()), It); + assert(std::move(i).base() == It(a)); + } +} + +TEST_CONSTEXPR_CXX14 bool test() { + test_one >(); + test_one >(); + test_one >(); + test_one >(); + test_one(); + test_one(); +#if TEST_STD_VER > 17 + test_one>(); +#endif + + return true; +} + +int main(int, char**) { + test(); +#if TEST_STD_VER > 14 + static_assert(test()); +#endif + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.conv/tested_elsewhere.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.conv/tested_elsewhere.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.conv/tested_elsewhere.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -int main(int, char**) -{ - - return 0; -} diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.incr/post.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.incr/post.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.incr/post.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.incr/post.pass.cpp @@ -10,9 +10,7 @@ // move_iterator -// move_iterator operator++(int); -// -// constexpr in C++17 +// constexpr auto operator++(int); // Return type was move_iterator until C++20 #include #include @@ -31,30 +29,27 @@ #endif template -void -test(It i, It x) -{ +void test(It i, It x) { std::move_iterator r(i); std::move_iterator rr = r++; assert(r.base() == x); assert(rr.base() == i); } -int main(int, char**) -{ - char s[] = "123"; +int main(int, char**) { + char s[] = "123"; #if TEST_STD_VER > 17 - test_single_pass(cpp17_input_iterator(s), cpp17_input_iterator(s + 1)); + test_single_pass(cpp17_input_iterator(s), cpp17_input_iterator(s + 1)); #else - test(cpp17_input_iterator(s), cpp17_input_iterator(s+1)); + test(cpp17_input_iterator(s), cpp17_input_iterator(s+1)); #endif - test(forward_iterator(s), forward_iterator(s+1)); - test(bidirectional_iterator(s), bidirectional_iterator(s+1)); - test(random_access_iterator(s), random_access_iterator(s+1)); - test(s, s+1); + test(forward_iterator(s), forward_iterator(s+1)); + test(bidirectional_iterator(s), bidirectional_iterator(s+1)); + test(random_access_iterator(s), random_access_iterator(s+1)); + test(s, s+1); #if TEST_STD_VER > 14 - { + { constexpr const char *p = "123456789"; typedef std::move_iterator MI; constexpr MI it1 = std::make_move_iterator(p); @@ -63,7 +58,32 @@ constexpr MI it3 = std::make_move_iterator(p) ++; static_assert(it1 == it3, ""); static_assert(it2 != it3, ""); - } + } +#endif + +#if TEST_STD_VER > 17 + // Forward iterators return a copy. + { + int a[] = {1, 2, 3}; + using MoveIter = std::move_iterator>; + + MoveIter i = MoveIter(forward_iterator(a)); + ASSERT_SAME_TYPE(decltype(i++), MoveIter); + auto j = i++; + assert(base(j.base()) == a); + assert(base(i.base()) == a + 1); + } + + // Non-forward iterators return void. + { + int a[] = {1, 2, 3}; + using MoveIter = std::move_iterator>; + + MoveIter i = MoveIter(cpp20_input_iterator(a)); + ASSERT_SAME_TYPE(decltype(i++), void); + i++; + assert(base(i.base()) == a + 1); + } #endif return 0; diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.index/difference_type.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.index/difference_type.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.index/difference_type.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.index/difference_type.pass.cpp @@ -10,10 +10,7 @@ // move_iterator -// requires RandomAccessIterator -// unspecified operator[](difference_type n) const; -// -// constexpr in C++17 +// constexpr reference operator[](difference_type n) const; // Return type unspecified until C++20 #include #include @@ -65,5 +62,20 @@ } #endif +#if TEST_STD_VER > 17 + // Ensure the `iter_move` customization point is being used. + { + int a[] = {0, 1, 2}; + + int iter_moves = 0; + adl::Iterator i = adl::Iterator::TrackMoves(a, iter_moves); + std::move_iterator mi(i); + + auto x = mi[0]; + assert(x == 0); + assert(iter_moves == 1); + } +#endif + return 0; } diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.star/op_star.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.star/op_star.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.star/op_star.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.star/op_star.pass.cpp @@ -18,6 +18,7 @@ #include #include +#include "test_iterators.h" #include "test_macros.h" class A @@ -47,28 +48,42 @@ }; -int main(int, char**) -{ - { - A a; - test(&a, A()); - } +int main(int, char**) { + { + A a; + test(&a, A()); + } #if TEST_STD_VER >= 11 - { - int i; - std::unique_ptr p(&i); - test(&p, std::unique_ptr(&i)); - } + { + int i; + std::unique_ptr p(&i); + test(&p, std::unique_ptr(&i)); + } #endif #if TEST_STD_VER > 14 - { + { constexpr const char *p = "123456789"; typedef std::move_iterator MI; constexpr MI it1 = std::make_move_iterator(p); constexpr MI it2 = std::make_move_iterator(p+1); static_assert(*it1 == p[0], ""); static_assert(*it2 == p[1], ""); - } + } +#endif + +#if TEST_STD_VER > 17 + // Ensure the `iter_move` customization point is being used. + { + int a[] = {0, 1, 2}; + + int iter_moves = 0; + adl::Iterator i = adl::Iterator::TrackMoves(a, iter_moves); + std::move_iterator mi(i); + + auto x = *mi; + assert(x == 0); + assert(iter_moves == 1); + } #endif return 0; diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp @@ -15,11 +15,13 @@ // template // class move_iterator { // public: -// typedef Iter iterator_type; -// typedef Iter::difference_type difference_type; -// typedef Iter pointer; -// typedef Iter::value_type value_type; -// typedef value_type&& reference; +// using iterator_type = Iterator; +// using iterator_concept = input_iterator_tag; // From C++20 +// using iterator_category = see below; // not always present starting from C++20 +// using value_type = iter_value_t; // Until C++20, iterator_traits::value_type +// using difference_type = iter_difference_t; // Until C++20, iterator_traits::difference_type; +// using pointer = Iterator; +// using reference = iter_rvalue_reference_t; // Until C++20, value_type&& // }; #include @@ -28,6 +30,37 @@ #include "test_macros.h" #include "test_iterators.h" +struct FooIter { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = void*; + using difference_type = void*; + using pointer = void*; + using reference = char&; + bool& operator*() const; +}; + +#if TEST_STD_VER > 17 +template <> +struct std::indirectly_readable_traits { + using value_type = int; +}; +template <> +struct std::incrementable_traits { + using difference_type = char; +}; +#endif + +#if TEST_STD_VER > 17 +// Not using `FooIter::value_type`. +static_assert(std::is_same_v::value_type, int>); +// Not using `FooIter::difference_type`. +static_assert(std::is_same_v::difference_type, char>); +static_assert(std::is_same_v::reference, bool&>); + +#else +static_assert(std::is_same::reference, char&>::value, ""); +#endif + template struct DummyIt { typedef std::forward_iterator_tag iterator_category; @@ -40,71 +73,79 @@ }; template -void -test() -{ - typedef std::move_iterator R; - typedef std::iterator_traits T; - 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::value), ""); +void test() { + typedef std::move_iterator R; + typedef std::iterator_traits T; + static_assert((std::is_same::value), ""); + static_assert((std::is_same::value), ""); + static_assert((std::is_same::value), ""); + static_assert((std::is_same::value), ""); + #if TEST_STD_VER > 17 - if constexpr (std::is_same_v) { - static_assert((std::is_same::value), ""); - } else { - static_assert((std::is_same::value), ""); - } + static_assert((std::is_same_v>), ""); #else + static_assert((std::is_same::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 + static_assert((std::is_same::value), ""); +#endif + +#if TEST_STD_VER > 17 + static_assert(std::is_same_v); #endif } -int main(int, char**) -{ - test >(); - test >(); - test >(); - test >(); - test(); +int main(int, char**) { + test >(); + test >(); + test >(); + test >(); + test(); + #if TEST_STD_VER >= 11 - { - typedef DummyIt T; - typedef std::move_iterator It; - static_assert(std::is_same::value, ""); - } - { - typedef DummyIt > T; - typedef std::move_iterator It; - static_assert(std::is_same >::value, ""); - } - { - // Check that move_iterator uses whatever reference type it's given - // when it's not a reference. - typedef DummyIt T; - typedef std::move_iterator It; - static_assert(std::is_same::value, ""); - } - { - typedef DummyIt T; - typedef std::move_iterator It; - static_assert(std::is_same::value, ""); - } - { - typedef DummyIt T; - typedef std::move_iterator It; - static_assert(std::is_same::value, ""); - } + { + typedef DummyIt T; + typedef std::move_iterator It; + static_assert(std::is_same::value, ""); + } + { + typedef DummyIt > T; + typedef std::move_iterator It; + static_assert(std::is_same >::value, ""); + } + { + // Check that move_iterator uses whatever reference type it's given + // when it's not a reference. + typedef DummyIt T; + typedef std::move_iterator It; + static_assert(std::is_same::value, ""); + } + { + typedef DummyIt T; + typedef std::move_iterator It; + static_assert(std::is_same::value, ""); + } + { + typedef DummyIt T; + typedef std::move_iterator It; + static_assert(std::is_same::value, ""); + } #endif #if TEST_STD_VER > 17 - test>(); - static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); - static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); - static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); - static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); - static_assert(std::is_same_v::iterator_concept, std::input_iterator_tag>); + test>(); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, std::input_iterator_tag>); #endif return 0; diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp @@ -21,7 +21,7 @@ #include struct NonAssignable { - NonAssignable& operator=(int i); + NonAssignable& operator=(int i); }; static_assert(std::semiregular); static_assert(std::is_assignable_v); @@ -29,17 +29,22 @@ constexpr bool test() { + // Assigning from an lvalue. { std::move_sentinel m(42); std::move_sentinel m2; m2 = m; assert(m2.base() == 42L); } + + // Assigning from an rvalue. { std::move_sentinel m2; m2 = std::move_sentinel(43); assert(m2.base() == 43L); } + + // SFINAE checks. { static_assert( std::is_assignable_v, std::move_sentinel>); static_assert(!std::is_assignable_v, std::move_sentinel>); diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp @@ -22,6 +22,7 @@ constexpr bool test() { + // The sentinel type is a value. { auto m = std::move_sentinel(42); const auto& cm = m; @@ -34,6 +35,8 @@ ASSERT_SAME_TYPE(decltype(std::move(m).base()), int); ASSERT_SAME_TYPE(decltype(std::move(cm).base()), int); } + + // The sentinel type is a pointer. { int a[] = {1, 2, 3}; auto m = std::move_sentinel(a); diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp @@ -19,6 +19,7 @@ void test() { + // Pointer. { using It = int*; static_assert( std::sentinel_for, std::move_iterator>); @@ -28,6 +29,8 @@ static_assert( std::sentinel_for>, std::move_iterator>); static_assert( std::sized_sentinel_for>, std::move_iterator>); } + + // `Cpp17InputIterator`. { using It = cpp17_input_iterator; static_assert( std::sentinel_for>, std::move_iterator>); @@ -35,6 +38,8 @@ static_assert( std::sentinel_for>, std::move_iterator>); static_assert( std::sized_sentinel_for>, std::move_iterator>); } + + // `std::input_iterator`. { using It = cpp20_input_iterator; static_assert( std::sentinel_for>, std::move_iterator>); @@ -42,6 +47,8 @@ static_assert( std::sentinel_for>, std::move_iterator>); static_assert( std::sized_sentinel_for>, std::move_iterator>); } + + // `std::forward_iterator`. { using It = forward_iterator; static_assert( std::sentinel_for, std::move_iterator>); @@ -51,6 +58,8 @@ static_assert( std::sentinel_for>, std::move_iterator>); static_assert( std::sized_sentinel_for>, std::move_iterator>); } + + // `std::bidirectional_iterator`. { using It = bidirectional_iterator; static_assert( std::sentinel_for, std::move_iterator>); @@ -60,6 +69,8 @@ static_assert( std::sentinel_for>, std::move_iterator>); static_assert( std::sized_sentinel_for>, std::move_iterator>); } + + // `std::random_access_iterator`. { using It = random_access_iterator; static_assert( std::sentinel_for, std::move_iterator>); @@ -69,6 +80,8 @@ static_assert( std::sentinel_for>, std::move_iterator>); static_assert( std::sized_sentinel_for>, std::move_iterator>); } + + // `std::contiguous_iterator`. { using It = contiguous_iterator; static_assert( std::sentinel_for, std::move_iterator>); @@ -78,6 +91,8 @@ static_assert( std::sentinel_for>, std::move_iterator>); static_assert( std::sized_sentinel_for>, std::move_iterator>); } + + // `std::contiguous_iterator` with the spaceship operator. { using It = three_way_contiguous_iterator; static_assert( std::sentinel_for, std::move_iterator>); diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp @@ -31,15 +31,20 @@ constexpr bool test() { + // Constructing from an lvalue. { std::move_sentinel m(42); std::move_sentinel m2 = m; assert(m2.base() == 42L); } + + // Constructing from an rvalue. { std::move_sentinel m2 = std::move_sentinel(43); assert(m2.base() == 43L); } + + // SFINAE checks. { static_assert( std::is_convertible_v, std::move_sentinel>); static_assert( std::is_convertible_v, std::move_sentinel>); diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp @@ -19,14 +19,20 @@ constexpr bool test() { + + // The underlying sentinel is an integer. { std::move_sentinel m; assert(m.base() == 0); } + + // The underlying sentinel is a pointer. { std::move_sentinel m; assert(m.base() == nullptr); } + + // The underlying sentinel is a user-defined type with an explicit default constructor. { struct S { explicit S() = default; @@ -35,6 +41,7 @@ std::move_sentinel m; assert(m.base().i == 3); } + return true; } diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp @@ -19,17 +19,22 @@ constexpr bool test() { + // The underlying sentinel is an integer. { static_assert(!std::is_convertible_v>); std::move_sentinel m(42); assert(m.base() == 42); } + + // The underlying sentinel is a pointer. { static_assert(!std::is_convertible_v>); int i = 42; std::move_sentinel m(&i); assert(m.base() == &i); } + + // The underlying sentinel is a user-defined type with an explicit default constructor. { struct S { explicit S() = default; diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.pass.cpp @@ -38,30 +38,25 @@ static_assert(!HasLess, std::move_sentinel>); template -constexpr bool test_one() -{ - { - char s[] = "abc"; - const auto it = std::move_iterator(It(s)); - const auto sent1 = std::move_sentinel>(sentinel_wrapper(It(s))); - const auto sent2 = std::move_sentinel>(sentinel_wrapper(It(s + 1))); - ASSERT_SAME_TYPE(decltype(it == sent1), bool); - assert( (it == sent1)); - assert(!(it != sent1)); - assert(!(it == sent2)); - assert( (it != sent2)); - assert( (sent1 == it)); - assert(!(sent1 != it)); - assert(!(sent2 == it)); - assert( (sent2 != it)); - static_assert(!HasEquals); - static_assert(!HasLess); - } - return true; +constexpr void test_one() { + char s[] = "abc"; + const auto it = std::move_iterator(It(s)); + const auto sent1 = std::move_sentinel>(sentinel_wrapper(It(s))); + const auto sent2 = std::move_sentinel>(sentinel_wrapper(It(s + 1))); + ASSERT_SAME_TYPE(decltype(it == sent1), bool); + assert( (it == sent1)); + assert(!(it != sent1)); + assert(!(it == sent2)); + assert( (it != sent2)); + assert( (sent1 == it)); + assert(!(sent1 != it)); + assert(!(sent2 == it)); + assert( (sent2 != it)); + static_assert(!HasEquals); + static_assert(!HasLess); } -constexpr bool test() -{ +constexpr bool test() { test_one>(); test_one>(); test_one>(); @@ -75,8 +70,7 @@ return true; } -int main(int, char**) -{ +int main(int, char**) { test(); static_assert(test()); 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 --- 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 @@ -20,48 +20,9 @@ #include #include #include +#include "test_iterators.h" #include "test_macros.h" -namespace adl { - -struct Iterator { - using value_type = int; - using difference_type = ptrdiff_t; - - value_type* ptr_ = nullptr; - int* iter_move_invocations_ = nullptr; - - constexpr Iterator() = default; - constexpr explicit Iterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {} - - constexpr value_type& operator*() const { return *ptr_; } - - Iterator& operator++() { ++ptr_; return *this; } - Iterator operator++(int) { - Iterator prev = *this; - ++ptr_; - return prev; - } - - constexpr Iterator& operator--() { --ptr_; return *this; } - constexpr Iterator operator--(int) { - Iterator prev = *this; - --ptr_; - return prev; - } - - constexpr friend value_type&& iter_move(Iterator iter) { - if (iter.iter_move_invocations_) { - ++(*iter.iter_move_invocations_); - } - return std::move(*iter); - } - - friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } -}; - -} // namespace adl - constexpr bool test() { // Can use `iter_move` with a regular array. { @@ -76,13 +37,13 @@ assert(iter_move(ri) == 1); } - // Ensure the `iter_move` customization point is being used. + // Check that the `iter_move` customization point is being used. { constexpr int N = 3; int a[N] = {0, 1, 2}; int iter_move_invocations = 0; - adl::Iterator i(a + N, iter_move_invocations); + adl::Iterator i = adl::Iterator::TrackMoves(a + N, iter_move_invocations); std::reverse_iterator ri(i); int x = iter_move(ri); assert(x == 2); 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 --- 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 @@ -22,47 +22,9 @@ #include #include #include +#include "test_iterators.h" #include "test_macros.h" -namespace adl { - -struct Iterator { - using value_type = int; - using difference_type = ptrdiff_t; - - value_type* ptr_ = nullptr; - int* iter_swap_invocations_ = nullptr; - - constexpr Iterator() = default; - constexpr explicit Iterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {} - - value_type& 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; - } - - constexpr friend void iter_swap(Iterator a, Iterator) { - if (a.iter_swap_invocations_) { - ++(*a.iter_swap_invocations_); - } - } - - friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } -}; - -} // namespace adl - constexpr bool test() { // Can use `iter_swap` with a regular array. { @@ -80,15 +42,17 @@ assert(a[2] == 0); } - // Ensure the `iter_swap` customization point is being used. + // Check that the `iter_swap` customization point is being used. { int iter_swap_invocations = 0; - adl::Iterator i1(iter_swap_invocations), i2(iter_swap_invocations); - std::reverse_iterator ri1(i1), ri2(i2); - iter_swap(i1, i2); + int a[] = {0, 1, 2}; + adl::Iterator base1 = adl::Iterator::TrackSwaps(a + 1, iter_swap_invocations); + adl::Iterator base2 = adl::Iterator::TrackSwaps(a + 2, iter_swap_invocations); + std::reverse_iterator ri1(base1), ri2(base2); + iter_swap(ri1, ri2); assert(iter_swap_invocations == 1); - iter_swap(i2, i1); + iter_swap(ri2, ri1); assert(iter_swap_invocations == 2); } diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp @@ -82,7 +82,9 @@ using difference_type = char; }; +// Not using `FooIter::value_type`. static_assert(std::is_same_v::value_type, int>); +// Not using `FooIter::difference_type`. static_assert(std::is_same_v::difference_type, char>); #endif diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp @@ -22,40 +22,40 @@ namespace adl { template -struct Iterator { +struct MaybeNoexceptIterator { using value_type = int; using difference_type = ptrdiff_t; value_type* ptr_ = nullptr; int* iter_move_invocations_ = nullptr; - constexpr Iterator() = default; - constexpr explicit Iterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {} + constexpr MaybeNoexceptIterator() = default; + constexpr explicit MaybeNoexceptIterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {} constexpr value_type& operator*() const { return *ptr_; } - Iterator& operator++() { ++ptr_; return *this; } - Iterator operator++(int) { - Iterator prev = *this; + MaybeNoexceptIterator& operator++() { ++ptr_; return *this; } + MaybeNoexceptIterator operator++(int) { + MaybeNoexceptIterator prev = *this; ++ptr_; return prev; } - constexpr Iterator& operator--() { --ptr_; return *this; } - constexpr Iterator operator--(int) { - Iterator prev = *this; + constexpr MaybeNoexceptIterator& operator--() { --ptr_; return *this; } + constexpr MaybeNoexceptIterator operator--(int) { + MaybeNoexceptIterator prev = *this; --ptr_; return prev; } - constexpr friend value_type&& iter_move(Iterator iter) noexcept(IsNoexcept) { + constexpr friend value_type&& iter_move(MaybeNoexceptIterator iter) noexcept(IsNoexcept) { if (iter.iter_move_invocations_) { ++(*iter.iter_move_invocations_); } return std::move(*iter); } - friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } + friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) { return lhs.ptr_ == rhs.ptr_; } }; template @@ -68,8 +68,12 @@ constexpr View(int& iter_move_invocations) : iter_moves(&iter_move_invocations) { } - constexpr adl::Iterator begin() { return adl::Iterator(a, *iter_moves); } - constexpr adl::Iterator end() { return adl::Iterator(a + N, *iter_moves); } + constexpr adl::MaybeNoexceptIterator begin() { + return adl::MaybeNoexceptIterator(a, *iter_moves); + } + constexpr adl::MaybeNoexceptIterator end() { + return adl::MaybeNoexceptIterator(a + N, *iter_moves); + } }; } // namespace adl @@ -134,7 +138,7 @@ using ThrowingSplitView = std::ranges::lazy_split_view, adl::View>; using ThrowingValueType = std::ranges::iterator_t::value_type; using ThrowingIter = std::ranges::iterator_t; - ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval>())); + ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval>())); ASSERT_NOT_NOEXCEPT(iter_move(std::declval())); } @@ -142,7 +146,7 @@ using NoexceptSplitView = std::ranges::lazy_split_view, adl::View>; using NoexceptValueType = std::ranges::iterator_t::value_type; using NoexceptIter = std::ranges::iterator_t; - ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval>())); + ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval>())); ASSERT_NOEXCEPT(iter_move(std::declval())); } } diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp @@ -23,39 +23,41 @@ namespace adl { template -struct Iterator { +struct MaybeNoexceptIterator { using value_type = int; using difference_type = ptrdiff_t; value_type* ptr_ = nullptr; int* iter_swap_invocations_ = nullptr; - constexpr Iterator() = default; - constexpr explicit Iterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {} + constexpr MaybeNoexceptIterator() = default; + constexpr explicit MaybeNoexceptIterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {} value_type& operator*() const { return *ptr_; } - Iterator& operator++() { ++ptr_; return *this; } - Iterator operator++(int) { - Iterator prev = *this; + MaybeNoexceptIterator& operator++() { ++ptr_; return *this; } + MaybeNoexceptIterator operator++(int) { + MaybeNoexceptIterator prev = *this; ++ptr_; return prev; } - Iterator& operator--() { --ptr_; return *this; } - Iterator operator--(int) { - Iterator prev = *this; + MaybeNoexceptIterator& operator--() { --ptr_; return *this; } + MaybeNoexceptIterator operator--(int) { + MaybeNoexceptIterator prev = *this; --ptr_; return prev; } - constexpr friend void iter_swap(Iterator a, Iterator) noexcept(IsNoexcept) { + constexpr friend void iter_swap(MaybeNoexceptIterator a, MaybeNoexceptIterator) noexcept(IsNoexcept) { if (a.iter_swap_invocations_) { ++(*a.iter_swap_invocations_); } } - friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } + friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) { + return lhs.ptr_ == rhs.ptr_; + } }; template @@ -66,8 +68,12 @@ constexpr View(int& iter_swap_invocations) : iter_swaps(&iter_swap_invocations) { } - constexpr adl::Iterator begin() { return adl::Iterator(*iter_swaps); } - constexpr adl::Iterator end() { return adl::Iterator(*iter_swaps); } + constexpr adl::MaybeNoexceptIterator begin() { + return adl::MaybeNoexceptIterator(*iter_swaps); + } + constexpr adl::MaybeNoexceptIterator end() { + return adl::MaybeNoexceptIterator(*iter_swaps); + } }; } // namespace adl @@ -189,8 +195,9 @@ using ThrowingSplitView = std::ranges::lazy_split_view, adl::View>; using ThrowingValueType = std::ranges::iterator_t::value_type; using ThrowingIter = std::ranges::iterator_t; - ASSERT_NOT_NOEXCEPT( - std::ranges::iter_swap(std::declval>(), std::declval>())); + ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap( + std::declval>(), + std::declval>())); ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); } @@ -198,8 +205,9 @@ using NoexceptSplitView = std::ranges::lazy_split_view, adl::View>; using NoexceptValueType = std::ranges::iterator_t::value_type; using NoexceptIter = std::ranges::iterator_t; - ASSERT_NOEXCEPT( - std::ranges::iter_swap(std::declval>(), std::declval>())); + ASSERT_NOEXCEPT(std::ranges::iter_swap( + std::declval>(), + std::declval>())); ASSERT_NOEXCEPT(iter_swap(std::declval(), std::declval())); } } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp @@ -41,40 +41,6 @@ static_assert(!std::is_invocable_v); -namespace adl { - -static int iter_move_invocations = 0; - -template -struct Iterator { - using value_type = T; - using difference_type = int; - using iterator_concept = std::input_iterator_tag; - - 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; - } - - 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**) { // An empty range -- no default constructors should be invoked. { @@ -411,17 +377,18 @@ constexpr int N = 3; int in[N] = {1, 2, 3}; Buffer out; - adl::Iterator begin(in); - adl::Iterator end(in + N); + int iter_moves = 0; + adl::Iterator begin = adl::Iterator::TrackMoves(in, iter_moves); + adl::Iterator end = adl::Iterator::TrackMoves(in + N, iter_moves); std::ranges::uninitialized_move(begin, end, out.begin(), out.end()); - assert(adl::iter_move_invocations == 3); - adl::iter_move_invocations = 0; + assert(iter_moves == 3); + iter_moves = 0; std::ranges::subrange range(begin, end); std::ranges::uninitialized_move(range, out); - assert(adl::iter_move_invocations == 3); - adl::iter_move_invocations = 0; + assert(iter_moves == 3); + iter_moves = 0; } return 0; diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp @@ -24,8 +24,8 @@ #include "../buffer.h" #include "../counted.h" -#include "test_macros.h" #include "test_iterators.h" +#include "test_macros.h" // TODO(varconst): consolidate the ADL checks into a single file. // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, @@ -38,40 +38,6 @@ static_assert(!std::is_invocable_v); -namespace adl { - -static int iter_move_invocations = 0; - -template -struct Iterator { - using value_type = T; - using difference_type = int; - using iterator_concept = std::input_iterator_tag; - - 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; - } - - 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**) { // An empty range -- no default constructors should be invoked. { @@ -187,17 +153,18 @@ constexpr int N = 3; int in[N] = {1, 2, 3}; Buffer out; - adl::Iterator begin(in); - adl::Iterator end(in + N); + int iter_moves = 0; + adl::Iterator begin = adl::Iterator::TrackMoves(in, iter_moves); + adl::Iterator end = adl::Iterator::TrackMoves(in + N, iter_moves); std::ranges::uninitialized_move(begin, end, out.begin(), out.end()); - assert(adl::iter_move_invocations == 3); - adl::iter_move_invocations = 0; + assert(iter_moves == 3); + iter_moves = 0; std::ranges::subrange range(begin, end); std::ranges::uninitialized_move(range, out); - assert(adl::iter_move_invocations == 3); - adl::iter_move_invocations = 0; + assert(iter_moves == 3); + iter_moves = 0; } return 0; diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -721,6 +721,77 @@ private: decltype(base(std::declval())) base_; }; + +namespace adl { + +class Iterator { + public: + using value_type = int; + using difference_type = ptrdiff_t; + + private: + value_type* ptr_ = nullptr; + int* iter_moves_ = nullptr; + int* iter_swaps_ = nullptr; + + constexpr Iterator(int* p, int* iter_moves, int* iter_swaps) + : ptr_(p) + , iter_moves_(iter_moves) + , iter_swaps_(iter_swaps) {} + + public: + constexpr Iterator() = default; + static constexpr Iterator TrackMoves(int* p, int& iter_moves) { + return Iterator(p, &iter_moves, /*iter_swaps=*/nullptr); + } + static constexpr Iterator TrackSwaps(int& iter_swaps) { + return Iterator(/*p=*/nullptr, /*iter_moves=*/nullptr, &iter_swaps); + } + static constexpr Iterator TrackSwaps(int* p, int& iter_swaps) { + return Iterator(p, /*iter_moves=*/nullptr, &iter_swaps); + } + + constexpr int iter_moves() const { assert(iter_moves_); return *iter_moves_; } + constexpr int iter_swaps() const { assert(iter_swaps_); return *iter_swaps_; } + + constexpr value_type& operator*() const { return *ptr_; } + + constexpr Iterator operator+(difference_type n) const { + return Iterator(ptr_ + n, iter_moves_, iter_swaps_); + } + + constexpr Iterator& operator++() { ++ptr_; return *this; } + constexpr Iterator operator++(int) { + Iterator prev = *this; + ++ptr_; + return prev; + } + + constexpr Iterator& operator--() { --ptr_; return *this; } + constexpr Iterator operator--(int) { + Iterator prev = *this; + --ptr_; + return prev; + } + + constexpr friend void iter_swap(Iterator a, Iterator) { + if (a.iter_swaps_) { + ++(*a.iter_swaps_); + } + } + + constexpr friend value_type&& iter_move(Iterator iter) { + if (iter.iter_moves_) { + ++(*iter.iter_moves_); + } + return std::move(*iter); + } + + constexpr friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } +}; + +} // namespace adl + #endif // TEST_STD_VER > 17 #endif // SUPPORT_TEST_ITERATORS_H