diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h --- a/libcxx/include/__ranges/join_view.h +++ b/libcxx/include/__ranges/join_view.h @@ -16,10 +16,8 @@ #include <__ranges/all.h> #include <__ranges/concepts.h> #include <__ranges/non_propagating_cache.h> -#include <__ranges/ref_view.h> -#include <__ranges/subrange.h> +#include <__ranges/range_adaptor.h> #include <__ranges/view_interface.h> -#include <__utility/declval.h> #include <__utility/forward.h> #include #include @@ -338,7 +336,22 @@ template explicit join_view(_Range&&) -> join_view>; - + +namespace views { +namespace __join_view { + struct __fn : __range_adaptor_closure<__fn> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(join_view>(std::forward<_Range>(__range)))) + -> decltype( join_view>(std::forward<_Range>(__range))) + { return join_view>(std::forward<_Range>(__range)); } + }; +} // namespace __join_view +inline namespace __cpo { + inline constexpr auto join = __join_view::__fn{}; +} // namespace __cpo +} // namespace views } // namespace ranges #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// std::views::join +#include + +#include +#include + +#include "types.h" + +struct MoveOnlyOuter : SimpleForwardCommonOuter { + using SimpleForwardCommonOuter::SimpleForwardCommonOuter; + + constexpr MoveOnlyOuter(MoveOnlyOuter&&) = default; + constexpr MoveOnlyOuter(const MoveOnlyOuter&) = delete; + + constexpr MoveOnlyOuter& operator=(MoveOnlyOuter&&) = default; + constexpr MoveOnlyOuter& operator=(const MoveOnlyOuter&) = delete; +}; + +struct Foo { + int i; + constexpr Foo(int ii) : i(ii) {} +}; + +template +concept CanBePiped = requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; + }; + +constexpr bool test() { + int buffer1[3] = {1, 2, 3}; + int buffer2[2] = {4, 5}; + int buffer3[4] = {6, 7, 8, 9}; + Foo nested[2][3][3] = {{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}; + + { + // Test `views::join(v)` + ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3}; + using Result = std::ranges::join_view>; + std::same_as auto v = std::views::join(inners); + assert(std::ranges::next(v.begin(), 9) == v.end()); + assert(&(*v.begin()) == buffer1); + } + { + // Test `views::join(move-only-view)` + ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3}; + using Result = std::ranges::join_view; + std::same_as auto v = std::views::join(MoveOnlyOuter{inners}); + assert(std::ranges::next(v.begin(), 9) == v.end()); + assert(&(*v.begin()) == buffer1); + + static_assert(std::invocable); + static_assert(!std::invocable); + } + { + // views::join(join_view) should join the view instead of calling copy constructor + // LWG3474 + auto jv = std::views::join(nested); + static_assert(std::is_same_v, Foo(&)[3]>); + + auto jv2 = std::views::join(jv); + static_assert(std::is_same_v, Foo&>); + + assert(&(*jv2.begin()) == &nested[0][0][0]); + } + + { + // Test `v | views::join` + ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3}; + + using Result = std::ranges::join_view>; + std::same_as auto v = inners | std::views::join; + assert(std::ranges::next(v.begin(), 9) == v.end()); + assert(&(*v.begin()) == buffer1); + static_assert(CanBePiped); + } + { + // Test `move-only-view | views::join` + ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3}; + using Result = std::ranges::join_view; + std::same_as auto v = MoveOnlyOuter{inners} | std::views::join; + assert(std::ranges::next(v.begin(), 9) == v.end()); + assert(&(*v.begin()) == buffer1); + + static_assert(CanBePiped); + static_assert(!CanBePiped); + } + { + // join_view | views::join should join the view instead of calling copy constructor + // LWG3474 + auto jv = nested | std::views::join | std::views::join; + static_assert(std::is_same_v, Foo&>); + + assert(&(*jv.begin()) == &nested[0][0][0]); + static_assert(CanBePiped); + } + + { + // Test `adaptor | views::join` + auto join_twice = std::views::join | std::views::join; + auto jv = nested | join_twice; + static_assert(std::is_same_v, Foo&>); + + assert(&(*jv.begin()) == &nested[0][0][0]); + static_assert(CanBePiped); + } + + { + static_assert(!CanBePiped); + static_assert(!CanBePiped); + static_assert(!CanBePiped); + static_assert(CanBePiped); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp @@ -10,12 +10,13 @@ // UNSUPPORTED: libcpp-has-no-incomplete-ranges // constexpr auto begin(); -// constexpr auto begin() const; +// constexpr auto begin() const +// requires input_­range && +// is_reference_v>; #include #include -#include "test_macros.h" #include "types.h" struct NonSimpleParentView : std::ranges::view_base { @@ -29,6 +30,14 @@ const ChildView* end() const; }; +struct ConstNotRange : std::ranges::view_base { + const ChildView* begin(); + const ChildView* end(); +}; + +template +concept HasConstBegin = requires(const T& t) { t.begin(); }; + constexpr bool test() { int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; @@ -94,8 +103,19 @@ { const std::ranges::join_view jv(buffer); assert(*jv.begin() == 1111); + static_assert(HasConstBegin); + } + { + // !input_­range + std::ranges::join_view jv{ConstNotRange{}}; + static_assert(!HasConstBegin); + } + { + // !is_reference_v> + auto innerRValueRange = std::views::iota(0, 5) | std::views::transform([](int) { return ChildView{}; }); + std::ranges::join_view jv{innerRValueRange}; + static_assert(!HasConstBegin); } - // !simple-view { std::ranges::join_view jv; diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp @@ -37,6 +37,11 @@ template<> inline constexpr bool std::ranges::enable_borrowed_range = true; +struct NestedChildren : std::ranges::view_base { + View* begin() const; + View* end() const; +}; + void testCTAD() { View v; Range r; @@ -66,4 +71,13 @@ decltype(std::ranges::join_view(std::move(br))), std::ranges::join_view> >); + + NestedChildren n; + std::ranges::join_view jv(n); + + // CTAD generated from the copy constructor instead of joining the join_view + static_assert(std::same_as< decltype(std::ranges::join_view(jv)), decltype(jv) >); + + // CTAD generated from the move constructor instead of joining the join_view + static_assert(std::same_as< decltype(std::ranges::join_view(std::move(jv))), decltype(jv) >); } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp @@ -14,14 +14,24 @@ #include #include -#include "test_macros.h" #include "types.h" +struct DefaultView : std::ranges::view_base { + int i; // deliberately uninitialised -constexpr bool test() { - std::ranges::join_view> jv; - assert(std::move(jv).base().ptr_ == globalChildren); + ChildView* begin() const; + ChildView* end() const; +}; +constexpr bool test() { + { + std::ranges::join_view> jv; + assert(std::move(jv).base().ptr_ == globalChildren); + } + { + std::ranges::join_view jv; + assert(jv.base().i == 0); + } static_assert( std::default_initializable>>); static_assert(!std::default_initializable>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp @@ -19,46 +19,206 @@ #include "test_macros.h" #include "types.h" +template +concept HasConstEnd = requires (const T& t){ + t.end(); +}; + + +// | ID | outer | outer | outer | inner | inner | inner | end() | end() | +// | | simple | forward | common | l_ref | forward | common | | const | +// |----|--------|---------|--------|-------|---------|--------|---------------|--------------| +// | 1 | Y | Y | Y | Y | Y | Y |iterator |iterator| +// | 2 | Y | Y | Y | Y | Y | N |sentinel |sentinel| +// | 3 | Y | Y | Y | Y | N | Y |sentinel |sentinel| +// | 4 | Y | Y | Y | N | Y | Y |sentinel | - | +// | 5 | Y | Y | N | Y | Y | Y |sentinel |sentinel| +// | 6 | Y | N | Y | Y | Y | Y |sentinel |sentinel| +// | 7 | N | Y | Y | Y | Y | Y |iterator|iterator| +// | 8 | N | Y | Y | Y | Y | N |sentinel|sentinel| +// | 9 | N | Y | Y | Y | N | Y |sentinel|sentinel| +// | 10 | N | Y | Y | N | Y | Y |sentinel| - | +// | 11 | N | Y | N | Y | Y | Y |sentinel|sentinel| +// | 12 | N | N | Y | Y | Y | Y |sentinel|sentinel| +// +// + +struct ConstNotRange : std::ranges::view_base { + const ChildView* begin(); + const ChildView* end(); +}; constexpr bool test() { int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; - // Non const common, forward range. { - std::ranges::join_view jv(buffer); + // test ID 1 + ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]}; + SimpleForwardCommonOuter outer{inners}; + + std::ranges::join_view jv(outer); assert(jv.end() == std::ranges::next(jv.begin(), 16)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 16)); - static_assert(std::same_as); + static_assert(HasConstEnd); + static_assert(std::same_as); + static_assert(std::ranges::common_range); + static_assert(std::ranges::common_range); } + { + // test ID 2 + ForwardNonCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]}; + SimpleForwardCommonOuter outer{inners}; - // Non const not common, input range. + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 12)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 12)); + + static_assert(HasConstEnd); + static_assert(std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } { - ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; - auto jv = std::ranges::join_view(ParentView(children)); + // test ID 3 + InputCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]}; + SimpleForwardCommonOuter outer{inners}; + + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 12)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 12)); + + static_assert(HasConstEnd); + static_assert(std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } + { + // test ID 4 + ForwardCommonInner inners[2] = {buffer[0], buffer[1]}; + InnerRValue> outer{inners}; + + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 8)); + + static_assert(!HasConstEnd); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } + { + // test ID 5 + ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]}; + SimpleForwardNonCommonOuter outer{inners}; + + std::ranges::join_view jv(outer); assert(jv.end() == std::ranges::next(jv.begin(), 16)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 16)); - static_assert(!std::same_as); + static_assert(HasConstEnd); + static_assert(std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); } - - // Const common, forward range. { - const std::ranges::join_view jv(buffer); + // test ID 6 + ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]}; + SimpleInputCommonOuter outer{inners}; + + std::ranges::join_view jv(outer); assert(jv.end() == std::ranges::next(jv.begin(), 16)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 16)); + + static_assert(HasConstEnd); + static_assert(std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } + { + // test ID 7 + ForwardCommonInner inners[1] = {buffer[0]}; + NonSimpleForwardCommonOuter outer{inners}; + + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 4)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 4)); - static_assert(std::same_as); + static_assert(HasConstEnd); + static_assert(!std::same_as); + static_assert(std::ranges::common_range); + static_assert(std::ranges::common_range); } + { + // test ID 8 + ForwardNonCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]}; + NonSimpleForwardCommonOuter outer{inners}; - // Const not common, input range. + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 12)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 12)); + + static_assert(HasConstEnd); + static_assert(!std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } { - static_assert(std::is_reference_v>); + // test ID 9 + InputCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]}; + NonSimpleForwardCommonOuter outer{inners}; - CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; - const auto jv = std::ranges::join_view(ParentView(children)); + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 12)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 12)); + + static_assert(HasConstEnd); + static_assert(!std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } + { + // test ID 10 + ForwardCommonInner inners[2] = {buffer[0], buffer[1]}; + InnerRValue> outer{inners}; + + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 8)); + + static_assert(!HasConstEnd); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } + { + // test ID 11 + ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]}; + NonSimpleForwardNonCommonOuter outer{inners}; + + std::ranges::join_view jv(outer); assert(jv.end() == std::ranges::next(jv.begin(), 16)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 16)); - static_assert(!std::same_as); + static_assert(HasConstEnd); + static_assert(!std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); } + { + // test ID 12 + ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]}; + NonSimpleInputCommonOuter outer{inners}; + std::ranges::join_view jv(outer); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 16)); + + static_assert(HasConstEnd); + static_assert(!std::same_as); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + } + { + std::ranges::join_view jv(ConstNotRange{}); + static_assert(!HasConstEnd); + } // Has some empty children. { CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 1), CopyableChild(buffer[3], 0)}; diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp @@ -17,12 +17,10 @@ #include #include -#include "test_macros.h" #include "types.h" - -template -bool isEqual(R &r, I i) { +template +bool isEqual(R& r, I i) { for (auto e : r) if (e != *i++) return false; @@ -32,7 +30,7 @@ int main(int, char**) { { int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; - int *flattened = reinterpret_cast(buffer); + int* flattened = reinterpret_cast(buffer); ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; auto jv = std::ranges::join_view(ParentView(children)); @@ -45,6 +43,22 @@ std::ranges::join_view jv(vec); assert(isEqual(jv, check.begin())); } + { + // P2328R1 + // join a range of prvalue containers + std::vector x{1, 2, 3, 4}; + auto y = x | std::views::transform([](auto i) { + std::vector v(i); + for (int& ii : v) { + ii = i; + } + return v; + }); + + std::ranges::join_view jv(y); + std::vector check{1, 2, 2, 3, 3, 3, 4, 4, 4, 4}; + assert(isEqual(jv, check.begin())); + } return 0; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp @@ -15,9 +15,37 @@ #include #include -#include "test_macros.h" #include "../types.h" +template +struct arrow_input_iter { + Base it_; + + using value_type = std::iter_value_t; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + arrow_input_iter() + requires std::default_initializable + = default; + constexpr arrow_input_iter(Base it) : it_(std::move(it)) {} + + constexpr arrow_input_iter& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } + + constexpr std::iter_reference_t operator*() const { return *it_; } + constexpr auto operator->() const { return it_; } + + friend constexpr bool operator==(const arrow_input_iter& x, const arrow_input_iter& y) { return x.it_ == y.it_; } +}; + +using ArrowInner = BufferView>; +static_assert(std::ranges::input_range); +static_assert(HasArrow>); + constexpr bool test() { Box buffer[4][4] = {{{1111}, {2222}, {3333}, {4444}}, {{555}, {666}, {777}, {888}}, {{99}, {1010}, {1111}, {1212}}, {{13}, {14}, {15}, {16}}}; @@ -26,18 +54,50 @@ ValueView children[4] = {ValueView(buffer[0]), ValueView(buffer[1]), ValueView(buffer[2]), ValueView(buffer[3])}; std::ranges::join_view jv(ValueView>{children}); assert(jv.begin()->x == 1111); + static_assert(HasArrow); } { std::ranges::join_view jv(buffer); assert(jv.begin()->x == 1111); + static_assert(HasArrow); } { const std::ranges::join_view jv(buffer); assert(jv.begin()->x == 1111); + static_assert(HasArrow); } + { + // has-arrow && !copyable + static_assert(HasArrow>); + MoveOnlyIterInner inners[2] = {buffer[0], buffer[1]}; + std::ranges::join_view jv{inners}; + static_assert(HasArrow); + static_assert(!HasArrow); + } + + { + // !has-arrow && copyable + using Inner = BufferView>; + Inner inners[2] = {buffer[0], buffer[1]}; + std::ranges::join_view jv{inners}; + static_assert(!HasArrow); + static_assert(!HasArrow); + } + + { + // arrow returns inner iterator + ArrowInner inners[2] = {buffer[0], buffer[1]}; + std::ranges::join_view jv{inners}; + static_assert(HasArrow); + static_assert(HasArrow); + + auto jv_it = jv.begin(); + std::same_as> auto arrow_it = jv_it.operator->(); + assert(arrow_it->x == 1111); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp @@ -9,7 +9,10 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-has-no-incomplete-ranges -// constexpr iterator(iterator i); +// constexpr iterator(iterator i) +// requires Const && +// convertible_­to, OuterIter> && +// convertible_­to, InnerIter>; #include #include @@ -17,18 +20,59 @@ #include "test_macros.h" #include "../types.h" +using ConstCompatibleInner = BufferView; + +using ConstIncompatibleInner = BufferView, forward_iterator, + bidirectional_iterator, bidirectional_iterator>; + +template +using ConstCompatibleOuter = BufferView; + +template +using ConstIncompatibleOuter = BufferView, forward_iterator, + bidirectional_iterator, bidirectional_iterator>; + constexpr bool test() { int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + { + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), + CopyableChild(buffer[3])}; + std::ranges::join_view jv(CopyableParent{children}); + auto iter1 = jv.begin(); + using iterator = decltype(iter1); + using const_iterator = decltype(std::as_const(jv).begin()); + static_assert(!std::is_same_v); + const_iterator iter2 = iter1; + assert(iter1 == iter2); - CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; - std::ranges::join_view jv(CopyableParent{children}); - auto iter1 = jv.begin(); - std::ranges::iterator_t iter2 = iter1; - assert(iter1 == iter2); + static_assert(std::constructible_from); + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + } + { + // !convertible_to>; + ConstIncompatibleInner inners[2] = {buffer[0], buffer[1]}; + ConstCompatibleOuter outer{inners}; + std::ranges::join_view jv(outer); + using iterator = decltype(jv.begin()); + using const_iterator = decltype(std::as_const(jv).begin()); + static_assert(!std::is_same_v); - // We cannot create a non-const iterator from a const iterator. - static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } + { + // !convertible_to>; + ConstCompatibleInner inners[2] = {buffer[0], buffer[1]}; + ConstIncompatibleOuter outer{inners}; + std::ranges::join_view jv(outer); + using iterator = decltype(jv.begin()); + using const_iterator = decltype(std::as_const(jv).begin()); + static_assert(!std::is_same_v); + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp @@ -14,17 +14,33 @@ #include #include -#include "test_macros.h" #include "../types.h" +using NonDefaultIter = cpp20_input_iterator; +static_assert(!std::default_initializable); + +using NonDefaultIterView = BufferView>; +static_assert(std::ranges::input_range); + constexpr bool test() { int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; - - CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; - CopyableParent parent{children}; - std::ranges::join_view jv(parent); - std::ranges::iterator_t iter(jv, std::ranges::begin(parent)); - assert(*iter == 1); + { + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), + CopyableChild(buffer[3])}; + CopyableParent parent{children}; + std::ranges::join_view jv(parent); + std::ranges::iterator_t iter(jv, std::ranges::begin(parent)); + assert(*iter == 1); + } + + { + // LWG 3569 Inner iterator not default_initializable + NonDefaultIterView inners[2] = {buffer[0], buffer[1]}; + auto outer = std::views::all(inners); + std::ranges::join_view jv(outer); + std::ranges::iterator_t iter(jv, std::ranges::begin(outer)); + assert(*iter == 1); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp @@ -10,14 +10,26 @@ // UNSUPPORTED: libcpp-has-no-incomplete-ranges // constexpr iterator& operator--(); +// requires ref-is-glvalue && bidirectional_­range && +// bidirectional_­range> && +// common_­range>; // constexpr iterator operator--(int); +// requires ref-is-glvalue && bidirectional_­range && +// bidirectional_­range> && +// common_­range>; #include #include +#include -#include "test_macros.h" #include "../types.h" +template +concept CanPreDecrement = requires(T& t) { --t; }; + +template +concept CanPostDecrement = requires(T& t) { t--; }; + constexpr bool test() { int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; @@ -61,6 +73,67 @@ assert(*--iter == i); } } + { +#if defined(__GNUG__) && !defined(__clang__) + if (!std::is_constant_evaluated()) { +#endif + // skip empty inner + BidiCommonInner inners[4] = {buffer[0], {nullptr, 0}, {nullptr, 0}, buffer[1]}; + std::ranges::join_view jv(inners); + auto iter = jv.end(); + for (int i = 8; i != 0; --i) { + assert(*--iter == i); + } +#if defined(__GNUG__) && !defined(__clang__) + } +#endif + } + { + // basic type checking + std::ranges::join_view jv(buffer); + auto iter1 = std::ranges::next(jv.begin(), 4); + using iterator = decltype(iter1); + + std::same_as decltype(auto) iter2 = --iter1; + assert(&iter1 == &iter2); + + std::same_as decltype(auto) iter3 = iter1--; + assert(iter3 == std::next(iter1)); + } + { + // !ref-is-glvalue + BidiCommonInner inners[2] = {buffer[0], buffer[1]}; + InnerRValue> outer{inners}; + std::ranges::join_view jv(outer); + auto iter = jv.begin(); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + { + // !bidirectional_­range + BidiCommonInner inners[2] = {buffer[0], buffer[1]}; + SimpleForwardCommonOuter outer{inners}; + std::ranges::join_view jv(outer); + auto iter = jv.begin(); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + { + // !bidirectional_­range> + ForwardCommonInner inners[2] = {buffer[0], buffer[1]}; + std::ranges::join_view jv(inners); + auto iter = jv.begin(); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + { + // !common_­range> + BidiNonCommonInner inners[2] = {buffer[0], buffer[1]}; + std::ranges::join_view jv(inners); + auto iter = jv.begin(); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp @@ -10,24 +10,58 @@ // UNSUPPORTED: libcpp-has-no-incomplete-ranges // friend constexpr bool operator==(const iterator& x, const iterator& y); +// requires ref-is-glvalue && equality_comparable> && +// equality_comparable>>; #include #include -#include "test_macros.h" #include "../types.h" constexpr bool test() { int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + { + std::ranges::join_view jv(buffer); + auto iter1 = jv.begin(); + auto iter2 = jv.begin(); + assert(iter1 == iter2); + iter1++; + assert(iter1 != iter2); + iter2++; + assert(iter1 == iter2); - std::ranges::join_view jv(buffer); - auto iter1 = jv.begin(); - auto iter2 = jv.begin(); - assert(iter1 == iter2); - iter1++; - assert(iter1 != iter2); - iter2++; - assert(iter1 == iter2); + assert(jv.begin() == std::as_const(jv).begin()); + } + { + // !ref-is-glvalue + BidiCommonInner inners[2] = {buffer[0], buffer[1]}; + InnerRValue> outer{inners}; + std::ranges::join_view jv(outer); + auto iter = jv.begin(); + static_assert(!std::equality_comparable); + } + { + // !equality_comparable> + using Inner = BufferView; + using Outer = BufferView, sentinel_wrapper>>; + static_assert(!std::equality_comparable>); + Inner inners[2] = {buffer[0], buffer[1]}; + std::ranges::join_view jv(Outer{inners}); + auto iter = jv.begin(); + static_assert(!std::equality_comparable); + auto const_iter = std::as_const(jv).begin(); + static_assert(!std::equality_comparable); + } + { + // !equality_comparable>>; + using Inner = BufferView, sentinel_wrapper>>; + Inner inners[1] = {buffer[0]}; + std::ranges::join_view jv{inners}; + auto iter = jv.begin(); + static_assert(!std::equality_comparable); + auto const_iter = std::as_const(jv).begin(); + static_assert(!std::equality_comparable); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp @@ -11,7 +11,9 @@ // constexpr iterator& operator++(); // constexpr void operator++(int); -// constexpr iterator operator++(int); +// constexpr iterator operator++(int) +// requires ref-is-glvalue && forward_­range && +// forward_range>; #include #include @@ -147,6 +149,41 @@ ASSERT_SAME_TYPE(decltype(++iter), decltype(iter)&); } + { + // check return value + std::ranges::join_view jv(buffer1); + auto iter = jv.begin(); + using iterator = decltype(iter); + + std::same_as decltype(auto) iter2 = ++iter; + assert(&iter2 == &iter); + + std::same_as decltype(auto) iter3 = iter++; + assert(std::next(iter3) == iter); + } + { + // !ref-is-glvalue + BidiCommonInner inners[2] = {buffer1[0], buffer1[1]}; + InnerRValue> outer{inners}; + std::ranges::join_view jv(outer); + auto iter = jv.begin(); + static_assert(std::is_void_v); + } + { + // !forward_­range + BufferView inners[2] = {buffer1[0], buffer1[1]}; + using Outer = SimpleInputCommonOuter>; + std::ranges::join_view jv{Outer(inners)}; + auto iter = jv.begin(); + static_assert(std::is_void_v); + } + { + // !forward_range> + InputCommonInner inners[1] = {buffer1[0]}; + std::ranges::join_view jv{inners}; + auto iter = jv.begin(); + static_assert(std::is_void_v); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp @@ -14,18 +14,41 @@ #include #include -#include "test_macros.h" #include "../types.h" constexpr bool test() { int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; - std::ranges::join_view jv(buffer); - assert(std::ranges::iter_move(jv.begin()) == 1); - ASSERT_SAME_TYPE(decltype(std::ranges::iter_move(jv.begin())), int&&); - - static_assert(noexcept(std::ranges::iter_move(std::declval()))); - + { + std::ranges::join_view jv(buffer); + assert(std::ranges::iter_move(jv.begin()) == 1); + static_assert(std::is_same_v); + + static_assert(noexcept(std::ranges::iter_move(std::declval()))); + } + { + // iter_move calls inner's iter_move + IterMoveSwapAwareView inners[2] = {buffer[0], buffer[1]}; + std::ranges::join_view jv(inners); + auto it = jv.begin(); + + const auto& iter_move_called_times1 = jv.base().begin()->iter_move_called; + const auto& iter_move_called_times2 = std::next(jv.base().begin())->iter_move_called; + assert(iter_move_called_times1 == 0); + assert(iter_move_called_times2 == 0); + + std::same_as> decltype(auto) x = std::ranges::iter_move(it); + assert(std::get<0>(x) == 1); + assert(iter_move_called_times1 == 1); + assert(iter_move_called_times2 == 0); + + auto it2 = std::ranges::next(it, 4); + + std::same_as> decltype(auto) y = std::ranges::iter_move(it2); + assert(std::get<0>(y) == 5); + assert(iter_move_called_times1 == 1); + assert(iter_move_called_times2 == 1); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp @@ -14,23 +14,42 @@ #include #include -#include "test_macros.h" #include "../types.h" constexpr bool test() { int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; - std::ranges::join_view jv(buffer); - auto iter1 = jv.begin(); - auto iter2 = std::next(jv.begin()); - assert(*iter1 == 1); - assert(*iter2 == 2); - std::ranges::swap(iter1, iter2); - assert(*iter1 == 2); - assert(*iter2 == 1); - - static_assert(noexcept(std::ranges::iter_swap(iter1, iter2))); - + { + std::ranges::join_view jv(buffer); + auto iter1 = jv.begin(); + auto iter2 = std::next(jv.begin()); + assert(buffer[0][0] == 1); + assert(buffer[0][1] == 2); + std::ranges::iter_swap(iter1, iter2); + assert(buffer[0][0] == 2); + assert(buffer[0][1] == 1); + + static_assert(noexcept(std::ranges::iter_swap(iter1, iter2))); + } + { + // iter_move calls inner's iter_swap + IterMoveSwapAwareView inners[1] = {buffer[0]}; + std::ranges::join_view jv(inners); + auto it1 = jv.begin(); + auto it2 = std::ranges::next(it1); + + const auto& iter_swap_called_times = jv.base().begin()->iter_swap_called; + + assert(iter_swap_called_times == 0); + assert(buffer[0][0] == 2); + assert(buffer[0][1] == 1); + + std::ranges::iter_swap(it1, it2); + + assert(buffer[0][0] == 1); + assert(buffer[0][1] == 2); + assert(iter_swap_called_times == 1); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp @@ -29,6 +29,24 @@ sentinel_wrapper> end() const; }; +template +struct diff_type_iter { + using iterator_category = std::input_iterator_tag; + using value_type = V; + using difference_type = T; + + V& operator*() const; + diff_type_iter& operator++(); + void operator++(int); + friend constexpr bool operator==(diff_type_iter, diff_type_iter) = default; +}; + +template +struct DiffTypeRange : std::ranges::view_base { + diff_type_iter begin() const; + diff_type_iter end() const; +}; + template concept HasIterCategory = requires { typename T::iterator_category; }; @@ -38,25 +56,49 @@ std::ranges::join_view jv(buffer); using Iter = std::ranges::iterator_t; - ASSERT_SAME_TYPE(Iter::iterator_concept, std::bidirectional_iterator_tag); - ASSERT_SAME_TYPE(Iter::iterator_category, std::bidirectional_iterator_tag); - ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); - ASSERT_SAME_TYPE(Iter::value_type, int); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); } { using Iter = std::ranges::iterator_t>>>; - ASSERT_SAME_TYPE(Iter::iterator_concept, std::forward_iterator_tag); - ASSERT_SAME_TYPE(Iter::iterator_category, std::forward_iterator_tag); - ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); - ASSERT_SAME_TYPE(Iter::value_type, int); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); } { using Iter = std::ranges::iterator_t>>>; - ASSERT_SAME_TYPE(Iter::iterator_concept, std::input_iterator_tag); + static_assert(std::is_same_v); + static_assert(!HasIterCategory); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + { + // !ref-is-glvalue + using Outer = InnerRValue>; + using Iter = std::ranges::iterator_t>; static_assert(!HasIterCategory); - ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); - ASSERT_SAME_TYPE(Iter::value_type, int); + static_assert(std::is_same_v); + } + { + // value_type == inner's value_type + using Inner = IterMoveSwapAwareView; + using Outer = BidiCommonOuter; + using Iter = std::ranges::iterator_t>; + static_assert(std::is_same_v, std::tuple>); + static_assert(std::is_same_v>); + } + { + // difference_type + using Inner = DiffTypeRange; + using Outer = DiffTypeRange; + using Iter = std::ranges::iterator_t>; + static_assert(std::is_same_v>); } } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp @@ -10,25 +10,38 @@ // UNSUPPORTED: libcpp-has-no-incomplete-ranges // constexpr sentinel(sentinel s); +// requires Const && convertible_­to, sentinel_t>; #include #include -#include "test_macros.h" #include "../types.h" constexpr bool test() { int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; - - CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; - std::ranges::join_view jv(CopyableParent{children}); - auto sent1 = jv.end(); - std::ranges::sentinel_t sent2 = sent1; - (void) sent2; // We can't really do anything with these sentinels now :/ - - // We cannot create a non-const iterator from a const iterator. - static_assert(!std::constructible_from); - + { + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), + CopyableChild(buffer[3])}; + std::ranges::join_view jv(CopyableParent{children}); + auto sent1 = jv.end(); + std::ranges::sentinel_t sent2 = sent1; + assert(std::as_const(jv).begin() != sent2); + assert(std::ranges::next(std::as_const(jv).begin(), 16) == sent2); + + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + } + { + using Inner = BufferView; + using ConstIncompatibleOuter = + BufferView, sentinel_wrapper>, + bidirectional_iterator, sentinel_wrapper>>; + using JoinView = std::ranges::join_view; + using sentinel = std::ranges::sentinel_t; + using const_sentinel = std::ranges::sentinel_t; + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h --- a/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h @@ -10,6 +10,7 @@ #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H #include +#include #include "test_macros.h" #include "test_iterators.h" @@ -138,4 +139,304 @@ constexpr const T *end() const { return ptr_.ptr_ + 4; } }; +template +struct Buffer{ + T* buffer_; + std::size_t size_; + + template + constexpr Buffer(T (&b)[N]) :buffer_(b), size_(N){} + constexpr Buffer(T* p, std::size_t s) : buffer_(p), size_(s){} +}; + +template +struct BufferView : std::ranges::view_base, Buffer> { + using Buffer>::Buffer; + + constexpr NonConstIter begin() + requires(!std::is_same_v) + { + return NonConstIter(this->buffer_); + } + constexpr Iter begin() const { return Iter(this->buffer_); } + + constexpr NonConstSent end() + requires(!std::is_same_v) + { + if constexpr (std::is_same_v) { + return NonConstIter(this->buffer_ + this->size_); + } else { + return NonConstSent(NonConstIter(this->buffer_ + this->size_)); + } + } + + constexpr Sent end() const { + if constexpr (std::is_same_v) { + return Iter(this->buffer_ + this->size_); + } else { + return Sent(Iter(this->buffer_ + this->size_)); + } + } +}; + +template +struct common_input_iterator { + Base it_; + + using value_type = std::iter_value_t; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr common_input_iterator() = default; + constexpr explicit common_input_iterator(Base it) : it_(it) {} + + constexpr common_input_iterator& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } + + constexpr std::iter_reference_t operator*() const { return *it_; } + + friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default; +}; + +using InputCommonInner = BufferView>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(std::ranges::common_range); + +using InputNonCommonInner = BufferView, sentinel_wrapper>>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(!std::ranges::common_range); + +using ForwardCommonInner = BufferView>; +static_assert(std::ranges::forward_range); +static_assert(!std::ranges::bidirectional_range); +static_assert(std::ranges::common_range); + +using ForwardNonCommonInner = BufferView, sentinel_wrapper>>; +static_assert(std::ranges::forward_range); +static_assert(!std::ranges::bidirectional_range); +static_assert(!std::ranges::common_range); + +using BidiCommonInner = BufferView>; +static_assert(std::ranges::bidirectional_range); +static_assert(std::ranges::common_range); + +using BidiNonCommonInner = BufferView, sentinel_wrapper>>; +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::common_range); + + +template > +using SimpleInputCommonOuter = BufferView>; +static_assert(!std::ranges::forward_range>); +static_assert(!std::ranges::bidirectional_range>); +static_assert(std::ranges::common_range>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view>); + +template > +using NonSimpleInputCommonOuter = BufferView, common_input_iterator, + common_input_iterator< Inner*>, common_input_iterator< Inner*>>; +static_assert(!std::ranges::forward_range>); +static_assert(!std::ranges::bidirectional_range>); +static_assert(std::ranges::common_range>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view>); + +template > +using SimpleForwardCommonOuter = BufferView>; +static_assert(std::ranges::forward_range>); +static_assert(!std::ranges::bidirectional_range>); +static_assert(std::ranges::common_range>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view>); + +template > +using NonSimpleForwardCommonOuter = BufferView, forward_iterator, + forward_iterator, forward_iterator>; +static_assert(std::ranges::forward_range>); +static_assert(!std::ranges::bidirectional_range>); +static_assert(std::ranges::common_range>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view>); + +template > +using SimpleForwardNonCommonOuter = BufferView, sentinel_wrapper>>; +static_assert(std::ranges::forward_range>); +static_assert(!std::ranges::bidirectional_range>); +static_assert(!std::ranges::common_range>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view>); + +template > +using NonSimpleForwardNonCommonOuter = + BufferView, sentinel_wrapper>, + forward_iterator, sentinel_wrapper>>; +static_assert(std::ranges::forward_range>); +static_assert(!std::ranges::bidirectional_range>); +static_assert(!std::ranges::common_range>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view>); + +template > +using BidiCommonOuter = BufferView>; +static_assert(std::ranges::bidirectional_range>); +static_assert(std::ranges::common_range>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view>); + +template +struct copy_iterator +{ + It it_ = It(); + + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + using pointer = typename std::iterator_traits::pointer; + using referece = value_type; + + copy_iterator() + requires std::default_initializable = default; + constexpr copy_iterator(It it) : it_(std::move(it)) {} + + constexpr referece operator*() const {return *it_;} + + constexpr copy_iterator& operator++() {++it_; return *this;} + constexpr copy_iterator& operator--() requires std::bidirectional_iterator {--it_; return *this;} + constexpr copy_iterator operator++(int) requires std::forward_iterator {return copy_iterator(it_++);} + constexpr void operator++(int) {return it_++;} + constexpr copy_iterator operator--(int) requires std::bidirectional_iterator {return copy_iterator(it_--);} + + friend constexpr bool operator==(const copy_iterator& x, const copy_iterator& y) { + return x.it_ == y.it_; + } + +}; + +template +struct InnerRValue : Outer { + + using iterator = copy_iterator>; + using const_iterator = copy_iterator>; + using sentinel = copy_iterator>; + using const_sentinel = copy_iterator>; + + using Outer::Outer; + static_assert(std::ranges::common_range,"non-common range is not supported yet"); + + constexpr iterator begin() { return Outer::begin(); } + constexpr const_iterator begin() const + requires std::ranges::range + { + return Outer::begin(); + } + + constexpr auto end() { + return iterator{Outer::end()}; + } + constexpr auto end() const + requires std::ranges::range + { + return const_iterator{Outer::end()}; + } +}; +static_assert(std::ranges::forward_range>>); +static_assert(!std::ranges::bidirectional_range>>); +static_assert(std::ranges::common_range>>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view>>); +static_assert(!std::is_lvalue_reference_v>>>); + +template +concept HasArrow = std::input_iterator && (std::is_pointer_v || requires(T i) { i.operator->(); }); + +template +struct move_only_input_iter { + Base it_; + + using value_type = std::iter_value_t; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr move_only_input_iter(Base it) : it_(std::move(it)) {} + constexpr move_only_input_iter(move_only_input_iter&&) = default; + constexpr move_only_input_iter(const move_only_input_iter&) = delete; + constexpr move_only_input_iter& operator=(move_only_input_iter&&) = default; + constexpr move_only_input_iter& operator=(const move_only_input_iter&) = delete; + + constexpr move_only_input_iter& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } + + constexpr std::iter_reference_t operator*() const { return *it_; } + constexpr auto operator->() const requires (HasArrow && std::copyable) { return it_; } +}; +static_assert(!std::copyable>); + +template +struct move_iter_sentinel { + Base it_; + explicit move_iter_sentinel() = default; + constexpr move_iter_sentinel( Base it) : it_(std::move(it)) {} + constexpr bool operator==(const move_only_input_iter& other) const { return it_ == other.it_; } +}; +static_assert(std::sentinel_for,move_only_input_iter>); + +struct MoveOnlyIterInner : BufferView, move_iter_sentinel>{ + using BufferView::BufferView; + + using iterator = move_only_input_iter; + using sentinel = move_iter_sentinel; + + iterator begin() const {return buffer_;} + sentinel end() const {return sentinel{buffer_ + size_};} +}; +static_assert(std::ranges::input_range); + +struct move_swap_aware_iter{ + + using value_type = std::tuple; + using reference = std::tuple; + using rvalue_reference = std::tuple; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + int* iter_move_called; + int* iter_swap_called; + int* i_; + + constexpr move_swap_aware_iter& operator++() { + ++i_; + return *this; + } + constexpr void operator++(int) { ++i_; } + + constexpr reference operator*() const { return reference(*i_); } + constexpr friend bool operator==(const move_swap_aware_iter& x, const move_swap_aware_iter& y){ + return x.i_ == y.i_; + } + + constexpr friend rvalue_reference iter_move(const move_swap_aware_iter& x) noexcept { + ++(*x.iter_move_called); + return rvalue_reference{std::move(*x.i_)}; + } + + constexpr friend void iter_swap(const move_swap_aware_iter& x, const move_swap_aware_iter& y) noexcept { + ++(*x.iter_swap_called); + std::swap(*x.i_, *y.i_); + } +}; + +struct IterMoveSwapAwareView : Buffer, std::ranges::view_base { + int iter_move_called = 0; + int iter_swap_called = 0; + using Buffer::Buffer; + + constexpr auto begin() { return move_swap_aware_iter{&iter_move_called, &iter_swap_called, buffer_}; } + + constexpr auto end() { + return + move_swap_aware_iter{&iter_move_called, &iter_swap_called, buffer_ + size_}; + } +}; +static_assert(std::ranges::input_range); + #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H